/src/geos/include/geos/operation/buffer/BufferCurveSetBuilder.h
Line | Count | Source |
1 | | /********************************************************************** |
2 | | * |
3 | | * GEOS - Geometry Engine Open Source |
4 | | * http://geos.osgeo.org |
5 | | * |
6 | | * Copyright (C) 2011 Sandro Santilli <strk@kbt.io> |
7 | | * Copyright (C) 2006 Refractions Research Inc. |
8 | | * |
9 | | * This is free software; you can redistribute and/or modify it under |
10 | | * the terms of the GNU Lesser General Public Licence as published |
11 | | * by the Free Software Foundation. |
12 | | * See the COPYING file for more information. |
13 | | * |
14 | | ********************************************************************** |
15 | | * |
16 | | * Last port: operation/buffer/BufferCurveSetBuilder.java 4c343e79f (JTS-1.19) |
17 | | * |
18 | | **********************************************************************/ |
19 | | |
20 | | #pragma once |
21 | | |
22 | | #include <geos/export.h> |
23 | | #include <geos/geom/Location.h> |
24 | | #include <geos/operation/buffer/OffsetCurveBuilder.h> |
25 | | |
26 | | #include <vector> |
27 | | |
28 | | #ifdef _MSC_VER |
29 | | #pragma warning(push) |
30 | | #pragma warning(disable: 4251) // warning C4251: needs to have dll-interface to be used by clients of class |
31 | | #endif |
32 | | |
33 | | // Forward declarations |
34 | | namespace geos { |
35 | | namespace geom { |
36 | | class Geometry; |
37 | | class CoordinateSequence; |
38 | | class PrecisionModel; |
39 | | class GeometryCollection; |
40 | | class Point; |
41 | | class LineString; |
42 | | class LinearRing; |
43 | | class Polygon; |
44 | | } |
45 | | namespace geomgraph { |
46 | | class Label; |
47 | | } |
48 | | namespace noding { |
49 | | class SegmentString; |
50 | | } |
51 | | namespace operation { |
52 | | namespace buffer { |
53 | | class BufferParameters; |
54 | | } |
55 | | } |
56 | | } |
57 | | |
58 | | namespace geos { |
59 | | namespace operation { // geos.operation |
60 | | namespace buffer { // geos.operation.buffer |
61 | | |
62 | | /** |
63 | | * \class BufferCurveSetBuilder |
64 | | * |
65 | | * \brief |
66 | | * Creates all the raw offset curves for a buffer of a Geometry. |
67 | | * |
68 | | * Raw curves need to be noded together and polygonized to form the |
69 | | * final buffer area. |
70 | | * |
71 | | */ |
72 | | class GEOS_DLL BufferCurveSetBuilder { |
73 | | using CoordinateSequence = geos::geom::CoordinateSequence; |
74 | | using Envelope = geos::geom::Envelope; |
75 | | |
76 | | private: |
77 | | |
78 | | static constexpr int MAX_INVERTED_RING_SIZE = 9; |
79 | | static constexpr int INVERTED_CURVE_VERTEX_FACTOR = 4; |
80 | | static constexpr double NEARNESS_FACTOR = 0.99; |
81 | | |
82 | | // To keep track of newly-created Labels. |
83 | | // Labels will be released by object dtor |
84 | | std::vector<geomgraph::Label*> newLabels; |
85 | | const geom::Geometry& inputGeom; |
86 | | double distance; |
87 | | OffsetCurveBuilder curveBuilder; |
88 | | |
89 | | /// The raw offset curves computed. |
90 | | /// This class holds ownership of std::vector elements. |
91 | | /// |
92 | | std::vector<noding::SegmentString*> curveList; |
93 | | bool isInvertOrientation = false; |
94 | | |
95 | | /** |
96 | | * Creates a noding::SegmentString for a coordinate list which is a raw |
97 | | * offset curve, and adds it to the list of buffer curves. |
98 | | * The noding::SegmentString is tagged with a geomgraph::Label |
99 | | * giving the topology of the curve. |
100 | | * The curve may be oriented in either direction. |
101 | | * If the curve is oriented CW, the locations will be: |
102 | | * - Left: Location.EXTERIOR |
103 | | * - Right: Location.INTERIOR |
104 | | * |
105 | | * @param coord is raw offset curve, ownership transferred here |
106 | | */ |
107 | | void addCurve(std::unique_ptr<geom::CoordinateSequence> coord, geom::Location leftLoc, |
108 | | geom::Location rightLoc); |
109 | | |
110 | | void add(const geom::Geometry& g); |
111 | | |
112 | | void addCollection(const geom::GeometryCollection* gc); |
113 | | |
114 | | /** |
115 | | * Add a Point to the graph. |
116 | | */ |
117 | | void addPoint(const geom::Point* p); |
118 | | |
119 | | void addLineString(const geom::LineString* line); |
120 | | |
121 | | void addPolygon(const geom::Polygon* p); |
122 | | |
123 | | void addLinearRingSides(const geom::CoordinateSequence* coord, double p_distance); |
124 | | |
125 | | /** |
126 | | * Add an offset curve for a polygon ring. |
127 | | * The side and left and right topological location arguments |
128 | | * assume that the ring is oriented CW. |
129 | | * If the ring is in the opposite orientation, |
130 | | * the left and right locations must be interchanged and the side |
131 | | * flipped. |
132 | | * |
133 | | * @param coord the coordinates of the ring (must not contain |
134 | | * repeated points) |
135 | | * @param offsetDistance the distance at which to create the buffer |
136 | | * @param side the side of the ring on which to construct the buffer |
137 | | * line |
138 | | * @param cwLeftLoc the location on the L side of the ring |
139 | | * (if it is CW) |
140 | | * @param cwRightLoc the location on the R side of the ring |
141 | | * (if it is CW) |
142 | | */ |
143 | | void addPolygonRingSide(const geom::CoordinateSequence* coord, |
144 | | double offsetDistance, int side, geom::Location cwLeftLoc, |
145 | | geom::Location cwRightLoc); |
146 | | |
147 | | void addRingSide(const geom::CoordinateSequence* coord, |
148 | | double offsetDistance, int side, geom::Location leftLoc, |
149 | | geom::Location rightLoc); |
150 | | |
151 | | /** |
152 | | * Tests whether the offset curve for a ring is fully inverted. |
153 | | * An inverted ("inside-out") curve occurs in some specific situations |
154 | | * involving a buffer distance which should result in a fully-eroded (empty) buffer. |
155 | | * It can happen that the sides of a small, convex polygon |
156 | | * produce offset segments which all cross one another to form |
157 | | * a curve with inverted orientation. |
158 | | * This happens at buffer distances slightly greater than the distance at |
159 | | * which the buffer should disappear. |
160 | | * The inverted curve will produce an incorrect non-empty buffer (for a shell) |
161 | | * or an incorrect hole (for a hole). |
162 | | * It must be discarded from the set of offset curves used in the buffer. |
163 | | * Heuristics are used to reduce the number of cases which area checked, |
164 | | * for efficiency and correctness. |
165 | | * <p> |
166 | | * See https://github.com/locationtech/jts/issues/472 |
167 | | * |
168 | | * @param inputPts the input ring |
169 | | * @param distance the buffer distance |
170 | | * @param curvePts the generated offset curve |
171 | | * @return true if the offset curve is inverted |
172 | | */ |
173 | | static bool isRingCurveInverted( |
174 | | const geom::CoordinateSequence* inputPts, double dist, |
175 | | const geom::CoordinateSequence* curvePts); |
176 | | |
177 | | /** |
178 | | * Tests if there are points on the raw offset curve which may |
179 | | * lie on the final buffer curve |
180 | | * (i.e. they are (approximately) at the buffer distance from the input ring). |
181 | | * For efficiency this only tests a limited set of points on the curve. |
182 | | * |
183 | | * @param inputRing |
184 | | * @param distance |
185 | | * @param curveRing |
186 | | * @return true if the curve contains points lying at the required buffer distance |
187 | | */ |
188 | | static bool hasPointOnBuffer( |
189 | | const CoordinateSequence* inputRing, double dist, |
190 | | const CoordinateSequence* curveRing); |
191 | | |
192 | | /** |
193 | | * The ringCoord is assumed to contain no repeated points. |
194 | | * It may be degenerate (i.e. contain only 1, 2, or 3 points). |
195 | | * In this case it has no area, and hence has a minimum diameter of 0. |
196 | | * |
197 | | * @param ringCoord |
198 | | * @param bufferDistance |
199 | | * @return |
200 | | */ |
201 | | bool isRingFullyEroded(const geom::LinearRing* ring, bool isHole, |
202 | | double bufferDistance); |
203 | | |
204 | | bool isRingFullyEroded(const CoordinateSequence* ringCoord, const Envelope* env, bool isHole, |
205 | | double bufferDistance); |
206 | | |
207 | | /** |
208 | | * Tests whether a triangular ring would be eroded completely by |
209 | | * the given buffer distance. |
210 | | * This is a precise test. It uses the fact that the inner buffer |
211 | | * of a triangle converges on the inCentre of the triangle (the |
212 | | * point equidistant from all sides). If the buffer distance is |
213 | | * greater than the distance of the inCentre from a side, the |
214 | | * triangle will be eroded completely. |
215 | | * |
216 | | * This test is important, since it removes a problematic case where |
217 | | * the buffer distance is slightly larger than the inCentre distance. |
218 | | * In this case the triangle buffer curve "inverts" with incorrect |
219 | | * topology, producing an incorrect hole in the buffer. |
220 | | * |
221 | | * @param triCoord |
222 | | * @param bufferDistance |
223 | | * @return |
224 | | */ |
225 | | bool isTriangleErodedCompletely(const geom::CoordinateSequence* triCoords, |
226 | | double bufferDistance); |
227 | | |
228 | | // Declare type as noncopyable |
229 | | BufferCurveSetBuilder(const BufferCurveSetBuilder& other) = delete; |
230 | | BufferCurveSetBuilder& operator=(const BufferCurveSetBuilder& rhs) = delete; |
231 | | |
232 | | /** |
233 | | * Computes orientation of a ring using a signed-area orientation test. |
234 | | * For invalid (self-crossing) rings this ensures the largest enclosed area |
235 | | * is taken to be the interior of the ring. |
236 | | * This produces a more sensible result when |
237 | | * used for repairing polygonal geometry via buffer-by-zero. |
238 | | * For buffer, using the lower robustness of orientation-by-area |
239 | | * doesn't matter, since narrow or flat rings |
240 | | * produce an acceptable offset curve for either orientation. |
241 | | * |
242 | | * @param coord the ring coordinates |
243 | | * @return true if the ring is CCW |
244 | | */ |
245 | | bool isRingCCW(const geom::CoordinateSequence* coords) const; |
246 | | |
247 | | public: |
248 | | |
249 | | /// Constructor |
250 | | BufferCurveSetBuilder( |
251 | | const geom::Geometry& newInputGeom, |
252 | | double newDistance, |
253 | | const geom::PrecisionModel* newPm, |
254 | | const BufferParameters& newBufParams) |
255 | 0 | : inputGeom(newInputGeom) |
256 | 0 | , distance(newDistance) |
257 | 0 | , curveBuilder(newPm, newBufParams) |
258 | 0 | , curveList() |
259 | 0 | , isInvertOrientation(false) |
260 | 0 | {}; |
261 | | |
262 | | /// Destructor |
263 | | ~BufferCurveSetBuilder(); |
264 | | |
265 | | /** \brief |
266 | | * Computes the set of raw offset curves for the buffer. |
267 | | * |
268 | | * Each offset curve has an attached {@link geomgraph::Label} indicating |
269 | | * its left and right location. |
270 | | * |
271 | | * @return a Collection of SegmentStrings representing the raw buffer curves |
272 | | */ |
273 | | std::vector<noding::SegmentString*>& getCurves(); |
274 | | |
275 | | /// \brief Add raw curves for a set of CoordinateSequences. |
276 | | /// |
277 | | /// @param lineList is a list of CoordinateSequence, ownership |
278 | | /// of which is transferred here |
279 | | /// @param leftLoc left location |
280 | | /// @param rightLoc right location |
281 | | /// |
282 | | void addCurves(std::vector<std::unique_ptr<geom::CoordinateSequence>>& lineList, |
283 | | geom::Location leftLoc, geom::Location rightLoc); |
284 | | |
285 | | /** |
286 | | * Sets whether the offset curve is generated |
287 | | * using the inverted orientation of input rings. |
288 | | * This allows generating a buffer(0) polygon from the smaller lobes |
289 | | * of self-crossing rings. |
290 | | * |
291 | | * @param p_isInvertOrientation true if input ring orientation should be inverted |
292 | | */ |
293 | 0 | void setInvertOrientation(bool p_isInvertOrientation) { |
294 | 0 | isInvertOrientation = p_isInvertOrientation; |
295 | 0 | } |
296 | | |
297 | | }; |
298 | | |
299 | | } // namespace geos::operation::buffer |
300 | | } // namespace geos::operation |
301 | | } // namespace geos |
302 | | |
303 | | #ifdef _MSC_VER |
304 | | #pragma warning(pop) |
305 | | #endif |