diff --git a/src/main/java/org/openstreetmap/atlas/geography/Segment.java b/src/main/java/org/openstreetmap/atlas/geography/Segment.java index ae9d63c13b..08fbf1d9bf 100644 --- a/src/main/java/org/openstreetmap/atlas/geography/Segment.java +++ b/src/main/java/org/openstreetmap/atlas/geography/Segment.java @@ -107,7 +107,7 @@ public Optional heading() * @return The intersection point if any, null otherwise * @see "http://stackoverflow.com/a/1968345/1558687" */ - public Location intersection(final Segment that) + private Location intersectionUsingDouble(final Segment that) { final double p0X = this.start().getLongitude().asDegrees(); final double p0Y = this.start().getLatitude().asDegrees(); @@ -129,8 +129,9 @@ public Location intersection(final Segment that) final double sValue; final double tValue; - sValue = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / (-s2X * s1Y + s1X * s2Y); - tValue = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / (-s2X * s1Y + s1X * s2Y); + final double commonDenominator = -s2X * s1Y + s1X * s2Y; + sValue = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / commonDenominator; + tValue = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / commonDenominator; if (sValue >= 0 && sValue <= 1 && tValue >= 0 && tValue <= 1) { @@ -138,6 +139,45 @@ public Location intersection(final Segment that) return new Location(Latitude.degrees(p0Y + tValue * s1Y), Longitude.degrees(p0X + tValue * s1X)); } + return null; + } + + /** + * Implements the same function as intersection, but with long to get the intersection at end of two segments + * + * @param that + * The segment to intersect + * @return The intersection point if any, null otherwise + */ + public Location intersection(final Segment that) + { + final long p0X = this.start().getLongitude().asDm7(); + final long p0Y = this.start().getLatitude().asDm7(); + final long p1X = this.end().getLongitude().asDm7(); + final long p1Y = this.end().getLatitude().asDm7(); + final long p2X = that.start().getLongitude().asDm7(); + final long p2Y = that.start().getLatitude().asDm7(); + final long p3X = that.end().getLongitude().asDm7(); + final long p3Y = that.end().getLatitude().asDm7(); + final long s1X = p1X - p0X; + final long s1Y = p1Y - p0Y; + final long s2X = p3X - p2X; + final long s2Y = p3Y - p2Y; + final long commonDenominator = -s2X * s1Y + s1X * s2Y; + final long sValue = (-s1Y * (p0X - p2X) + s1X * (p0Y - p2Y)) / commonDenominator; + final long tValue = (s2X * (p0Y - p2Y) - s2Y * (p0X - p2X)) / commonDenominator; + // Checks for intersection + if (sValue >= 0 && sValue <= 1 && tValue >= 0 && tValue <= 1) + { + // Intersections that occur at points other than end points are more accurate using + // double and degrees calculations + final Location intersectionLocationUsingDouble = this.intersectionUsingDouble(that); + // Collision detected. If intersection calculated using double is null,intersection is at end point and so, + // return the location calculated using long and dm7. + return intersectionLocationUsingDouble != null? intersectionLocationUsingDouble : + new Location(Latitude.dm7(p0Y + tValue * s1Y), + Longitude.dm7(p0X + tValue * s1X)); + } // No collision return null; } diff --git a/src/test/java/org/openstreetmap/atlas/geography/SegmentTest.java b/src/test/java/org/openstreetmap/atlas/geography/SegmentTest.java index ccced5047d..b3b77db3dd 100644 --- a/src/test/java/org/openstreetmap/atlas/geography/SegmentTest.java +++ b/src/test/java/org/openstreetmap/atlas/geography/SegmentTest.java @@ -14,6 +14,8 @@ public class SegmentTest .wkt("LINESTRING (112.9699474 -84.7999528, 112.9699948 -84.7999669)").segments().get(0); private static Segment SEGMENT_SAME_END_2 = PolyLine .wkt("LINESTRING (112.9650809 -84.7999622, 112.9699948 -84.7999669)").segments().get(0); + private static final Location INTERSECTION_LOCATION = Location + .forString("-84.7999669,112.9699948"); @Test public void testAntimeridianNoIntersection() @@ -33,17 +35,16 @@ public void testAntimeridianNoIntersection() @Test public void testIntersection() { - Assert.assertEquals(null, new Segment(Location.TEST_1, Location.TEST_2) + Assert.assertNull(new Segment(Location.TEST_1, Location.TEST_2) .intersection(new Segment(Location.TEST_4, Location.TEST_3))); - Assert.assertEquals(false, new Segment(Location.TEST_1, Location.TEST_2) + Assert.assertFalse(new Segment(Location.TEST_1, Location.TEST_2) .intersects(new Segment(Location.TEST_4, Location.TEST_3))); - + Assert.assertTrue(new Segment(Location.TEST_1, Location.TEST_3) + .intersects(new Segment(Location.TEST_4, Location.TEST_2))); Assert.assertEquals( new Location(Latitude.degrees(37.3273389), Longitude.degrees(-122.0287109)), - new Segment(Location.TEST_1, Location.TEST_3) + new Segment(new Location(Latitude.degrees(37.3273389), Longitude.degrees(-122.0287109)), Location.TEST_3) .intersection(new Segment(Location.TEST_4, Location.TEST_2))); - Assert.assertEquals(true, new Segment(Location.TEST_1, Location.TEST_3) - .intersects(new Segment(Location.TEST_4, Location.TEST_2))); } @Test @@ -58,7 +59,8 @@ public void testIntersectionOverflow() Assert.assertTrue("Overflow issue", maxXandYMovement.intersects(maxXandNegativeYMovement)); Assert.assertTrue("Overflow issue", maxXandNegativeYMovement.intersects(maxXandYMovement)); - + Assert.assertNull(maxXandYMovement.intersection(maxXandNegativeYMovement)); + Assert.assertNull(maxXandNegativeYMovement.intersection(maxXandYMovement)); } @Test @@ -70,5 +72,13 @@ public void testIntersectionWithSameEnd() SEGMENT_SAME_END_2.reversed().intersects(SEGMENT_SAME_END_1.reversed())); Assert.assertTrue("Same End is broken", SEGMENT_SAME_END_1.intersects(SEGMENT_SAME_END_2)); Assert.assertTrue("Same End is broken", SEGMENT_SAME_END_2.intersects(SEGMENT_SAME_END_1)); + Assert.assertEquals(INTERSECTION_LOCATION, + SEGMENT_SAME_END_1.reversed().intersection(SEGMENT_SAME_END_2.reversed())); + Assert.assertEquals(INTERSECTION_LOCATION, + SEGMENT_SAME_END_2.reversed().intersection(SEGMENT_SAME_END_1.reversed())); + Assert.assertEquals(INTERSECTION_LOCATION, + SEGMENT_SAME_END_1.intersection(SEGMENT_SAME_END_2)); + Assert.assertEquals(INTERSECTION_LOCATION, + SEGMENT_SAME_END_2.intersection(SEGMENT_SAME_END_1)); } }