Coverage Report

Created: 2026-09-01 06:35

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
double
49
LineSegment::projectionFactor(const CoordinateXY& p) const
50
0
{
51
0
    return projectionFactor(p0, p1, p);
52
0
}
53
54
/*public*/
55
double
56
LineSegment::projectionFactor(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& q)
57
0
{
58
0
    if(q == p0) {
59
0
        return 0.0;
60
0
    }
61
0
    if(q == p1) {
62
0
        return 1.0;
63
0
    }
64
0
    if(p0 == p1) {
65
0
        return 0.0;
66
0
    }
67
    // Otherwise, use comp.graphics.algorithms Frequently Asked Questions method
68
    /*(1)             AC dot AB
69
                   r = ---------
70
                         ||AB||^2
71
                r has the following meaning:
72
                r=0 P = A
73
                r=1 P = B
74
                r<0 P is on the backward extension of AB
75
                r>1 P is on the forward extension of AB
76
                0<r<1 P is interior to AB
77
        */
78
0
    double dx = p1.x - p0.x;
79
0
    double dy = p1.y - p0.y;
80
0
    double len2 = dx * dx + dy * dy;
81
0
    double r = ((q.x - p0.x) * dx + (q.y - p0.y) * dy) / len2;
82
0
    return r;
83
0
}
84
85
/*public*/
86
double
87
LineSegment::segmentFraction(const CoordinateXY& inputPt) const
88
0
{
89
0
    double segFrac = projectionFactor(inputPt);
90
0
    if(segFrac < 0.0) {
91
0
        segFrac = 0.0;
92
0
    }
93
0
    else if(segFrac > 1.0) {
94
0
        segFrac = 1.0;
95
0
    }
96
0
    return segFrac;
97
0
}
98
99
/*public*/
100
void
101
LineSegment::project(const Coordinate& p, Coordinate& ret) const
102
0
{
103
0
    if(p == p0 || p == p1) {
104
0
        ret = p;
105
0
        return;
106
0
    }
107
0
    double r = projectionFactor(p);
108
0
    ret = Coordinate(p0.x + r * (p1.x - p0.x), p0.y + r * (p1.y - p0.y));
109
0
}
110
111
CoordinateXY
112
LineSegment::project(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& q)
113
0
{
114
0
    if(q == p0 || q == p1) {
115
0
        return q;
116
0
    }
117
0
    double r = projectionFactor(p0, p1, q);
118
0
    double x = p0.x + r * (p1.x - p0.x);
119
0
    double y = p0.y + r * (p1.y - p0.y);
120
0
    return CoordinateXY(x, y);
121
0
}
122
123
CoordinateXY
124
LineSegment::project(const CoordinateXY& p) const
125
0
{
126
0
    return project(p0, p1, p);
127
0
}
128
129
/*private*/
130
CoordinateXY
131
LineSegment::project(const CoordinateXY& p0, const CoordinateXY& p1, double factor)
132
0
{
133
0
    if(factor == 1.0) {
134
0
        return p1;
135
0
    }
136
137
0
    return CoordinateXY(p0.x + factor * (p1.x - p0.x), p0.y + factor * (p1.y - p0.y));
138
0
}
139
140
void
141
LineSegment::project(double factor, CoordinateXY& ret) const
142
0
{
143
0
    ret = project(p0, p1, factor);
144
0
}
145
146
bool
147
LineSegment::project(const LineSegment& seg, LineSegment& ret) const
148
0
{
149
0
    double pf0 = projectionFactor(seg.p0);
150
0
    double pf1 = projectionFactor(seg.p1);
151
    // check if segment projects at all
152
153
0
    if(pf0 >= 1.0 && pf1 >= 1.0) {
154
0
        return false;
155
0
    }
156
157
0
    if(pf0 <= 0.0 && pf1 <= 0.0) {
158
0
        return false;
159
0
    }
160
161
0
    Coordinate newp0;
162
0
    project(pf0, newp0);
163
0
    if (pf0 < 0.0) newp0 = p0;
164
0
    if (pf0 > 1.0) newp0 = p1;
165
166
0
    Coordinate newp1;
167
0
    project(pf1, newp1);
168
0
    if (pf1 < 0.0) newp1 = p0;
169
0
    if (pf1 > 1.0) newp1 = p1;
170
171
0
    ret.setCoordinates(newp0, newp1);
172
173
0
    return true;
174
0
}
175
176
CoordinateXY
177
LineSegment::closestPoint(const CoordinateXY& p0, const CoordinateXY& p1, const CoordinateXY& q)
178
0
{
179
0
    double factor = projectionFactor(p0, p1, q);
180
0
    if(factor > 0 && factor < 1) {
181
0
        return project(p0, p1, factor);
182
0
    }
183
184
0
    double dist0 = p0.distance(q);
185
0
    double dist1 = p1.distance(q);
186
0
    if(dist0 < dist1) {
187
0
        return p0;
188
0
    }
189
190
0
    return p1;
191
0
}
192
193
//Coordinate*
194
void
195
LineSegment::closestPoint(const CoordinateXY& p, CoordinateXY& ret) const
196
0
{
197
0
    double factor = projectionFactor(p);
198
0
    if(factor > 0 && factor < 1) {
199
0
        project(factor, ret);
200
0
        return;
201
0
    }
202
0
    double dist0 = p0.distance(p);
203
0
    double dist1 = p1.distance(p);
204
0
    if(dist0 < dist1) {
205
0
        ret = p0;
206
0
        return;
207
0
    }
208
0
    ret = p1;
209
0
}
210
211
/*public*/
212
bool
213
LineSegment::equalsTopo(const LineSegment& other) const
214
0
{
215
0
    return (p0 == other.p0 && p1 == other.p1) || (p0 == other.p1 && p1 == other.p0);
216
0
}
217
218
/*public*/
219
int
220
LineSegment::orientationIndex(const LineSegment& seg) const
221
0
{
222
0
    int orient0 = algorithm::Orientation::index(p0, p1, seg.p0);
223
0
    int orient1 = algorithm::Orientation::index(p0, p1, seg.p1);
224
    // this handles the case where the points are L or collinear
225
0
    if(orient0 >= 0 && orient1 >= 0) {
226
0
        return std::max(orient0, orient1);
227
0
    }
228
    // this handles the case where the points are R or collinear
229
0
    if(orient0 <= 0 && orient1 <= 0) {
230
0
        return std::min(orient0, orient1);
231
0
    }
232
    // points lie on opposite sides ==> indeterminate orientation
233
0
    return 0;
234
0
}
235
236
std::array<Coordinate, 2>
237
LineSegment::closestPoints(const LineSegment& line)
238
0
{
239
    // test for intersection
240
0
    Coordinate intPt = intersection(line);
241
0
    if(!intPt.isNull()) {
242
0
        return {{ intPt, intPt }};
243
0
    }
244
245
    /*
246
     * if no intersection closest pair contains at least one endpoint.
247
     * Test each endpoint in turn.
248
     */
249
0
    std::array<Coordinate, 2> closestPt;
250
251
0
    double dist;
252
253
0
    Coordinate close00;
254
0
    closestPoint(line.p0, close00);
255
0
    double minDistance = close00.distance(line.p0);
256
257
0
    closestPt[0] = close00;
258
0
    closestPt[1] = line.p0;
259
260
0
    Coordinate close01;
261
0
    closestPoint(line.p1, close01);
262
0
    dist = close01.distance(line.p1);
263
0
    if(dist < minDistance) {
264
0
        minDistance = dist;
265
0
        closestPt[0] = close01;
266
0
        closestPt[1] = line.p1;
267
0
    }
268
269
0
    Coordinate close10;
270
0
    line.closestPoint(p0, close10);
271
0
    dist = close10.distance(p0);
272
0
    if(dist < minDistance) {
273
0
        minDistance = dist;
274
0
        closestPt[0] = p0;
275
0
        closestPt[1] = close10;
276
0
    }
277
278
0
    Coordinate close11;
279
0
    line.closestPoint(p1, close11);
280
0
    dist = close11.distance(p1);
281
0
    if(dist < minDistance) {
282
0
        closestPt[0] = p1;
283
0
        closestPt[1] = close11;
284
0
    }
285
286
0
    return closestPt;
287
0
}
288
289
std::array<CoordinateXY, 2>
290
LineSegment::closestPoints(const CoordinateXY& p0, const CoordinateXY& p1,
291
                           const CoordinateXY& q0, const CoordinateXY& q1)
292
0
{
293
    // test for intersection
294
0
    algorithm::LineIntersector li;
295
0
    li.computeIntersection(p0, p1, q0, q1);
296
0
    if(li.hasIntersection()) {
297
0
        return {CoordinateXY{li.getIntersection(0)},
298
0
                   CoordinateXY{li.getIntersection(0)}};
299
0
    }
300
301
    /*
302
     * if no intersection closest pair contains at least one endpoint.
303
     * Test each endpoint in turn.
304
     */
305
0
    std::array<CoordinateXY, 2> closestPt;
306
307
0
    CoordinateXY close00 = closestPoint(p0, p1, q0);
308
0
    double minDistance = close00.distance(q0);
309
310
0
    closestPt[0] = close00;
311
0
    closestPt[1] = q0;
312
313
0
    CoordinateXY close01 = closestPoint(p0, p1, q1);
314
0
    double dist = close01.distance(q1);
315
0
    if(dist < minDistance) {
316
0
        minDistance = dist;
317
0
        closestPt[0] = close01;
318
0
        closestPt[1] = q1;
319
0
    }
320
321
0
    CoordinateXY close10 = closestPoint(q0, q1, p0);
322
0
    dist = close10.distance(p0);
323
0
    if(dist < minDistance) {
324
0
        minDistance = dist;
325
0
        closestPt[0] = p0;
326
0
        closestPt[1] = close10;
327
0
    }
328
329
0
    CoordinateXY close11 = closestPoint(q0, q1, p1);
330
0
    dist = close11.distance(p1);
331
0
    if(dist < minDistance) {
332
0
        closestPt[0] = p1;
333
0
        closestPt[1] = close11;
334
0
    }
335
336
0
    return closestPt;
337
0
}
338
339
Coordinate
340
LineSegment::intersection(const LineSegment& line) const
341
0
{
342
0
    algorithm::LineIntersector li;
343
0
    li.computeIntersection(p0, p1, line.p0, line.p1);
344
0
    if(li.hasIntersection()) {
345
0
        return li.getIntersection(0);
346
0
    }
347
0
    Coordinate rv;
348
0
    rv.setNull();
349
0
    return rv;
350
0
}
351
352
Coordinate
353
LineSegment::lineIntersection(const LineSegment& line) const
354
0
{
355
    // TODO return a CoordinateXY here.
356
0
    return Coordinate(algorithm::Intersection::intersection(p0, p1, line.p0, line.p1));
357
0
}
358
359
360
/* public */
361
void
362
LineSegment::pointAlongOffset(double segmentLengthFraction,
363
                              double offsetDistance,
364
                              Coordinate& ret) const
365
0
{
366
    // the point on the segment line
367
0
    double segx = p0.x + segmentLengthFraction * (p1.x - p0.x);
368
0
    double segy = p0.y + segmentLengthFraction * (p1.y - p0.y);
369
370
0
    double dx = p1.x - p0.x;
371
0
    double dy = p1.y - p0.y;
372
0
    double len = std::sqrt(dx * dx + dy * dy);
373
374
0
    double ux = 0.0;
375
0
    double uy = 0.0;
376
0
    if(offsetDistance != 0.0) {
377
0
        if(len <= 0.0) {
378
0
            throw util::IllegalStateException("Cannot compute offset from zero-length line segment");
379
0
        }
380
381
        // u is the vector that is the length of the offset,
382
        // in the direction of the segment
383
0
        ux = offsetDistance * dx / len;
384
0
        uy = offsetDistance * dy / len;
385
0
    }
386
387
    // the offset point is the seg point plus the offset
388
    // vector rotated 90 degrees CCW
389
0
    double offsetx = segx - uy;
390
0
    double offsety = segy + ux;
391
392
0
    ret = Coordinate(offsetx, offsety);
393
0
}
394
395
/* public */
396
LineSegment
397
LineSegment::offset(double offsetDistance)
398
0
{
399
0
    Coordinate offset0, offset1;
400
0
    pointAlongOffset(0, offsetDistance, offset0);
401
0
    pointAlongOffset(1, offsetDistance, offset1);
402
0
    LineSegment ls(offset0, offset1);
403
0
    return ls;
404
0
}
405
406
/* public */
407
double
408
LineSegment::distancePerpendicularOriented(const CoordinateXY& p) const
409
0
{
410
0
    if (p0.equals2D(p1))
411
0
        return p0.distance(p);
412
0
    double dist = distancePerpendicular(p);
413
0
    if (orientationIndex(p) < 0)
414
0
        return -dist;
415
0
    return dist;
416
0
}
417
418
/* public */
419
std::unique_ptr<LineString>
420
LineSegment::toGeometry(const GeometryFactory& gf) const
421
0
{
422
0
    auto cl = detail::make_unique<CoordinateSequence>(2u);
423
424
0
    cl->setAt(p0, 0);
425
0
    cl->setAt(p1, 1);
426
0
    return gf.createLineString(std::move(cl));
427
0
}
428
429
/* public */
430
std::ostream&
431
LineSegment::operator<< (std::ostream& os)
432
0
{
433
0
    return os << "LINESEGMENT("
434
0
        << p0.x << " " << p0.y << ","
435
0
        << p1.x << " " << p1.y << ")";
436
0
}
437
438
439
440
441
} // namespace geos::geom
442
} // namespace geos