package com.revolsys.geometry.test.old.algorithm; import com.revolsys.geometry.algorithm.LineIntersector; import com.revolsys.geometry.algorithm.RobustLineIntersector; import com.revolsys.geometry.model.Geometry; import com.revolsys.geometry.model.GeometryFactory; import com.revolsys.geometry.model.LineString; import com.revolsys.geometry.model.Point; import com.revolsys.geometry.model.coordinates.list.CoordinatesListUtil; import com.revolsys.geometry.model.impl.PointDoubleXY; import com.revolsys.geometry.wkb.ParseException; import junit.framework.TestCase; import junit.textui.TestRunner; /** * Tests robustness and correctness of RobustLineIntersector * in some tricky cases. * Failure modes can include exceptions thrown, or incorrect * results returned. * * @author Owner * */ public class RobustLineIntersectionTest extends TestCase { public static boolean equals(final Point p0, final Point p1, final double distanceTolerance) { return p0.distancePoint(p1) <= distanceTolerance; } public static void main(final String args[]) { TestRunner.run(RobustLineIntersectionTest.class); } private final GeometryFactory geometryFactory = GeometryFactory.DEFAULT_3D; private final GeometryFactory geometryFactory2d = GeometryFactory.DEFAULT_2D; public RobustLineIntersectionTest(final String name) { super(name); } /** * Check that intersection of segment defined by points in pt array * is equal to the expectedIntPt value (up to the given distanceTolerance). * * @param pt * @param expectedIntersectionNum * @param distanceTolerance tolerance to use for equality test * @param expectedIntPt the expected intersection points (maybe null if not tested) */ void checkIntersection(final Point[] pt, final int expectedIntersectionNum, final double distanceTolerance, final Point... expectedIntPt) { final LineIntersector li = new RobustLineIntersector(); li.computeIntersectionPoints(pt[0], pt[1], pt[2], pt[3]); final int intNum = li.getIntersectionCount(); assertEquals("Number of intersections not as expected", expectedIntersectionNum, intNum); if (expectedIntPt != null) { assertEquals("Wrong number of expected int pts provided", intNum, expectedIntPt.length); // test that both points are represented here final boolean isIntPointsCorrect = true; if (intNum == 1) { checkIntPoints(expectedIntPt[0], li.getIntersection(0), distanceTolerance); } else if (intNum == 2) { checkIntPoints(expectedIntPt[1], li.getIntersection(0), distanceTolerance); checkIntPoints(expectedIntPt[1], li.getIntersection(0), distanceTolerance); if (!(equals(expectedIntPt[0], li.getIntersection(0), distanceTolerance) || equals(expectedIntPt[0], li.getIntersection(1), distanceTolerance))) { checkIntPoints(expectedIntPt[0], li.getIntersection(0), distanceTolerance); checkIntPoints(expectedIntPt[0], li.getIntersection(1), distanceTolerance); } else if (!(equals(expectedIntPt[1], li.getIntersection(0), distanceTolerance) || equals(expectedIntPt[1], li.getIntersection(1), distanceTolerance))) { checkIntPoints(expectedIntPt[1], li.getIntersection(0), distanceTolerance); checkIntPoints(expectedIntPt[1], li.getIntersection(1), distanceTolerance); } } } } void checkIntersection(final String wkt1, final String wkt2, final int expectedIntersectionNum, final double distanceTolerance, final Point... intPt) throws ParseException { final LineString l1 = (LineString)this.geometryFactory.geometry(wkt1); final LineString l2 = (LineString)this.geometryFactory.geometry(wkt2); final Point[] pt = new Point[] { l1.getPoint(0), l1.getPoint(1), l2.getPoint(0), l2.getPoint(1) }; checkIntersection(pt, expectedIntersectionNum, distanceTolerance, intPt); } void checkIntersection(final String wkt1, final String wkt2, final int expectedIntersectionNum, final String expectedWKT, final double distanceTolerance) throws ParseException { final LineString l1 = (LineString)this.geometryFactory.geometry(wkt1); final LineString l2 = (LineString)this.geometryFactory.geometry(wkt2); final Point[] pt = new Point[] { l1.getPoint(0), l1.getPoint(1), l2.getPoint(0), l2.getPoint(1) }; final Geometry g = this.geometryFactory2d.geometry(expectedWKT); final Point[] intPt = CoordinatesListUtil.getPointArray(g); checkIntersection(pt, expectedIntersectionNum, distanceTolerance, intPt); } void checkIntersectionNone(final String wkt1, final String wkt2) throws ParseException { final LineString l1 = (LineString)this.geometryFactory.geometry(wkt1); final LineString l2 = (LineString)this.geometryFactory.geometry(wkt2); final Point[] pt = new Point[] { l1.getPoint(0), l1.getPoint(1), l2.getPoint(0), l2.getPoint(1) }; checkIntersection(pt, 0, 0, null); } void checkIntPoints(final Point expectedPt, final Point actualPt, final double distanceTolerance) { final boolean isEqual = equals(expectedPt, actualPt, distanceTolerance); assertTrue("Int Pts not equal - " + "expected " + expectedPt + " VS " + "actual " + actualPt, isEqual); } public void test01TouchFromFrom() throws ParseException { checkIntersection("LINESTRING (2 2,0 0)", "LINESTRING (2 2,4 4)", 1, 0, new PointDoubleXY(2, 2)); } public void test02TouchFromTo() throws ParseException { checkIntersection("LINESTRING (2 2,0 0)", "LINESTRING (4 4,2 2)", 1, 0, new PointDoubleXY(2, 2)); } public void test03TouchToFrom() throws ParseException { checkIntersection("LINESTRING (0 0,2 2)", "LINESTRING (2 2,4 4)", 1, 0, new PointDoubleXY(2, 2)); } public void test04TouchToTo() throws ParseException { checkIntersection("LINESTRING (0 0,2 2)", "LINESTRING (4 4,2 2)", 1, 0, new PointDoubleXY(2, 2)); } public void test10Crossing() throws ParseException { checkIntersection("LINESTRING (0 0,2 2)", "LINESTRING (0 2,2 0)", 1, 0, new PointDoubleXY(1, 1)); } /** * Following cases were failures when using the CentralEndpointIntersector heuristic. * This is because one segment lies at a significant angle to the other, * with only one endpoint is close to the other segment. * The CE heuristic chose the wrong endpoint to return. * The fix is to use a new heuristic which out of the 4 endpoints * chooses the one which is closest to the other segment. * This works in all known failure cases. * * @throws ParseException */ public void testCentralEndpointHeuristicFailure() throws ParseException { checkIntersection("LINESTRING(163.81867067 -211.31840378,165.9174252 -214.1665075)", "LINESTRING(2.84139601 -57.95412726,469.59990601 -502.63851732)", 1, "POINT(163.81867067 -211.31840378)", 0); } public void testCentralEndpointHeuristicFailure2() throws ParseException { checkIntersection( "LINESTRING(-58.00593335955 -1.43739086465,-513.86101637525 -457.29247388035)", "LINESTRING(-215.22279674875 -158.65425425385,-218.1208801283 -160.68343590235)", 1, "POINT(-215.22279674875 -158.65425425385)", 0); } /** * Result of this test should be the same as the WKT one! * @throws ParseException */ public void testCmp5CaseRaw() throws ParseException { checkIntersection(new Point[] { new PointDoubleXY(4348433.262114629, 5552595.478385733), new PointDoubleXY(4348440.849387404, 5552599.272022122), new PointDoubleXY(4348433.26211463, 5552595.47838573), new PointDoubleXY(4348440.8493874, 5552599.27202212) }, 1, 0, new Point[] { new PointDoubleXY(4348440.8493874, 5552599.27202212), }); } /** * Outside envelope using HCoordinate method. * * @throws ParseException */ public void testCmp5CaseWKT() throws ParseException { checkIntersection( "LINESTRING(4348433.262114629 5552595.478385733,4348440.849387404 5552599.272022122)", "LINESTRING(4348433.26211463 5552595.47838573,4348440.8493874 5552599.27202212)", 1, 0, new Point[] { new PointDoubleXY(4348440.8493874, 5552599.27202212), }); } /** * Test from strk which is bad in GEOS (2009-04-14). * * @throws ParseException */ public void testGEOS_1() throws ParseException { checkIntersection( "LINESTRING (588750.7429703881 4518950.493668233, 588748.2060409798 4518933.9452804085)", "LINESTRING (588745.824857241 4518940.742239175, 588748.2060437313 4518933.9452791475)", 1, "POINT (588748.2060416829 4518933.945284994)", 0); } /** * Test from strk which is bad in GEOS (2009-04-14). * * @throws ParseException */ public void testGEOS_2() throws ParseException { checkIntersection( "LINESTRING (588743.626135934 4518924.610969561, 588732.2822865889 4518925.4314047815)", "LINESTRING (588739.1191384895 4518927.235700594, 588731.7854614238 4518924.578370095)", 1, "POINT (588733.8306132929 4518925.319423238)", 0); } /** * Test involving two non-almost-parallel lines. * Does not seem to cause problems with basic line intersection algorithm. * * @throws ParseException */ public void testLeduc_1() throws ParseException { checkIntersection( "LINESTRING(305690.0434123494 254176.46578338774,305601.9999843455 254243.19999846347)", "LINESTRING(305689.6153764265 254177.33102743194,305692.4999844298 254171.4999983967)", 1, "POINT(305690.04341234936 254176.4657833878)", 0); } /** * Test from Tomas Fa - JTS list 6/13/2012 * * Fails using original JTS DeVillers determine orientation test. * Succeeds using DD and Shewchuk orientation * * @throws ParseException */ public void testTomasFa_1() throws ParseException { checkIntersectionNone("LINESTRING (-42.0 163.2, 21.2 265.2)", "LINESTRING (-26.2 188.7, 37.0 290.7)"); } /** * Test from Tomas Fa - JTS list 6/13/2012 * * Fails using original JTS DeVillers determine orientation test. * Succeeds using DD and Shewchuk orientation * * @throws ParseException */ public void testTomasFa_2() throws ParseException { checkIntersectionNone("LINESTRING (-5.9 163.1, 76.1 250.7)", "LINESTRING (14.6 185.0, 96.6 272.6)"); } }