Coverage Report

Created: 2025-11-24 06:57

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