From f63d5d3f7931396559040b8871046c5866374341 Mon Sep 17 00:00:00 2001 From: Martin Davis Date: Mon, 22 Apr 2024 22:36:51 -0700 Subject: [PATCH] Add PointLocation.isOnSegment, refactor --- include/geos/algorithm/LineIntersector.h | 11 -- include/geos/algorithm/PointLocation.h | 10 ++ src/algorithm/LineIntersector.cpp | 34 ----- src/algorithm/PointLocation.cpp | 18 ++- .../valid/PolygonTopologyAnalyzer.cpp | 3 +- tests/unit/algorithm/PointLocationTest.cpp | 129 ++++++++++++++++++ 6 files changed, 157 insertions(+), 48 deletions(-) create mode 100644 tests/unit/algorithm/PointLocationTest.cpp diff --git a/include/geos/algorithm/LineIntersector.h b/include/geos/algorithm/LineIntersector.h index e4a5c010db..958dbbf128 100644 --- a/include/geos/algorithm/LineIntersector.h +++ b/include/geos/algorithm/LineIntersector.h @@ -134,17 +134,6 @@ class GEOS_DLL LineIntersector { precisionModel = newPM; } - /// Compute the intersection of a point p and the line p1-p2. - /// - /// This function computes the boolean value of the hasIntersection test. - /// The actual value of the intersection (if there is one) - /// is equal to the value of p. - /// - void computeIntersection(const geom::CoordinateXY& p, const geom::CoordinateXY& p1, const geom::CoordinateXY& p2); - - /// Same as above but doesn't compute intersection point. Faster. - static bool hasIntersection(const geom::CoordinateXY& p, const geom::CoordinateXY& p1, const geom::CoordinateXY& p2); - enum intersection_type : uint8_t { /// Indicates that line segments do not intersect NO_INTERSECTION = 0, diff --git a/include/geos/algorithm/PointLocation.h b/include/geos/algorithm/PointLocation.h index b68010df65..e7c7f6acf7 100644 --- a/include/geos/algorithm/PointLocation.h +++ b/include/geos/algorithm/PointLocation.h @@ -36,6 +36,16 @@ namespace algorithm { // geos::algorithm class GEOS_DLL PointLocation { public: + /** \brief + * Tests whether a point lies on a line segment. + * + * @param p the point to test + * @param p0 a point of the line segment + * @param p1 a point of the line segment + * @return true if the point lies on the line segment + */ + static bool isOnSegment(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1); + /** \brief * Tests whether a point lies on the line defined by a * [CoordinateSequence](@ref geom::CoordinateSequence). diff --git a/src/algorithm/LineIntersector.cpp b/src/algorithm/LineIntersector.cpp index 61ae787d5e..b3901b9b3d 100644 --- a/src/algorithm/LineIntersector.cpp +++ b/src/algorithm/LineIntersector.cpp @@ -212,40 +212,6 @@ LineIntersector::computeIntersection(const CoordinateSequence& p, std::size_t p0 CoordinateSequences::binaryDispatch(p, q, dis); } -/*public*/ -void -LineIntersector::computeIntersection(const CoordinateXY& p, const CoordinateXY& p1, const CoordinateXY& p2) -{ - isProperVar = false; - - // do between check first, since it is faster than the orientation test - if(Envelope::intersects(p1, p2, p)) { - if((Orientation::index(p1, p2, p) == 0) && - (Orientation::index(p2, p1, p) == 0)) { - isProperVar = true; - if((p == p1) || (p == p2)) { // 2d only test - isProperVar = false; - } - result = POINT_INTERSECTION; - return; - } - } - result = NO_INTERSECTION; -} - -/* public static */ -bool -LineIntersector::hasIntersection(const CoordinateXY& p, const CoordinateXY& p1, const CoordinateXY& p2) -{ - if(Envelope::intersects(p1, p2, p)) { - if((Orientation::index(p1, p2, p) == 0) && - (Orientation::index(p2, p1, p) == 0)) { - return true; - } - } - return false; -} - /* private static */ const CoordinateXY& LineIntersector::nearestEndpoint(const CoordinateXY& p1, const CoordinateXY& p2, diff --git a/src/algorithm/PointLocation.cpp b/src/algorithm/PointLocation.cpp index 5ceb6bd53e..5ad430045a 100644 --- a/src/algorithm/PointLocation.cpp +++ b/src/algorithm/PointLocation.cpp @@ -20,16 +20,32 @@ #include #include +#include #include #include #include #include +#include #include #include namespace geos { namespace algorithm { // geos.algorithm +/* public static */ +bool +PointLocation::isOnSegment(const geom::CoordinateXY& p, const geom::CoordinateXY& p0, const geom::CoordinateXY& p1) +{ + //-- test envelope first since it's faster + if (! geom::Envelope::intersects(p0, p1, p)) + return false; + //-- handle zero-length segments + if (p.equals2D(p0)) + return true; + bool isOnLine = Orientation::COLLINEAR == Orientation::index(p0, p1, p); + return isOnLine; +} + /* public static */ bool PointLocation::isOnLine(const geom::CoordinateXY& p, const geom::CoordinateSequence* pt) @@ -42,7 +58,7 @@ PointLocation::isOnLine(const geom::CoordinateXY& p, const geom::CoordinateSeque const geom::CoordinateXY* pp = &(pt->getAt(0)); for(std::size_t i = 1; i < ptsize; ++i) { const geom::CoordinateXY& p1 = pt->getAt(i); - if(LineIntersector::hasIntersection(p, *pp, p1)) { + if(isOnSegment(p, *pp, p1)) { return true; } pp = &p1; diff --git a/src/operation/valid/PolygonTopologyAnalyzer.cpp b/src/operation/valid/PolygonTopologyAnalyzer.cpp index 39b672b1d8..c2b9d0a390 100644 --- a/src/operation/valid/PolygonTopologyAnalyzer.cpp +++ b/src/operation/valid/PolygonTopologyAnalyzer.cpp @@ -180,8 +180,7 @@ PolygonTopologyAnalyzer::intersectingSegIndex(const CoordinateSequence* ringPts, { algorithm::LineIntersector li; for (std::size_t i = 0; i < ringPts->size() - 1; i++) { - li.computeIntersection(*pt, ringPts->getAt(i), ringPts->getAt(i+1)); - if (li.hasIntersection()) { + if ( algorithm::PointLocation::isOnSegment(*pt, ringPts->getAt(i), ringPts->getAt(i+1)) ) { //-- check if pt is the start point of the next segment if (pt->equals2D(ringPts->getAt(i + 1))) { return i + 1; diff --git a/tests/unit/algorithm/PointLocationTest.cpp b/tests/unit/algorithm/PointLocationTest.cpp new file mode 100644 index 0000000000..bed1a1b0bc --- /dev/null +++ b/tests/unit/algorithm/PointLocationTest.cpp @@ -0,0 +1,129 @@ +// +// Test Suite for geos::algorithm::PointLocation +// Ported from JTS junit/algorithm/PointLocationTest.java + +#include +#include + +// geos +#include +#include + +// std +#include +#include +#include + +using geos::algorithm::PointLocation; +using geos::geom::CoordinateXY; + +namespace tut { +// +// Test Group +// + +// dummy data, not used +struct test_PointLocation_data { + + geos::io::WKTReader r_; + + void + checkOnLine(double x, double y, const std::string& wktLine, bool isExpected) + { + CoordinateXY p(x, y); + std::unique_ptr line = readPts(wktLine); + bool isOnLine = PointLocation::isOnLine(p, line.get()); + ensure(isOnLine == isExpected); + } + + void + checkOnSegment(double x, double y, const std::string& wktLine, bool isExpected) + { + CoordinateXY p(x, y); + std::unique_ptr line = readPts(wktLine); + + bool isOnSeg = PointLocation::isOnSegment(p, line->getAt(0), line->getAt(1)); + ensure(isOnSeg == isExpected); + } + + std::unique_ptr + readPts(const std::string& wkt) + { + std::unique_ptr geom = r_.read(wkt); + const LineString* line = dynamic_cast(geom.get()); + if (line) + return line->getCoordinatesRO()->clone(); + else + return nullptr; + } + +}; + + +typedef test_group group; +typedef group::object object; + +group test_PointLocation_data("geos::algorithm::PointLocation"); + + +// +// Test Cases +// + +// testOnLineOnVertex +template<> +template<> +void object::test<1> () +{ + checkOnLine(20, 20, "LINESTRING (0 00, 20 20, 30 30)", true); +} + +// testOnLineInSegment +template<> +template<> +void object::test<2> () +{ + checkOnLine(10, 10, "LINESTRING (0 0, 20 20, 0 40)", true); + checkOnLine(10, 30, "LINESTRING (0 0, 20 20, 0 40)", true); +} + +// testNotOnLine +template<> +template<> +void object::test<3> () +{ + checkOnLine(0, 100, "LINESTRING (10 10, 20 10, 30 10)", false); +} + +// testOnSegment +template<> +template<> +void object::test<4> () +{ + checkOnSegment(5, 5, "LINESTRING(0 0, 9 9)", true); + checkOnSegment(0, 0, "LINESTRING(0 0, 9 9)", true); + checkOnSegment(9, 9, "LINESTRING(0 0, 9 9)", true); +} + +// testNotOnSegment +template<> +template<> +void object::test<5> () +{ + checkOnSegment(5, 6, "LINESTRING(0 0, 9 9)", false); + checkOnSegment(10, 10, "LINESTRING(0 0, 9 9)", false); + checkOnSegment(9, 9.00001, "LINESTRING(0 0, 9 9)", false); +} + +// testOnZeroLengthSegment +template<> +template<> +void object::test<6> () +{ + checkOnSegment(1, 1, "LINESTRING(1 1, 1 1)", true); + checkOnSegment(1, 2, "LINESTRING(1 1, 1 1)", false); +} + + +} // namespace tut +