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 50 : GDALVectorBufferAlgorithm::GDALVectorBufferAlgorithm(bool standaloneStep)
29 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
30 50 : standaloneStep, m_opts)
31 : {
32 : AddArg("distance", 0, _("Distance to which to extend the geometry."),
33 100 : &m_opts.m_distance)
34 50 : .SetPositional()
35 50 : .SetRequired();
36 100 : AddArg("endcap-style", 0, _("Endcap style."), &m_opts.m_endCapStyle)
37 50 : .SetChoices("round", "flat", "square")
38 50 : .SetDefault(m_opts.m_endCapStyle);
39 100 : AddArg("join-style", 0, _("Join style."), &m_opts.m_joinStyle)
40 50 : .SetChoices("round", "mitre", "bevel")
41 50 : .SetDefault(m_opts.m_joinStyle);
42 : AddArg("mitre-limit", 0,
43 : _("Mitre ratio limit (only affects mitered join style)."),
44 100 : &m_opts.m_mitreLimit)
45 50 : .SetDefault(m_opts.m_mitreLimit)
46 50 : .SetMinValueIncluded(0);
47 : AddArg("quadrant-segments", 0,
48 : _("Number of line segments used to approximate a quarter circle."),
49 100 : &m_opts.m_quadrantSegments)
50 50 : .SetDefault(m_opts.m_quadrantSegments)
51 50 : .SetMinValueIncluded(1);
52 : AddArg("side", 0,
53 : _("Sets whether the computed buffer should be single-sided or not."),
54 100 : &m_opts.m_side)
55 50 : .SetChoices("both", "left", "right")
56 50 : .SetDefault(m_opts.m_side);
57 50 : }
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 7 : GDALVectorBufferAlgorithmLayer(
73 : OGRLayer &oSrcLayer, const GDALVectorBufferAlgorithm::Options &opts)
74 7 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorBufferAlgorithm>(
75 : oSrcLayer, opts),
76 7 : m_poFeatureDefn(oSrcLayer.GetLayerDefn()->Clone())
77 : {
78 7 : m_poFeatureDefn->Reference();
79 14 : for (int i = 0; i < m_poFeatureDefn->GetGeomFieldCount(); ++i)
80 : {
81 7 : if (IsSelectedGeomField(i))
82 : {
83 7 : m_poFeatureDefn->GetGeomFieldDefn(i)->SetType(wkbMultiPolygon);
84 : }
85 : }
86 :
87 : m_aosBufferOptions.SetNameValue("ENDCAP_STYLE",
88 7 : opts.m_endCapStyle.c_str());
89 7 : m_aosBufferOptions.SetNameValue("JOIN_STYLE", opts.m_joinStyle.c_str());
90 : m_aosBufferOptions.SetNameValue("MITRE_LIMIT",
91 7 : CPLSPrintf("%.17g", opts.m_mitreLimit));
92 : m_aosBufferOptions.SetNameValue(
93 7 : "QUADRANT_SEGMENTS", CPLSPrintf("%d", opts.m_quadrantSegments));
94 : m_aosBufferOptions.SetNameValue("SINGLE_SIDED",
95 7 : m_opts.m_side != "both" ? "YES" : "NO");
96 7 : }
97 :
98 14 : ~GDALVectorBufferAlgorithmLayer() override
99 7 : {
100 7 : m_poFeatureDefn->Release();
101 14 : }
102 :
103 14 : const OGRFeatureDefn *GetLayerDefn() const override
104 : {
105 14 : return m_poFeatureDefn;
106 : }
107 :
108 : protected:
109 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
110 :
111 : std::unique_ptr<OGRFeature>
112 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
113 :
114 : private:
115 : CPLStringList m_aosBufferOptions{};
116 : OGRFeatureDefn *const m_poFeatureDefn;
117 :
118 : CPL_DISALLOW_COPY_ASSIGN(GDALVectorBufferAlgorithmLayer)
119 : };
120 :
121 : /************************************************************************/
122 : /* TranslateFeature() */
123 : /************************************************************************/
124 :
125 14 : std::unique_ptr<OGRFeature> GDALVectorBufferAlgorithmLayer::TranslateFeature(
126 : std::unique_ptr<OGRFeature> poSrcFeature) const
127 : {
128 14 : const int nGeomFieldCount = m_poFeatureDefn->GetGeomFieldCount();
129 28 : for (int i = 0; i < nGeomFieldCount; ++i)
130 : {
131 14 : if (IsSelectedGeomField(i))
132 : {
133 14 : if (auto poGeom = std::unique_ptr<OGRGeometry>(
134 28 : poSrcFeature->StealGeometry(i)))
135 : {
136 14 : poGeom.reset(poGeom->BufferEx(m_opts.m_distance,
137 7 : m_aosBufferOptions.List()));
138 7 : if (poGeom)
139 : {
140 : const auto poGeomFieldDefn =
141 7 : m_poFeatureDefn->GetGeomFieldDefn(i);
142 7 : poGeom.reset(OGRGeometryFactory::forceTo(
143 : poGeom.release(), poGeomFieldDefn->GetType()));
144 7 : if (poGeom)
145 : {
146 14 : poGeom->assignSpatialReference(
147 7 : poGeomFieldDefn->GetSpatialRef());
148 7 : poSrcFeature->SetGeomField(i, std::move(poGeom));
149 : }
150 : }
151 : }
152 : }
153 : }
154 :
155 14 : poSrcFeature->SetFDefnUnsafe(m_poFeatureDefn);
156 14 : return poSrcFeature;
157 : }
158 :
159 : } // namespace
160 :
161 : #endif // HAVE_GEOS
162 :
163 : /************************************************************************/
164 : /* GDALVectorBufferAlgorithm::CreateAlgLayer() */
165 : /************************************************************************/
166 :
167 : std::unique_ptr<OGRLayerWithTranslateFeature>
168 7 : GDALVectorBufferAlgorithm::CreateAlgLayer([[maybe_unused]] OGRLayer &srcLayer)
169 : {
170 : #ifdef HAVE_GEOS
171 7 : return std::make_unique<GDALVectorBufferAlgorithmLayer>(srcLayer, m_opts);
172 : #else
173 : CPLAssert(false);
174 : return nullptr;
175 : #endif
176 : }
177 :
178 : /************************************************************************/
179 : /* GDALVectorBufferAlgorithm::RunStep() */
180 : /************************************************************************/
181 :
182 7 : bool GDALVectorBufferAlgorithm::RunStep(GDALPipelineStepRunContext &ctxt)
183 : {
184 : #ifdef HAVE_GEOS
185 7 : if (m_opts.m_side == "right")
186 1 : m_opts.m_distance = -m_opts.m_distance;
187 :
188 7 : return GDALVectorGeomAbstractAlgorithm::RunStep(ctxt);
189 : #else
190 : (void)ctxt;
191 : ReportError(CE_Failure, CPLE_NotSupported,
192 : "This algorithm is only supported for builds against GEOS");
193 : return false;
194 : #endif
195 : }
196 :
197 : GDALVectorBufferAlgorithmStandalone::~GDALVectorBufferAlgorithmStandalone() =
198 : default;
199 :
200 : //! @endcond
|