GEOS 3.15.0dev
LineIntersector.h
1/**********************************************************************
2 *
3 * GEOS - Geometry Engine Open Source
4 * http://geos.osgeo.org
5 *
6 * Copyright (C) 2005-2006 Refractions Research Inc.
7 * Copyright (C) 2001-2002 Vivid Solutions 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: algorithm/RobustLineIntersector.java r785 (JTS-1.13+)
17 *
18 **********************************************************************/
19
20#pragma once
21
22#include <geos/export.h>
23#include <geos/algorithm/Intersection.h>
24#include <geos/algorithm/Interpolate.h>
25#include <geos/algorithm/Orientation.h>
26#include <geos/geom/Coordinate.h>
27#include <geos/geom/Envelope.h>
28#include <geos/geom/PrecisionModel.h>
29
30#include <string>
31
32// Forward declarations
33namespace geos {
34namespace geom {
35class PrecisionModel;
36}
37}
38
39namespace geos {
40namespace algorithm { // geos::algorithm
41
53class GEOS_DLL LineIntersector {
54public:
55
74 static double computeEdgeDistance(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1);
75
76 static double nonRobustComputeEdgeDistance(const geom::Coordinate& p, const geom::Coordinate& p1,
77 const geom::Coordinate& p2);
78
79 explicit LineIntersector(const geom::PrecisionModel* initialPrecisionModel = nullptr)
80 :
81 precisionModel(initialPrecisionModel),
82 result(0),
83 inputLines(),
84 isProperVar(false)
85 {}
86
87 ~LineIntersector() = default;
88
97 {
98 if(isInteriorIntersection(0)) {
99 return true;
100 }
101 if(isInteriorIntersection(1)) {
102 return true;
103 }
104 return false;
105 };
106
114 bool isInteriorIntersection(std::size_t inputLineIndex) const
115 {
116 for(std::size_t i = 0; i < result; ++i) {
117 if(!(intPt[i].equals2D(*inputLines[inputLineIndex][0])
118 || intPt[i].equals2D(*inputLines[inputLineIndex][1]))) {
119 return true;
120 }
121 }
122 return false;
123 };
124
131 void
133 {
134 precisionModel = newPM;
135 }
136
137 enum intersection_type : uint8_t {
139 NO_INTERSECTION = 0,
140
142 POINT_INTERSECTION = 1,
143
145 COLLINEAR_INTERSECTION = 2
146 };
147
149 template<typename C1, typename C2>
150 void computeIntersection(const C1& p1, const C1& p2,
151 const C2& p3, const C2& p4)
152 {
153 inputLines[0][0] = &p1;
154 inputLines[0][1] = &p2;
155 inputLines[1][0] = &p3;
156 inputLines[1][1] = &p4;
157 result = computeIntersect(p1, p2, p3, p4);
158 }
159
161 void computeIntersection(const geom::CoordinateSequence& p, std::size_t p0,
162 const geom::CoordinateSequence& q, std::size_t q0);
163
165 void computeIntersection(const geom::CoordinateSequence& p, std::size_t p0, std::size_t p1,
166 const geom::CoordinateSequence& q, std::size_t q0, std::size_t q1);
167
168 std::string toString() const;
169
175 bool
177 {
178 return result != NO_INTERSECTION;
179 }
180
181
189 const geom::CoordinateXY*
190 getEndpoint(std::size_t segmentIndex, std::size_t ptIndex) const
191 {
192 return inputLines[segmentIndex][ptIndex];
193 }
194
199 size_t
201 {
202 return result;
203 }
204
205
212 const geom::CoordinateXYZM&
213 getIntersection(std::size_t intIndex) const
214 {
215 return intPt[intIndex];
216 }
217
222 static bool isSameSignAndNonZero(double a, double b);
223
234 bool isIntersection(const geom::Coordinate& pt) const
235 {
236 for(std::size_t i = 0; i < result; ++i) {
237 if(intPt[i].equals2D(pt)) {
238 return true;
239 }
240 }
241 return false;
242 };
243
258 bool
259 isProper() const
260 {
261 return hasIntersection() && isProperVar;
262 }
263
274 const geom::Coordinate& getIntersectionAlongSegment(std::size_t segmentIndex, std::size_t intIndex);
275
285 std::size_t getIndexAlongSegment(std::size_t segmentIndex, std::size_t intIndex);
286
296 double getEdgeDistance(std::size_t geomIndex, std::size_t intIndex) const;
297
298private:
299
304 const geom::PrecisionModel* precisionModel;
305
306 std::size_t result;
307
308 const geom::CoordinateXY* inputLines[2][2];
309
314 geom::CoordinateXYZM intPt[2];
315
320 std::size_t intLineIndex[2][2];
321
322 bool isProperVar;
323 //Coordinate &pa;
324 //Coordinate &pb;
325
326 bool
327 isCollinear() const
328 {
329 return result == COLLINEAR_INTERSECTION;
330 }
331
332 template<typename C1, typename C2>
333 uint8_t computeIntersect(const C1& p1, const C1& p2, const C2& q1, const C2& q2)
334 {
335 isProperVar = false;
336
337 // first try a fast test to see if the envelopes of the lines intersect
338 if(!geom::Envelope::intersects(p1, p2, q1, q2)) {
339 return NO_INTERSECTION;
340 }
341
342 // for each endpoint, compute which side of the other segment it lies
343 // if both endpoints lie on the same side of the other segment,
344 // the segments do not intersect
345 int Pq1 = Orientation::index(p1, p2, q1);
346 int Pq2 = Orientation::index(p1, p2, q2);
347
348 if((Pq1 > 0 && Pq2 > 0) || (Pq1 < 0 && Pq2 < 0)) {
349 return NO_INTERSECTION;
350 }
351
352 int Qp1 = Orientation::index(q1, q2, p1);
353 int Qp2 = Orientation::index(q1, q2, p2);
354
355 if((Qp1 > 0 && Qp2 > 0) || (Qp1 < 0 && Qp2 < 0)) {
356 return NO_INTERSECTION;
357 }
358
362 bool collinear = Pq1 == 0 && Pq2 == 0 && Qp1 == 0 && Qp2 == 0;
363 if(collinear) {
364 return computeCollinearIntersection(p1, p2, q1, q2);
365 }
366
367 /*
368 * At this point we know that there is a single intersection point
369 * (since the lines are not collinear).
370 */
371
372 /*
373 * Check if the intersection is an endpoint.
374 * If it is, copy the endpoint as
375 * the intersection point. Copying the point rather than
376 * computing it ensures the point has the exact value,
377 * which is important for robustness. It is sufficient to
378 * simply check for an endpoint which is on the other line,
379 * since at this point we know that the inputLines must
380 * intersect.
381 */
382 geom::CoordinateXYZM p;
383 double z = DoubleNotANumber;
384 double m = DoubleNotANumber;
385
386 if(Pq1 == 0 || Pq2 == 0 || Qp1 == 0 || Qp2 == 0) {
387
388 isProperVar = false;
389
390 /* Check for two equal endpoints.
391 * This is done explicitly rather than by the orientation tests
392 * below in order to improve robustness.
393 *
394 * (A example where the orientation tests fail
395 * to be consistent is:
396 *
397 * LINESTRING ( 19.850257749638203 46.29709338043669,
398 * 20.31970698357233 46.76654261437082 )
399 * and
400 * LINESTRING ( -48.51001596420236 -22.063180333403878,
401 * 19.850257749638203 46.29709338043669 )
402 *
403 * which used to produce the INCORRECT result:
404 * (20.31970698357233, 46.76654261437082, NaN)
405 */
406
407 if (p1.equals2D(q1)) {
408 p = p1;
409 z = Interpolate::zGet(p1, q1);
410 m = Interpolate::mGet(p1, q1);
411 }
412 else if (p1.equals2D(q2)) {
413 p = p1;
414 z = Interpolate::zGet(p1, q2);
415 m = Interpolate::mGet(p1, q2);
416 }
417 else if (p2.equals2D(q1)) {
418 p = p2;
419 z = Interpolate::zGet(p2, q1);
420 m = Interpolate::mGet(p2, q1);
421 }
422 else if (p2.equals2D(q2)) {
423 p = p2;
424 z = Interpolate::zGet(p2, q2);
425 m = Interpolate::mGet(p2, q2);
426 }
427 /*
428 * Now check to see if any endpoint lies on the interior of the other segment.
429 */
430 else if(Pq1 == 0) {
431 p = q1;
432 z = Interpolate::zGetOrInterpolate(q1, p1, p2);
433 m = Interpolate::mGetOrInterpolate(q1, p1, p2);
434 }
435 else if(Pq2 == 0) {
436 p = q2;
437 z = Interpolate::zGetOrInterpolate(q2, p1, p2);
438 m = Interpolate::mGetOrInterpolate(q2, p1, p2);
439 }
440 else if(Qp1 == 0) {
441 p = p1;
442 z = Interpolate::zGetOrInterpolate(p1, q1, q2);
443 m = Interpolate::mGetOrInterpolate(p1, q1, q2);
444 }
445 else if(Qp2 == 0) {
446 p = p2;
447 z = Interpolate::zGetOrInterpolate(p2, q1, q2);
448 m = Interpolate::mGetOrInterpolate(p2, q1, q2);
449 }
450 } else {
451 isProperVar = true;
452 p = intersection(p1, p2, q1, q2);
453 z = Interpolate::zInterpolate(p, p1, p2, q1, q2);
454 m = Interpolate::mInterpolate(p, p1, p2, q1, q2);
455 }
456 intPt[0] = geom::CoordinateXYZM(p.x, p.y, z, m);
457 #if GEOS_DEBUG
458 std::cerr << " POINT_INTERSECTION; intPt[0]:" << intPt[0].toString() << std::endl;
459 #endif // GEOS_DEBUG
460 return POINT_INTERSECTION;
461 }
462
463 bool
464 isEndPoint() const
465 {
466 return hasIntersection() && !isProperVar;
467 }
468
469 void computeIntLineIndex();
470
471 void computeIntLineIndex(std::size_t segmentIndex);
472
473 template<typename C1, typename C2>
474 uint8_t computeCollinearIntersection(const C1& p1, const C1& p2, const C2& q1, const C2& q2)
475 {
476 bool q1inP = geom::Envelope::intersects(p1, p2, q1);
477 bool q2inP = geom::Envelope::intersects(p1, p2, q2);
478 bool p1inQ = geom::Envelope::intersects(q1, q2, p1);
479 bool p2inQ = geom::Envelope::intersects(q1, q2, p2);
480
481 if(q1inP && q2inP) {
482 intPt[0] = zmGetOrInterpolateCopy(q1, p1, p2);
483 intPt[1] = zmGetOrInterpolateCopy(q2, p1, p2);
484 return COLLINEAR_INTERSECTION;
485 }
486 if(p1inQ && p2inQ) {
487 intPt[0] = zmGetOrInterpolateCopy(p1, q1, q2);
488 intPt[1] = zmGetOrInterpolateCopy(p2, q1, q2);
489 return COLLINEAR_INTERSECTION;
490 }
491 if(q1inP && p1inQ) {
492 // if pts are equal Z is chosen arbitrarily
493 intPt[0] = zmGetOrInterpolateCopy(q1, p1, p2);
494 intPt[1] = zmGetOrInterpolateCopy(p1, q1, q2);
495
496 return (q1 == p1) && !q2inP && !p2inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION;
497 }
498 if(q1inP && p2inQ) {
499 // if pts are equal Z is chosen arbitrarily
500 intPt[0] = zmGetOrInterpolateCopy(q1, p1, p2);
501 intPt[1] = zmGetOrInterpolateCopy(p2, q1, q2);
502
503 return (q1 == p2) && !q2inP && !p1inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION;
504 }
505 if(q2inP && p1inQ) {
506 // if pts are equal Z is chosen arbitrarily
507 intPt[0] = zmGetOrInterpolateCopy(q2, p1, p2);
508 intPt[1] = zmGetOrInterpolateCopy(p1, q1, q2);
509
510 return (q2 == p1) && !q1inP && !p2inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION;
511 }
512 if(q2inP && p2inQ) {
513 // if pts are equal Z is chosen arbitrarily
514 intPt[0] = zmGetOrInterpolateCopy(q2, p1, p2);
515 intPt[1] = zmGetOrInterpolateCopy(p2, q1, q2);
516 return (q2 == p2) && !q1inP && !p1inQ ? POINT_INTERSECTION : COLLINEAR_INTERSECTION;
517 }
518 return NO_INTERSECTION;
519 }
520
530 template<typename C1, typename C2>
531 geom::CoordinateXYZM intersection (const C1& p1, const C1& p2, const C2& q1, const C2& q2) const {
532 auto intPtOut = intersectionSafe(p1, p2, q1, q2);
533
534 /*
535 * Due to rounding it can happen that the computed intersection is
536 * outside the envelopes of the input segments. Clearly this
537 * is inconsistent.
538 * This code checks this condition and forces a more reasonable answer
539 *
540 * MD - May 4 2005 - This is still a problem. Here is a failure case:
541 *
542 * LINESTRING (2089426.5233462777 1180182.3877339689,
543 * 2085646.6891757075 1195618.7333999649)
544 * LINESTRING (1889281.8148903656 1997547.0560044837,
545 * 2259977.3672235999 483675.17050843034)
546 * int point = (2097408.2633752143,1144595.8008114607)
547 */
548
549 if(! isInSegmentEnvelopes(intPtOut)) {
550 //intPt = CentralEndpointIntersector::getIntersection(p1, p2, q1, q2);
551 intPtOut = nearestEndpoint(p1, p2, q1, q2);
552 }
553
554 if(precisionModel != nullptr) {
555 precisionModel->makePrecise(intPtOut);
556 }
557
558 return intPtOut;
559 }
560
571 bool isInSegmentEnvelopes(const geom::CoordinateXY& pt) const
572 {
573 geom::Envelope env0(*inputLines[0][0], *inputLines[0][1]);
574 geom::Envelope env1(*inputLines[1][0], *inputLines[1][1]);
575 return env0.contains(pt) && env1.contains(pt);
576 };
577
590 template<typename C1, typename C2>
591 geom::CoordinateXYZM intersectionSafe(const C1& p1, const C1& p2,
592 const C2& q1, const C2& q2) const
593 {
594 geom::CoordinateXYZM ptInt(Intersection::intersection(p1, p2, q1, q2));
595 if (ptInt.isNull()) {
596 const geom::CoordinateXY& nearest = nearestEndpoint(p1, p2, q1, q2);
597#if __cplusplus >= 201703L
598 if constexpr (std::is_same<C1, C2>::value) {
599#else
600 if (std::is_same<C1, C2>::value) {
601#endif
602 ptInt = static_cast<const C1&>(nearest);
603 } else {
604 if (&nearest == static_cast<const geom::CoordinateXY*>(&p1) || &nearest == static_cast<const geom::CoordinateXY*>(&p2)) {
605 ptInt = static_cast<const C1&>(nearest);
606 } else {
607 ptInt = static_cast<const C2&>(nearest);
608 }
609 }
610 }
611 return ptInt;
612 }
613
633 static const geom::CoordinateXY& nearestEndpoint(const geom::CoordinateXY& p1,
634 const geom::CoordinateXY& p2,
635 const geom::CoordinateXY& q1,
636 const geom::CoordinateXY& q2);
637
638
639 template<typename C1, typename C2>
640 static geom::CoordinateXYZM zmGetOrInterpolateCopy(
641 const C1& p,
642 const C2& p1,
643 const C2& p2)
644 {
645 geom::CoordinateXYZM pCopy(p);
646 pCopy.z = Interpolate::zGetOrInterpolate(p, p1, p2);
647 pCopy.m = Interpolate::mGetOrInterpolate(p, p1, p2);
648 return pCopy;
649 }
650
651};
652
653
654} // namespace geos::algorithm
655} // namespace geos
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
A LineIntersector is an algorithm that can both test whether two line segments intersect and compute ...
Definition LineIntersector.h:53
intersection_type
Definition LineIntersector.h:137
void computeIntersection(const geom::CoordinateSequence &p, std::size_t p0, const geom::CoordinateSequence &q, std::size_t q0)
Compute the intersection between two segments, given a sequence and starting index of each.
const geom::Coordinate & getIntersectionAlongSegment(std::size_t segmentIndex, std::size_t intIndex)
Computes the intIndex'th intersection point in the direction of a specified input line segment.
void setPrecisionModel(const geom::PrecisionModel *newPM)
Definition LineIntersector.h:132
bool isInteriorIntersection()
Tests whether either intersection point is an interior point of one of the input segments.
Definition LineIntersector.h:96
const geom::CoordinateXYZM & getIntersection(std::size_t intIndex) const
Definition LineIntersector.h:213
static double computeEdgeDistance(const geom::CoordinateXY &p, const geom::CoordinateXY &p0, const geom::CoordinateXY &p1)
bool hasIntersection() const
Definition LineIntersector.h:176
static bool isSameSignAndNonZero(double a, double b)
void computeIntersection(const C1 &p1, const C1 &p2, const C2 &p3, const C2 &p4)
Computes the intersection of the lines p1-p2 and p3-p4.
Definition LineIntersector.h:150
const geom::CoordinateXY * getEndpoint(std::size_t segmentIndex, std::size_t ptIndex) const
Definition LineIntersector.h:190
bool isProper() const
Tests whether an intersection is proper.
Definition LineIntersector.h:259
bool isInteriorIntersection(std::size_t inputLineIndex) const
Tests whether either intersection point is an interior point of the specified input segment.
Definition LineIntersector.h:114
size_t getIntersectionNum() const
Definition LineIntersector.h:200
double getEdgeDistance(std::size_t geomIndex, std::size_t intIndex) const
Computes the "edge distance" of an intersection point along the specified input line segment.
std::size_t getIndexAlongSegment(std::size_t segmentIndex, std::size_t intIndex)
Computes the index of the intIndex'th intersection point in the direction of a specified input line s...
bool isIntersection(const geom::Coordinate &pt) const
Test whether a point is a intersection point of two line segments.
Definition LineIntersector.h:234
void computeIntersection(const geom::CoordinateSequence &p, std::size_t p0, std::size_t p1, const geom::CoordinateSequence &q, std::size_t q0, std::size_t q1)
Compute the intersection between two segments, given a sequence and indices of each endpoint.
The internal representation of a list of coordinates inside a Geometry.
Definition CoordinateSequence.h:56
Coordinate is the lightweight class used to store coordinates.
Definition Coordinate.h:220
Specifies the precision model of the Coordinate in a Geometry.
Definition PrecisionModel.h:87
double makePrecise(double val) const
Rounds a numeric value to the PrecisionModel grid.
Basic namespace for all GEOS functionalities.
Definition geos.h:38