Line data Source code
1 : /******************************************************************************
2 : *
3 : * Project: GDAL
4 : * Purpose: "gdal vector geom 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_geom_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 : /* GDALVectorGeomBufferAlgorithm() */
26 : /************************************************************************/
27 :
28 12 : GDALVectorGeomBufferAlgorithm::GDALVectorGeomBufferAlgorithm(
29 12 : bool standaloneStep)
30 : : GDALVectorGeomAbstractAlgorithm(NAME, DESCRIPTION, HELP_URL,
31 12 : standaloneStep, m_opts)
32 : {
33 : AddArg("distance", 0, _("Distance to which to extend the geometry."),
34 24 : &m_opts.m_distance)
35 12 : .SetPositional()
36 12 : .SetRequired();
37 24 : AddArg("endcap-style", 0, _("Endcap style."), &m_opts.m_endCapStyle)
38 12 : .SetChoices("round", "flat", "square")
39 12 : .SetDefault(m_opts.m_endCapStyle);
40 24 : AddArg("join-style", 0, _("Join style."), &m_opts.m_joinStyle)
41 12 : .SetChoices("round", "mitre", "bevel")
42 12 : .SetDefault(m_opts.m_joinStyle);
43 : AddArg("mitre-limit", 0,
44 : _("Mitre ratio limit (only affects mitered join style)."),
45 24 : &m_opts.m_mitreLimit)
46 12 : .SetDefault(m_opts.m_mitreLimit)
47 12 : .SetMinValueIncluded(0);
48 : AddArg("quadrant-segments", 0,
49 : _("Number of line segments used to approximate a quarter circle."),
50 24 : &m_opts.m_quadrantSegments)
51 12 : .SetDefault(m_opts.m_quadrantSegments)
52 12 : .SetMinValueIncluded(1);
53 : AddArg("side", 0,
54 : _("Sets whether the computed buffer should be single-sided or not."),
55 24 : &m_opts.m_side)
56 12 : .SetChoices("both", "left", "right")
57 12 : .SetDefault(m_opts.m_side);
58 12 : }
59 :
60 : #ifdef HAVE_GEOS
61 :
62 : namespace
63 : {
64 :
65 : /************************************************************************/
66 : /* GDALVectorGeomBufferAlgorithmLayer */
67 : /************************************************************************/
68 :
69 : class GDALVectorGeomBufferAlgorithmLayer final
70 : : public GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorGeomBufferAlgorithm>
71 : {
72 : public:
73 7 : GDALVectorGeomBufferAlgorithmLayer(
74 : OGRLayer &oSrcLayer, const GDALVectorGeomBufferAlgorithm::Options &opts)
75 7 : : GDALVectorGeomOneToOneAlgorithmLayer<GDALVectorGeomBufferAlgorithm>(
76 7 : oSrcLayer, opts)
77 : {
78 : m_aosBufferOptions.SetNameValue("ENDCAP_STYLE",
79 7 : opts.m_endCapStyle.c_str());
80 7 : m_aosBufferOptions.SetNameValue("JOIN_STYLE", opts.m_joinStyle.c_str());
81 : m_aosBufferOptions.SetNameValue("MITRE_LIMIT",
82 7 : CPLSPrintf("%.17g", opts.m_mitreLimit));
83 : m_aosBufferOptions.SetNameValue(
84 7 : "QUADRANT_SEGMENTS", CPLSPrintf("%d", opts.m_quadrantSegments));
85 : m_aosBufferOptions.SetNameValue("SINGLE_SIDED",
86 7 : m_opts.m_side != "both" ? "YES" : "NO");
87 7 : }
88 :
89 : protected:
90 : using GDALVectorGeomOneToOneAlgorithmLayer::TranslateFeature;
91 :
92 : std::unique_ptr<OGRFeature>
93 : TranslateFeature(std::unique_ptr<OGRFeature> poSrcFeature) const override;
94 :
95 : private:
96 : CPLStringList m_aosBufferOptions{};
97 : };
98 :
99 : /************************************************************************/
100 : /* TranslateFeature() */
101 : /************************************************************************/
102 :
103 : std::unique_ptr<OGRFeature>
104 14 : GDALVectorGeomBufferAlgorithmLayer::TranslateFeature(
105 : std::unique_ptr<OGRFeature> poSrcFeature) const
106 : {
107 14 : const int nGeomFieldCount = poSrcFeature->GetGeomFieldCount();
108 28 : for (int i = 0; i < nGeomFieldCount; ++i)
109 : {
110 14 : if (IsSelectedGeomField(i))
111 : {
112 14 : if (auto poGeom = std::unique_ptr<OGRGeometry>(
113 28 : poSrcFeature->StealGeometry(i)))
114 : {
115 14 : poGeom.reset(poGeom->BufferEx(m_opts.m_distance,
116 7 : m_aosBufferOptions.List()));
117 7 : if (poGeom)
118 : {
119 14 : poGeom->assignSpatialReference(m_srcLayer.GetLayerDefn()
120 7 : ->GetGeomFieldDefn(i)
121 14 : ->GetSpatialRef());
122 7 : poSrcFeature->SetGeomField(i, std::move(poGeom));
123 : }
124 : }
125 : }
126 : }
127 :
128 14 : return poSrcFeature;
129 : }
130 :
131 : } // namespace
132 :
133 : #endif // HAVE_GEOS
134 :
135 : /************************************************************************/
136 : /* GDALVectorGeomBufferAlgorithm::CreateAlgLayer() */
137 : /************************************************************************/
138 :
139 : std::unique_ptr<OGRLayerWithTranslateFeature>
140 7 : GDALVectorGeomBufferAlgorithm::CreateAlgLayer(
141 : [[maybe_unused]] OGRLayer &srcLayer)
142 : {
143 : #ifdef HAVE_GEOS
144 7 : return std::make_unique<GDALVectorGeomBufferAlgorithmLayer>(srcLayer,
145 7 : m_opts);
146 : #else
147 : CPLAssert(false);
148 : return nullptr;
149 : #endif
150 : }
151 :
152 : /************************************************************************/
153 : /* GDALVectorGeomBufferAlgorithm::RunStep() */
154 : /************************************************************************/
155 :
156 7 : bool GDALVectorGeomBufferAlgorithm::RunStep(GDALProgressFunc, void *)
157 : {
158 : #ifdef HAVE_GEOS
159 7 : if (m_opts.m_side == "right")
160 1 : m_opts.m_distance = -m_opts.m_distance;
161 :
162 7 : return GDALVectorGeomAbstractAlgorithm::RunStep(nullptr, nullptr);
163 : #else
164 : ReportError(CE_Failure, CPLE_NotSupported,
165 : "This algorithm is only supported for builds against GEOS");
166 : return false;
167 : #endif
168 : }
169 :
170 : //! @endcond
|