Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add PointLocation.isOnSegment function #1083

Merged
merged 1 commit into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 0 additions & 11 deletions include/geos/algorithm/LineIntersector.h
Original file line number Diff line number Diff line change
Expand Up @@ -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 <code>p</code>.
///
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,
Expand Down
10 changes: 10 additions & 0 deletions include/geos/algorithm/PointLocation.h
Original file line number Diff line number Diff line change
Expand Up @@ -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).
Expand Down
34 changes: 0 additions & 34 deletions src/algorithm/LineIntersector.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
18 changes: 17 additions & 1 deletion src/algorithm/PointLocation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,32 @@
#include <vector>

#include <geos/algorithm/LineIntersector.h>
#include <geos/algorithm/Orientation.h>
#include <geos/algorithm/PointLocation.h>
#include <geos/algorithm/RayCrossingCounter.h>
#include <geos/geom/CoordinateSequence.h>
#include <geos/geom/Coordinate.h>
#include <geos/geom/Envelope.h>
#include <geos/geom/Location.h>
#include <geos/util/IllegalArgumentException.h>

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)
Expand All @@ -42,7 +58,7 @@ PointLocation::isOnLine(const geom::CoordinateXY& p, const geom::CoordinateSeque
const geom::CoordinateXY* pp = &(pt->getAt<geom::CoordinateXY>(0));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe change this to a reference so you dispense with the * further down,

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried that but was getting compile errors. Does it need to be a pointer because it gets revalued at the end of the loop?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Another option is to simply define references for both points each time through the loop.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yeah, const references hate being redefined. 🤷 Nm.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to d4b654a

for(std::size_t i = 1; i < ptsize; ++i) {
const geom::CoordinateXY& p1 = pt->getAt<geom::CoordinateXY>(i);
if(LineIntersector::hasIntersection(p, *pp, p1)) {
if(isOnSegment(p, *pp, p1)) {
return true;
}
pp = &p1;
Expand Down
3 changes: 1 addition & 2 deletions src/operation/valid/PolygonTopologyAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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<CoordinateXY>(i), ringPts->getAt<CoordinateXY>(i+1));
if (li.hasIntersection()) {
if ( algorithm::PointLocation::isOnSegment(*pt, ringPts->getAt<CoordinateXY>(i), ringPts->getAt<CoordinateXY>(i+1)) ) {
//-- check if pt is the start point of the next segment
if (pt->equals2D(ringPts->getAt<CoordinateXY>(i + 1))) {
return i + 1;
Expand Down
129 changes: 129 additions & 0 deletions tests/unit/algorithm/PointLocationTest.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
//
// Test Suite for geos::algorithm::PointLocation
// Ported from JTS junit/algorithm/PointLocationTest.java

#include <tut/tut.hpp>
#include <utility.h>

// geos
#include <geos/geom/Coordinate.h>
#include <geos/algorithm/PointLocation.h>

// std
#include <sstream>
#include <string>
#include <memory>

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<CoordinateSequence> 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<CoordinateSequence> line = readPts(wktLine);

bool isOnSeg = PointLocation::isOnSegment(p, line->getAt(0), line->getAt(1));
ensure(isOnSeg == isExpected);
}

std::unique_ptr<CoordinateSequence>
readPts(const std::string& wkt)
{
std::unique_ptr<Geometry> geom = r_.read(wkt);
const LineString* line = dynamic_cast<LineString*>(geom.get());
if (line)
return line->getCoordinatesRO()->clone();
else
return nullptr;
}

};


typedef test_group<test_PointLocation_data> 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

Loading