Coverage Report

Created: 2026-02-14 06:22

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/geos/src/geom/LineSegment.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * GEOS - Geometry Engine Open Source
4
 * http://geos.osgeo.org
5
 *
6
 * Copyright (C) 2009 2011 Sandro Santilli <strk@kbt.io>
7
 * Copyright (C) 2005-2006 Refractions Research Inc.
8
 * Copyright (C) 2001-2002 Vivid Solutions Inc.
9
 *
10
 * This is free software; you can redistribute and/or modify it under
11
 * the terms of the GNU Lesser General Public Licence as published
12
 * by the Free Software Foundation.
13
 * See the COPYING file for more information.
14
 *
15
 **********************************************************************
16
 *
17
 * Last port: geom/LineSegment.java r18 (JTS-1.11)
18
 *
19
 **********************************************************************/
20
21
#include <geos/constants.h>
22
#include <geos/geom/LineSegment.h>
23
#include <geos/geom/LineString.h> // for toGeometry
24
#include <geos/geom/CoordinateSequence.h>
25
#include <geos/geom/GeometryFactory.h>
26
#include <geos/algorithm/LineIntersector.h>
27
#include <geos/algorithm/Intersection.h>
28
#include <geos/util/IllegalStateException.h>
29
#include <geos/profiler.h>
30
#include <geos/util.h>
31
32
#include <algorithm> // for max
33
#include <sstream>
34
#include <cmath>
35
36
37
namespace geos {
38
namespace geom { // geos::geom
39
40
41
/*public*/
42
void
43
LineSegment::reverse()
44
0
{
45
0
    std::swap(p0, p1);
46
0
}
47
48
/*public*/
49
double
50
LineSegment::projectionFactor(const CoordinateXY& p) const
51
0
{
52
0
    if(p == p0) {
53
0
        return 0.0;
54
0
    }
55
0
    if(p == p1) {
56
0
        return 1.0;
57
0
    }
58
0
    if(p0 == p1) {
59
0
        return 0.0;
60
0
    }
61
    // Otherwise, use comp.graphics.algorithms Frequently Asked Questions method
62
    /*(1)             AC dot AB
63
                   r = ---------
64
                         ||AB||^2
65
                r has the following meaning:
66
                r=0 P = A
67
                r=1 P = B
68
                r<0 P is on the backward extension of AB
69
                r>1 P is on the forward extension of AB
70
                0<r<1 P is interior to AB
71
        */
72
0
    double dx = p1.x - p0.x;
73
0
    double dy = p1.y - p0.y;
74
0
    double len2 = dx * dx + dy * dy;
75
0
    double r = ((p.x - p0.x) * dx + (p.y - p0.y) * dy) / len2;
76
0
    return r;
77
0
}
78
79
/*public*/
80
double
81
LineSegment::segmentFraction(const CoordinateXY& inputPt) const
82
0
{
83
0
    double segFrac = projectionFactor(inputPt);
84
0
    if(segFrac < 0.0) {
85
0
        segFrac = 0.0;
86
0
    }
87
0
    else if(segFrac > 1.0) {
88
0
        segFrac = 1.0;
89
0
    }
90
0
    return segFrac;
91
0
}
92
93
/*public*/
94
void
95
LineSegment::project(const Coordinate& p, Coordinate& ret) const
96
0
{
97
0
    if(p == p0 || p == p1) {
98
0
        ret = p;
99
0
        return;
100
0
    }
101
0
    double r = projectionFactor(p);
102
0
    ret = Coordinate(p0.x + r * (p1.x - p0.x), p0.y + r * (p1.y - p0.y));
103
0
}
104
105
CoordinateXY
106
LineSegment::project(const CoordinateXY& p) const
107
0
{
108
0
    if(p == p0 || p == p1) {
109
0
        return p;
110
0
    }
111
0
    double r = projectionFactor(p);
112
0
    double x = p0.x + r * (p1.x - p0.x);
113
0
    double y = p0.y + r * (p1.y - p0.y);
114
0
    return CoordinateXY(x, y);
115
0
}
116
117
118
119
/*private*/
120
void
121
LineSegment::project(double factor, CoordinateXY& ret) const
122
0
{
123
0
    if( factor == 1.0 )
124
0
        ret = p1;
125
0
    else
126
0
        ret = CoordinateXY(p0.x + factor * (p1.x - p0.x), p0.y + factor * (p1.y - p0.y));
127
0
}
128
129
bool
130
LineSegment::project(const LineSegment& seg, LineSegment& ret) const
131
0
{
132
0
    double pf0 = projectionFactor(seg.p0);
133
0
    double pf1 = projectionFactor(seg.p1);
134
    // check if segment projects at all
135
136
0
    if(pf0 >= 1.0 && pf1 >= 1.0) {
137
0
        return false;
138
0
    }
139
140
0
    if(pf0 <= 0.0 && pf1 <= 0.0) {
141
0
        return false;
142
0
    }
143
144
0
    Coordinate newp0;
145
0
    project(pf0, newp0);
146
0
    if (pf0 < 0.0) newp0 = p0;
147
0
    if (pf0 > 1.0) newp0 = p1;
148
149
0
    Coordinate newp1;
150
0
    project(pf1, newp1);
151
0
    if (pf1 < 0.0) newp1 = p0;
152
0
    if (pf1 > 1.0) newp1 = p1;
153
154
0
    ret.setCoordinates(newp0, newp1);
155
156
0
    return true;
157
0
}
158
159
//Coordinate*
160
void
161
LineSegment::closestPoint(const CoordinateXY& p, CoordinateXY& ret) const
162
0
{
163
0
    double factor = projectionFactor(p);
164
0
    if(factor > 0 && factor < 1) {
165
0
        project(factor, ret);
166
0
        return;
167
0
    }
168
0
    double dist0 = p0.distance(p);
169
0
    double dist1 = p1.distance(p);
170
0
    if(dist0 < dist1) {
171
0
        ret = p0;
172
0
        return;
173
0
    }
174
0
    ret = p1;
175
0
}
176
177
/*public*/
178
bool
179
LineSegment::equalsTopo(const LineSegment& other) const
180
0
{
181
0
    return (p0 == other.p0 && p1 == other.p1) || (p0 == other.p1 && p1 == other.p0);
182
0
}
183
184
/*public*/
185
int
186
LineSegment::orientationIndex(const LineSegment& seg) const
187
0
{
188
0
    int orient0 = algorithm::Orientation::index(p0, p1, seg.p0);
189
0
    int orient1 = algorithm::Orientation::index(p0, p1, seg.p1);
190
    // this handles the case where the points are L or collinear
191
0
    if(orient0 >= 0 && orient1 >= 0) {
192
0
        return std::max(orient0, orient1);
193
0
    }
194
    // this handles the case where the points are R or collinear
195
0
    if(orient0 <= 0 && orient1 <= 0) {
196
0
        return std::min(orient0, orient1);
197
0
    }
198
    // points lie on opposite sides ==> indeterminate orientation
199
0
    return 0;
200
0
}
201
202
std::array<Coordinate, 2>
203
LineSegment::closestPoints(const LineSegment& line)
204
0
{
205
    // test for intersection
206
0
    Coordinate intPt = intersection(line);
207
0
    if(!intPt.isNull()) {
208
0
        return {{ intPt, intPt }};
209
0
    }
210
211
    /*
212
     * if no intersection closest pair contains at least one endpoint.
213
     * Test each endpoint in turn.
214
     */
215
0
    std::array<Coordinate, 2> closestPt;
216
217
0
    double dist;
218
219
0
    Coordinate close00;
220
0
    closestPoint(line.p0, close00);
221
0
    double minDistance = close00.distance(line.p0);
222
223
0
    closestPt[0] = close00;
224
0
    closestPt[1] = line.p0;
225
226
0
    Coordinate close01;
227
0
    closestPoint(line.p1, close01);
228
0
    dist = close01.distance(line.p1);
229
0
    if(dist < minDistance) {
230
0
        minDistance = dist;
231
0
        closestPt[0] = close01;
232
0
        closestPt[1] = line.p1;
233
0
    }
234
235
0
    Coordinate close10;
236
0
    line.closestPoint(p0, close10);
237
0
    dist = close10.distance(p0);
238
0
    if(dist < minDistance) {
239
0
        minDistance = dist;
240
0
        closestPt[0] = p0;
241
0
        closestPt[1] = close10;
242
0
    }
243
244
0
    Coordinate close11;
245
0
    line.closestPoint(p1, close11);
246
0
    dist = close11.distance(p1);
247
0
    if(dist < minDistance) {
248
0
        closestPt[0] = p1;
249
0
        closestPt[1] = close11;
250
0
    }
251
252
0
    return closestPt;
253
0
}
254
255
Coordinate
256
LineSegment::intersection(const LineSegment& line) const
257
0
{
258
0
    algorithm::LineIntersector li;
259
0
    li.computeIntersection(p0, p1, line.p0, line.p1);
260
0
    if(li.hasIntersection()) {
261
0
        return li.getIntersection(0);
262
0
    }
263
0
    Coordinate rv;
264
0
    rv.setNull();
265
0
    return rv;
266
0
}
267
268
Coordinate
269
LineSegment::lineIntersection(const LineSegment& line) const
270
0
{
271
    // TODO return a CoordinateXY here.
272
0
    return Coordinate(algorithm::Intersection::intersection(p0, p1, line.p0, line.p1));
273
0
}
274
275
276
/* public */
277
void
278
LineSegment::pointAlongOffset(double segmentLengthFraction,
279
                              double offsetDistance,
280
                              Coordinate& ret) const
281
0
{
282
    // the point on the segment line
283
0
    double segx = p0.x + segmentLengthFraction * (p1.x - p0.x);
284
0
    double segy = p0.y + segmentLengthFraction * (p1.y - p0.y);
285
286
0
    double dx = p1.x - p0.x;
287
0
    double dy = p1.y - p0.y;
288
0
    double len = std::sqrt(dx * dx + dy * dy);
289
290
0
    double ux = 0.0;
291
0
    double uy = 0.0;
292
0
    if(offsetDistance != 0.0) {
293
0
        if(len <= 0.0) {
294
0
            throw util::IllegalStateException("Cannot compute offset from zero-length line segment");
295
0
        }
296
297
        // u is the vector that is the length of the offset,
298
        // in the direction of the segment
299
0
        ux = offsetDistance * dx / len;
300
0
        uy = offsetDistance * dy / len;
301
0
    }
302
303
    // the offset point is the seg point plus the offset
304
    // vector rotated 90 degrees CCW
305
0
    double offsetx = segx - uy;
306
0
    double offsety = segy + ux;
307
308
0
    ret = Coordinate(offsetx, offsety);
309
0
}
310
311
/* public */
312
LineSegment
313
LineSegment::offset(double offsetDistance)
314
0
{
315
0
    Coordinate offset0, offset1;
316
0
    pointAlongOffset(0, offsetDistance, offset0);
317
0
    pointAlongOffset(1, offsetDistance, offset1);
318
0
    LineSegment ls(offset0, offset1);
319
0
    return ls;
320
0
}
321
322
/* public */
323
double
324
LineSegment::distancePerpendicularOriented(const CoordinateXY& p) const
325
0
{
326
0
    if (p0.equals2D(p1))
327
0
        return p0.distance(p);
328
0
    double dist = distancePerpendicular(p);
329
0
    if (orientationIndex(p) < 0)
330
0
        return -dist;
331
0
    return dist;
332
0
}
333
334
/* public */
335
std::unique_ptr<LineString>
336
LineSegment::toGeometry(const GeometryFactory& gf) const
337
0
{
338
0
    auto cl = detail::make_unique<CoordinateSequence>(2u);
339
340
0
    cl->setAt(p0, 0);
341
0
    cl->setAt(p1, 1);
342
0
    return gf.createLineString(std::move(cl));
343
0
}
344
345
/* public */
346
std::ostream&
347
LineSegment::operator<< (std::ostream& os)
348
0
{
349
0
    return os << "LINESEGMENT("
350
0
        << p0.x << " " << p0.y << ","
351
0
        << p1.x << " " << p1.y << ")";
352
0
}
353
354
355
356
357
} // namespace geos::geom
358
} // namespace geos