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