/*
* Copyright (c) 2016 Vivid Solutions.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Eclipse Distribution License v. 1.0 which accompanies this distribution.
* The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html
* and the Eclipse Distribution License is available at
*
* http://www.eclipse.org/org/documents/edl-v10.php.
*/
package org.locationtech.jts.algorithm;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.geom.LineString;
import org.locationtech.jts.geom.PrecisionModel;
import org.locationtech.jts.io.ParseException;
import org.locationtech.jts.io.WKTReader;
import org.locationtech.jts.io.WKTWriter;
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
{
private WKTReader reader = new WKTReader();
public static void main(String args[]) {
TestRunner.run(RobustLineIntersectionTest.class);
}
public RobustLineIntersectionTest(String name) {
super(name);
}
/**
* 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);
}
/**
* Tests a case where intersection point is rounded,
* and it is computed as a nearest endpoint.
* Exposed a bug due to aliasing of endpoint.
*
* MD 8 Mar 2013
*
* @throws ParseException
*/
public void testRoundedPointsNotAltered()
throws ParseException
{
checkInputNotAltered(
"LINESTRING (-58.00593335955 -1.43739086465, -513.86101637525 -457.29247388035)",
"LINESTRING (-215.22279674875 -158.65425425385, -218.1208801283 -160.68343590235)",
100000 );
}
/**
* 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)");
}
/**
* 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.0434123494 254176.46578338774)",
0);
}
/**
* 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);
}
/**
* This used to be a failure case (exception), but apparently works now.
* Possibly normalization has fixed this?
*
* @throws ParseException
*/
public void testDaveSkeaCase()
throws ParseException
{
checkIntersection(
"LINESTRING ( 2089426.5233462777 1180182.3877339689, 2085646.6891757075 1195618.7333999649 )",
"LINESTRING ( 1889281.8148903656 1997547.0560044837, 2259977.3672235999 483675.17050843034 )",
1,
new Coordinate[] {
new Coordinate(2087536.6062609926, 1187900.560566967),
}, 0);
}
/**
* 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,
new Coordinate[] {
new Coordinate(4348440.8493874, 5552599.27202212),
},
0);
}
/**
* Result of this test should be the same as the WKT one!
* @throws ParseException
*/
public void testCmp5CaseRaw()
throws ParseException
{
checkIntersection(
new Coordinate[] {
new Coordinate(4348433.262114629, 5552595.478385733),
new Coordinate(4348440.849387404, 5552599.272022122),
new Coordinate(4348433.26211463, 5552595.47838573),
new Coordinate(4348440.8493874, 5552599.27202212)
}, 1,
new Coordinate[] {
new Coordinate(4348440.8493874, 5552599.27202212),
},
0);
}
void checkIntersectionNone(String wkt1, String wkt2)
throws ParseException
{
LineString l1 = (LineString) reader.read(wkt1);
LineString l2 = (LineString) reader.read(wkt2);
Coordinate[] pt = new Coordinate[] {
l1.getCoordinateN(0), l1.getCoordinateN(1),
l2.getCoordinateN(0), l2.getCoordinateN(1)
};
checkIntersection(pt, 0, null, 0);
}
void checkIntersection(String wkt1, String wkt2,
int expectedIntersectionNum,
Coordinate[] intPt,
double distanceTolerance)
throws ParseException
{
LineString l1 = (LineString) reader.read(wkt1);
LineString l2 = (LineString) reader.read(wkt2);
Coordinate[] pt = new Coordinate[] {
l1.getCoordinateN(0), l1.getCoordinateN(1),
l2.getCoordinateN(0), l2.getCoordinateN(1)
};
checkIntersection(pt, expectedIntersectionNum, intPt, distanceTolerance);
}
void checkIntersection(String wkt1, String wkt2,
int expectedIntersectionNum,
String expectedWKT,
double distanceTolerance)
throws ParseException
{
LineString l1 = (LineString) reader.read(wkt1);
LineString l2 = (LineString) reader.read(wkt2);
Coordinate[] pt = new Coordinate[] {
l1.getCoordinateN(0), l1.getCoordinateN(1),
l2.getCoordinateN(0), l2.getCoordinateN(1)
};
Geometry g = reader.read(expectedWKT);
Coordinate[] intPt = g.getCoordinates();
checkIntersection(pt, expectedIntersectionNum, intPt, distanceTolerance);
}
/**
* 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 expectedIntPt the expected intersection points (maybe null if not tested)
* @param distanceTolerance tolerance to use for equality test
*/
void checkIntersection(Coordinate[] pt,
int expectedIntersectionNum,
Coordinate[] expectedIntPt,
double distanceTolerance)
{
LineIntersector li = new RobustLineIntersector();
li.computeIntersection(pt[0], pt[1], pt[2], pt[3]);
int intNum = li.getIntersectionNum();
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
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 checkIntPoints(Coordinate expectedPt, Coordinate actualPt, double distanceTolerance)
{
boolean isEqual = equals(expectedPt, actualPt, distanceTolerance);
assertTrue("Int Pts not equal - "
+ "expected " + WKTWriter.toPoint(expectedPt) + " VS "
+ "actual " + WKTWriter.toPoint(actualPt), isEqual);
}
public static boolean equals(Coordinate p0, Coordinate p1, double distanceTolerance)
{
return p0.distance(p1) <= distanceTolerance;
}
void checkInputNotAltered(String wkt1, String wkt2, int scaleFactor) throws ParseException
{
LineString l1 = (LineString) reader.read(wkt1);
LineString l2 = (LineString) reader.read(wkt2);
Coordinate[] pt = new Coordinate[] { l1.getCoordinateN(0),
l1.getCoordinateN(1), l2.getCoordinateN(0), l2.getCoordinateN(1) };
checkInputNotAltered(pt, scaleFactor);
}
public void checkInputNotAltered(Coordinate[] pt, int scaleFactor)
{
// save input points
Coordinate[] savePt = new Coordinate[4];
for (int i = 0; i < 4; i++) {
savePt[i] = new Coordinate(pt[i]);
}
LineIntersector li = new RobustLineIntersector();
li.setPrecisionModel(new PrecisionModel(scaleFactor));
li.computeIntersection(pt[0], pt[1], pt[2], pt[3]);
// check that input points are unchanged
for (int i = 0; i < 4; i++) {
assertEquals("Input point " + i + " was altered - ", savePt[i], pt[i]);
}
}
}