Coverage Report

Created: 2026-02-26 07:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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