Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "gdal vector buffer"
5 : * Author: Even Rouault <even dot rouault at spatialys.com>
6 : *
7 : ******************************************************************************
8 : * Copyright (c) 2025, Even Rouault <even dot rouault at spatialys.com>
9 : *
10 : * SPDX-License-Identifier: MIT
11 : ****************************************************************************/
12 :
13 : #include "gdalalg_vector_buffer.h"
14 :
15 : #include "gdal_priv.h"
16 : #include "ogrsf_frmts.h"
17 :
18 : //! @cond Doxygen_Suppress
19 :
20 : #ifndef _
21 : #define _(x) (x)
22 : #endif
23 :
24 : /************************************************************************/
25 : /* GDALVectorBufferAlgorithm() */
26 : /************************************************************************/
27 :
28 100 : GDALVectorBufferAlgorithm::GDALVectorBufferAlgorithm(bool standaloneStep)
29 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 100 : standaloneStep, m_opts)
31 : {
32 : AddArg("distance", 0, _("Distance to which to extend the geometry."),
33 200 : &m_opts.m_distance)
34 100 : .SetPositional()
35 100 : .SetRequired();
36 200 : AddArg("endcap-style", 0, _("Endcap style."), &m_opts.m_endCapStyle)
37 100 : .SetChoices("round", "flat", "square")
38 100 : .SetDefault(m_opts.m_endCapStyle);
39 200 : AddArg("join-style", 0, _("Join style."), &m_opts.m_joinStyle)
40 100 : .SetChoices("round", "mitre", "bevel")
41 100 : .SetDefault(m_opts.m_joinStyle);
42 : AddArg("mitre-limit", 0,
43 : _("Mitre ratio limit (only affects mitered join style)."),
44 200 : &m_opts.m_mitreLimit)
45 100 : .SetDefault(m_opts.m_mitreLimit)
46 100 : .SetMinValueIncluded(0);
47 : AddArg("quadrant-segments", 0,
48 : _("Number of line segments used to approximate a quarter circle."),
49 200 : &m_opts.m_quadrantSegments)
50 100 : .SetDefault(m_opts.m_quadrantSegments)
51 100 : .SetMinValueIncluded(1);
52 : AddArg("side", 0,
53 : _("Sets whether the computed buffer should be single-sided or not."),
54 200 : &m_opts.m_side)
55 100 : .SetChoices("both", "left", "right")
56 100 : .SetDefault(m_opts.m_side);
57 100 : }
58 :
59 : #ifdef HAVE_GEOS
60 :
61 : namespace
62 : {
63 :
64 : /************************************************************************/
65 : /* GDALVectorBufferAlgorithmLayer */
66 : /************************************************************************/
67 :
68 : class GDALVectorBufferAlgorithmLayer final
69 : : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorBufferAlgorithm>
70 : {
71 : public:
72 9 : GDALVectorBufferAlgorithmLayer(
73 : OGRLayer &oSrcLayer, const GDALVectorBufferAlgorithm::Options &opts)
74 9 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorBufferAlgorithm>(
75 : oSrcLayer, opts),
76 9 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
77 : {
78 18 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
79 : {
80 9 : if (IsSelectedGeomField(i))
81 : {
82 9 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbMultiPolygon);
83 : }
84 : }
85 :
86 : m_aosBufferOptions.SetNameValue("ENDCAP_STYLE",
87 9 : opts.m_endCapStyle.c_str());
88 9 : m_aosBufferOptions.SetNameValue("JOIN_STYLE", opts.m_joinStyle.c_str());
89 : m_aosBufferOptions.SetNameValue("MITRE_LIMIT",
90 9 : CPLSPrintf("%.17g", opts.m_mitreLimit));
91 : m_aosBufferOptions.SetNameValue(
92 9 : "QUADRANT_SEGMENTS", CPLSPrintf("%d", opts.m_quadrantSegments));
93 : m_aosBufferOptions.SetNameValue("SINGLE_SIDED",
94 9 : m_opts.m_side != "both" ? "YES" : "NO");
95 9 : }
96 :
97 23 : const OGRFeatureDefn *GetLayerDefn() const override
98 : {
99 23 : return m_poFeatureDefn.get();
100 : }
101 :
102 1 : OGRErr IGetExtent(int iGeomField, OGREnvelope *psExtent,
103 : bool bForce) override
104 : {
105 1 : if (IsSelectedGeomField(iGeomField))
106 1 : return OGRLayer::IGetExtent(iGeomField, psExtent, bForce);
107 0 : return m_srcLayer.GetExtent(iGeomField, psExtent, bForce);
108 : }
109 :
110 2 : bool TestCapability(const char *pszCap) const override
111 : {
112 2 : if (EQUAL(pszCap, OLCFastGetExtent))
113 1 : return false;
114 1 : return GDALVectorGeomOneToOneAlgorithmLayer::TestCapability(pszCap);
115 : }
116 :
117 : protected:
118 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
119 :
120 : std::unique_ptr<OGRFeature>
121 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
122 :
123 : private:
124 : CPLStringList m_aosBufferOptions{};
125 : const OGRFeatureDefnRefCountedPtr m_poFeatureDefn;
126 :
127 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorBufferAlgorithmLayer)
128 : };
129 :
130 : /************************************************************************/
131 : /* TranslateFeature() */
132 : /************************************************************************/
133 :
134 32 : std::unique_ptr<OGRFeature> GDALVectorBufferAlgorithmLayer::TranslateFeature(
135 : std::unique_ptr<OGRFeature> poSrcFeature) const
136 : {
137 32 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
138 64 : for (int i = 0; i < nGeomFieldCount; ++i)
139 : {
140 32 : if (IsSelectedGeomField(i))
141 : {
142 32 : if (auto poGeom = std::unique_ptr<OGRGeometry>(
143 64 : poSrcFeature->StealGeometry(i)))
144 : {
145 25 : poGeom.reset(poGeom->BufferEx(m_opts.m_distance,
146 : m_aosBufferOptions.List()));
147 25 : if (poGeom)
148 : {
149 : const auto poGeomFieldDefn =
150 25 : m_poFeatureDefn->GetGeomFieldDefn(i);
151 75 : poGeom = OGRGeometryFactory::forceTo(
152 50 : std::move(poGeom), poGeomFieldDefn->GetType());
153 25 : if (poGeom)
154 : {
155 50 : poGeom->assignSpatialReference(
156 25 : poGeomFieldDefn->GetSpatialRef());
157 25 : poSrcFeature->SetGeomField(i, std::move(poGeom));
158 : }
159 : }
160 : }
161 : }
162 : }
163 :
164 32 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn.get());
165 32 : return poSrcFeature;
166 : }
167 :
168 : } // namespace
169 :
170 : #endif // HAVE_GEOS
171 :
172 : /************************************************************************/
173 : /* GDALVectorBufferAlgorithm::CreateAlgLayer() */
174 : /************************************************************************/
175 :
176 : std::unique_ptr<OGRLayerWithTranslateFeature>
177 9 : GDALVectorBufferAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer)
178 : {
179 : #ifdef HAVE_GEOS
180 9 : return std::make_unique<GDALVectorBufferAlgorithmLayer>(srcLayer, m_opts);
181 : #else
182 : CPLAssert(false);
183 : return nullptr;
184 : #endif
185 : }
186 :
187 : /************************************************************************/
188 : /* GDALVectorBufferAlgorithm::RunStep() */
189 : /************************************************************************/
190 :
191 9 : bool GDALVectorBufferAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
192 : {
193 : #ifdef HAVE_GEOS
194 9 : if (m_opts.m_side == "right")
195 1 : m_opts.m_distance = -m_opts.m_distance;
196 :
197 9 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
198 : #else
199 : (void)ctxt;
200 : ReportError(CE_Failure, CPLE_NotSupported,
201 : "This algorithm is only supported for builds against GEOS");
202 : return false;
203 : #endif
204 : }
205 :
206 : GDALVectorBufferAlgorithmStandalone::~GDALVectorBufferAlgorithmStandalone() =
207 : default;
208 :
209 : //! @endcond
|