/src/geos/include/geos/operation/buffer/OffsetCurve.h
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2021 Paul Ramsey <pramsey@cleverelephant.ca> |
7 | | * |
8 | | * This is free software; you can redistribute and/or modify it under |
9 | | * the terms of the GNU Lesser General Public Licence as published |
10 | | * by the Free Software Foundation. |
11 | | * See the COPYING file for more information. |
12 | | * |
13 | | **********************************************************************/ |
14 | | |
15 | | #pragma once |
16 | | |
17 | | #include <geos/export.h> |
18 | | |
19 | | #include <geos/operation/buffer/BufferParameters.h> |
20 | | #include <geos/geom/GeometryFactory.h> |
21 | | #include <geos/constants.h> |
22 | | |
23 | | // Forward declarations |
24 | | namespace geos { |
25 | | namespace geom { |
26 | | class Coordinate; |
27 | | class CoordinateSequence; |
28 | | class Geometry; |
29 | | class LineString; |
30 | | class Polygon; |
31 | | } |
32 | | namespace operation { |
33 | | namespace buffer { |
34 | | class OffsetCurveSection; |
35 | | class SegmentMCIndex; |
36 | | } |
37 | | } |
38 | | } |
39 | | |
40 | | namespace geos { |
41 | | namespace operation { |
42 | | namespace buffer { |
43 | | |
44 | | /** |
45 | | * Computes an offset curve from a geometry. |
46 | | * An offset curve is a linear geometry which is offset a given distance |
47 | | * from the input. |
48 | | * If the offset distance is positive the curve lies on the left side of the input; |
49 | | * if it is negative the curve is on the right side. |
50 | | * The curve(s) have the same direction as the input line(s). |
51 | | * |
52 | | * The offset curve is based on the boundary of the buffer for the geometry |
53 | | * at the offset distance (see BufferOp). |
54 | | * The normal mode of operation is to return the sections of the buffer boundary |
55 | | * which lie on the raw offset curve |
56 | | * (obtained via rawOffset(LineString, double). |
57 | | * The offset curve will contain multiple sections |
58 | | * if the input self-intersects or has close approaches. |
59 | | * The computed sections are ordered along the raw offset curve. |
60 | | * Sections are disjoint. They never self-intersect, but may be rings. |
61 | | * |
62 | | * * For a LineString the offset curve is a linear geometry |
63 | | * (LineString or MultiLineString). |
64 | | * * For a Point or MultiPoint the offset curve is an empty LineString. |
65 | | * * For a Polygon the offset curve is the boundary of the polygon buffer (which |
66 | | * may be a MultiLineString. |
67 | | * * For a collection the output is a MultiLineString containing |
68 | | * the offset curves of the elements. |
69 | | * |
70 | | * In "joined" mode (see setJoined(bool)) |
71 | | * the sections computed for each input line are joined into a single offset curve line. |
72 | | * The joined curve may self-intersect. |
73 | | * At larger offset distances the curve may contain "flat-line" artifacts |
74 | | * in places where the input self-intersects. |
75 | | * |
76 | | * Offset curves support setting the number of quadrant segments, |
77 | | * the join style, and the mitre limit (if applicable) via |
78 | | * the BufferParameters. |
79 | | * |
80 | | * @author Martin Davis |
81 | | * |
82 | | */ |
83 | | class GEOS_DLL OffsetCurve { |
84 | | using Coordinate = geos::geom::Coordinate; |
85 | | using CoordinateSequence = geos::geom::CoordinateSequence; |
86 | | using Geometry = geos::geom::Geometry; |
87 | | using GeometryFactory = geos::geom::GeometryFactory; |
88 | | using LineString = geos::geom::LineString; |
89 | | using Polygon = geos::geom::Polygon; |
90 | | |
91 | | private: |
92 | | |
93 | | // Members |
94 | | const Geometry& inputGeom; |
95 | | double distance; |
96 | | bool isJoined = false; |
97 | | |
98 | | BufferParameters bufferParams; |
99 | | double matchDistance; |
100 | | const GeometryFactory* geomFactory; |
101 | | |
102 | | // Methods |
103 | | |
104 | | std::unique_ptr<Geometry> computePolygonCurve( |
105 | | const Polygon& polyGeom, double distance); |
106 | | |
107 | | std::unique_ptr<Geometry> computeCurve( |
108 | | const LineString& lineGeom, double distance); |
109 | | |
110 | | std::vector<std::unique_ptr<OffsetCurveSection>> computeSections( |
111 | | const LineString& lineGeom, double distance); |
112 | | |
113 | | std::unique_ptr<LineString> offsetSegment( |
114 | | const CoordinateSequence* pts, double distance); |
115 | | |
116 | | static std::unique_ptr<Polygon> getBufferOriented( |
117 | | const LineString& geom, double distance, |
118 | | BufferParameters& bufParams); |
119 | | |
120 | | /** |
121 | | * Extracts the largest polygon by area from a geometry. |
122 | | * Used here to avoid issues with non-robust buffer results |
123 | | * which have spurious extra polygons. |
124 | | * |
125 | | * @param geom a geometry |
126 | | * @return the polygon element of largest area |
127 | | */ |
128 | | static const Polygon* extractMaxAreaPolygon(const Geometry* geom); |
129 | | |
130 | | void computeCurveSections( |
131 | | const CoordinateSequence* bufferRingPts, |
132 | | const CoordinateSequence& rawCurve, |
133 | | std::vector<std::unique_ptr<OffsetCurveSection>>& sections); |
134 | | |
135 | | /** |
136 | | * Matches the segments in a buffer ring to the raw offset curve |
137 | | * to obtain their match positions (if any). |
138 | | * |
139 | | * @param raw0 a raw curve segment start point |
140 | | * @param raw1 a raw curve segment end point |
141 | | * @param rawCurveIndex the index of the raw curve segment |
142 | | * @param bufferSegIndex the spatial index of the buffer ring segments |
143 | | * @param bufferPts the points of the buffer ring |
144 | | * @param rawCurvePos the raw curve positions of the buffer ring segments |
145 | | * @return the index of the minimum matched buffer segment |
146 | | */ |
147 | | std::size_t matchSegments( |
148 | | const Coordinate& raw0, const Coordinate& raw1, |
149 | | std::size_t rawCurveIndex, |
150 | | SegmentMCIndex& bufferSegIndex, |
151 | | const CoordinateSequence* bufferPts, |
152 | | std::vector<double>& rawCurvePos); |
153 | | |
154 | | static double segmentMatchFrac( |
155 | | const Coordinate& p0, const Coordinate& p1, |
156 | | const Coordinate& seg0, const Coordinate& seg1, |
157 | | double matchDistance); |
158 | | |
159 | | /** |
160 | | * This is only called when there is at least one ring segment matched |
161 | | * (so rawCurvePos has at least one entry != NOT_IN_CURVE). |
162 | | * The start index of the first section must be provided. |
163 | | * This is intended to be the section with lowest position |
164 | | * along the raw curve. |
165 | | * @param ringPts the points in a buffer ring |
166 | | * @param rawCurveLoc the position of buffer ring segments along the raw curve |
167 | | * @param startIndex the index of the start of a section |
168 | | * @param sections the list of extracted offset curve sections |
169 | | */ |
170 | | void extractSections( |
171 | | const CoordinateSequence* ringPts, |
172 | | std::vector<double>& rawCurveLoc, |
173 | | std::size_t startIndex, |
174 | | std::vector<std::unique_ptr<OffsetCurveSection>>& sections); |
175 | | |
176 | | std::size_t findSectionStart( |
177 | | const std::vector<double>& loc, |
178 | | std::size_t end); |
179 | | |
180 | | std::size_t findSectionEnd( |
181 | | const std::vector<double>& loc, |
182 | | std::size_t start, |
183 | | std::size_t firstStartIndex); |
184 | | |
185 | | static std::size_t nextIndex(std::size_t i, std::size_t size); |
186 | | static std::size_t prevIndex(std::size_t i, std::size_t size); |
187 | | |
188 | | |
189 | | public: |
190 | | |
191 | | // Constants |
192 | | static constexpr int MATCH_DISTANCE_FACTOR = 10000; |
193 | | |
194 | | /** |
195 | | * A QuadSegs minimum value that will prevent generating |
196 | | * unwanted offset curve artifacts near end caps. |
197 | | */ |
198 | | static constexpr int MIN_QUADRANT_SEGMENTS = 8; |
199 | | |
200 | | /** |
201 | | * Creates a new instance for computing an offset curve for a geometry at a given distance. |
202 | | * with default quadrant segments (BufferParameters::DEFAULT_QUADRANT_SEGMENTS) |
203 | | * and join style (BufferParameters::JOIN_STYLE). |
204 | | * |
205 | | * @param geom the geometry to offset |
206 | | * @param dist the offset distance (positive for left, negative for right) |
207 | | * |
208 | | * @see BufferParameters |
209 | | */ |
210 | | OffsetCurve(const Geometry& geom, double dist) |
211 | 0 | : inputGeom(geom) |
212 | 0 | , distance(dist) |
213 | 0 | , matchDistance(std::abs(dist)/MATCH_DISTANCE_FACTOR) |
214 | 0 | , geomFactory(geom.getFactory()) |
215 | 0 | { |
216 | 0 | if (!std::isfinite(dist)) { |
217 | 0 | throw util::IllegalArgumentException("OffsetCurve distance must be a finite value"); |
218 | 0 | } |
219 | 0 | }; |
220 | | |
221 | | /** |
222 | | * Creates a new instance for computing an offset curve for a geometry at a given distance. |
223 | | * setting the quadrant segments and join style and mitre limit |
224 | | * via {@link BufferParameters}. |
225 | | * |
226 | | * @param geom the geometry to offset |
227 | | * @param dist the offset distance (positive for left, negative for right) |
228 | | * @param bp the buffer parameters to use |
229 | | */ |
230 | | OffsetCurve(const Geometry& geom, double dist, BufferParameters& bp) |
231 | 0 | : inputGeom(geom) |
232 | 0 | , distance(dist) |
233 | 0 | , matchDistance(std::abs(dist)/MATCH_DISTANCE_FACTOR) |
234 | 0 | , geomFactory(geom.getFactory()) |
235 | 0 | { |
236 | 0 | if (!std::isfinite(dist)) { |
237 | 0 | throw util::IllegalArgumentException("OffsetCurve distance must be a finite value"); |
238 | 0 | } |
239 | | //-- set buffer params, leaving cap style as the default CAP_ROUND |
240 | | |
241 | | /** |
242 | | * Prevent using a very small QuadSegs value, to avoid |
243 | | * offset curve artifacts near the end caps. |
244 | | */ |
245 | 0 | int quadSegs = bp.getQuadrantSegments(); |
246 | 0 | if (quadSegs < MIN_QUADRANT_SEGMENTS) { |
247 | 0 | quadSegs = MIN_QUADRANT_SEGMENTS; |
248 | 0 | } |
249 | 0 | bufferParams.setQuadrantSegments(quadSegs); |
250 | |
|
251 | 0 | bufferParams.setJoinStyle( bp.getJoinStyle()); |
252 | 0 | bufferParams.setMitreLimit( bp.getMitreLimit()); |
253 | 0 | }; |
254 | | |
255 | | /** |
256 | | * Computes a single curve line for each input linear component, |
257 | | * by joining curve sections in order along the raw offset curve. |
258 | | * The default mode is to compute separate curve sections. |
259 | | * |
260 | | * @param pIsJoined true if joined mode should be used. |
261 | | */ |
262 | | void setJoined(bool pIsJoined); |
263 | | |
264 | | static std::unique_ptr<Geometry> getCurve( |
265 | | const Geometry& geom, |
266 | | double dist, |
267 | | int quadSegs, |
268 | | BufferParameters::JoinStyle joinStyle, |
269 | | double mitreLimit); |
270 | | |
271 | | static std::unique_ptr<Geometry> getCurve( |
272 | | const Geometry& geom, double dist); |
273 | | |
274 | | /** |
275 | | * Computes the offset curve of a geometry at a given distance, |
276 | | * joining curve sections into a single line for each input line. |
277 | | * |
278 | | * @param geom a geometry |
279 | | * @param dist the offset distance (positive for left, negative for right) |
280 | | * @return the joined offset curve |
281 | | */ |
282 | | static std::unique_ptr<Geometry> getCurveJoined( |
283 | | const Geometry& geom, double dist); |
284 | | |
285 | | /** |
286 | | * Gets the computed offset curve lines. |
287 | | * |
288 | | * @return the offset curve geometry |
289 | | */ |
290 | | std::unique_ptr<Geometry> getCurve(); |
291 | | |
292 | | /** |
293 | | * Gets the raw offset curve for a line at a given distance. |
294 | | * The quadrant segments, join style and mitre limit can be specified |
295 | | * via BufferParameters. |
296 | | * |
297 | | * The raw offset line may contain loops and other artifacts which are |
298 | | * not present in the true offset curve. |
299 | | * |
300 | | * @param line the line to offset |
301 | | * @param distance the offset distance (positive for left, negative for right) |
302 | | * @param bufParams the buffer parameters to use |
303 | | * @return the raw offset curve points |
304 | | */ |
305 | | static std::unique_ptr<CoordinateSequence> rawOffsetCurve( |
306 | | const LineString& line, |
307 | | double distance, |
308 | | BufferParameters& bufParams); |
309 | | |
310 | | /** |
311 | | * Gets the raw offset curve for a line at a given distance, |
312 | | * with default buffer parameters. |
313 | | * |
314 | | * @param line the line to offset |
315 | | * @param distance the offset distance (positive for left, negative for right) |
316 | | * @return the raw offset curve points |
317 | | */ |
318 | | static std::unique_ptr<CoordinateSequence> rawOffset( |
319 | | const LineString& line, |
320 | | double distance); |
321 | | |
322 | | }; |
323 | | |
324 | | } // namespace geos::operation::buffer |
325 | | } // namespace geos::operation |
326 | | } // namespace geos |
327 | | |
328 | | |
329 | | |