diff --git a/library/src/main/java/com/google/maps/android/PolyUtil.java b/library/src/main/java/com/google/maps/android/PolyUtil.java index 2f8413bc7..1c32d5a38 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -22,9 +22,25 @@ import java.util.List; import java.util.Stack; -import static com.google.maps.android.SphericalUtil.*; -import static java.lang.Math.*; -import static com.google.maps.android.MathUtil.*; +import static com.google.maps.android.MathUtil.EARTH_RADIUS; +import static com.google.maps.android.MathUtil.clamp; +import static com.google.maps.android.MathUtil.hav; +import static com.google.maps.android.MathUtil.havDistance; +import static com.google.maps.android.MathUtil.havFromSin; +import static com.google.maps.android.MathUtil.inverseMercator; +import static com.google.maps.android.MathUtil.mercator; +import static com.google.maps.android.MathUtil.sinFromHav; +import static com.google.maps.android.MathUtil.sinSumFromHav; +import static com.google.maps.android.MathUtil.wrap; +import static com.google.maps.android.SphericalUtil.computeDistanceBetween; +import static java.lang.Math.PI; +import static java.lang.Math.cos; +import static java.lang.Math.max; +import static java.lang.Math.min; +import static java.lang.Math.sin; +import static java.lang.Math.sqrt; +import static java.lang.Math.tan; +import static java.lang.Math.toRadians; public class PolyUtil { @@ -466,6 +482,7 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La return computeDistanceBetween(end, p); } + // Implementation of http://paulbourke.net/geometry/pointlineplane/ or http://geomalgorithms.com/a02-_lines.html final double s0lat = toRadians(p.latitude); final double s0lng = toRadians(p.longitude); final double s1lat = toRadians(start.latitude); @@ -473,9 +490,10 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La final double s2lat = toRadians(end.latitude); final double s2lng = toRadians(end.longitude); + double lonCorrection = Math.cos(s1lat); double s2s1lat = s2lat - s1lat; - double s2s1lng = s2lng - s1lng; - final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng) + double s2s1lng = (s2lng - s1lng) * lonCorrection; + final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * lonCorrection * s2s1lng) / (s2s1lat * s2s1lat + s2s1lng * s2s1lng); if (u <= 0) { return computeDistanceBetween(p, start); diff --git a/library/src/test/java/com/google/maps/android/PolyUtilTest.java b/library/src/test/java/com/google/maps/android/PolyUtilTest.java index c278887d6..cff8c538e 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -23,7 +23,10 @@ import java.util.ArrayList; import java.util.List; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertFalse; +import static org.junit.Assert.assertSame; +import static org.junit.Assert.assertTrue; public class PolyUtilTest { private static final String TEST_LINE = @@ -469,6 +472,17 @@ public void testIsClosedPolygon() { assertTrue(PolyUtil.isClosedPolygon(poly)); } + /** + * The following method checks whether {@link PolyUtil#distanceToLine(LatLng, LatLng, LatLng) distanceToLine()} } + * is determining the distance between a point and a segment accurately. + * + * Currently there are tests for different orders of magnitude (i.e., 1X, 10X, 100X, 1000X), as well as a test + * where the segment and the point lie in different hemispheres. + * + * If further tests need to be added here, make sure that the distance has been verified with QGIS. + * + * @see QGIS + */ @Test public void testDistanceToLine() { LatLng startLine = new LatLng(28.05359, -82.41632); @@ -476,7 +490,42 @@ public void testDistanceToLine() { LatLng p = new LatLng(28.05342, -82.41594); double distance = PolyUtil.distanceToLine(p, startLine, endLine); - assertEquals(37.947946, distance, 1e-6); + assertEquals(37.94596795917082, distance, 1e-6); + + startLine = new LatLng(49.321045, 12.097749); + endLine = new LatLng(49.321016, 12.097795); + p = new LatLng(49.3210674, 12.0978238); + + distance = PolyUtil.distanceToLine(p, startLine, endLine); + assertEquals(5.559443879999753, distance, 1e-6); + + startLine = new LatLng(48.125961, 11.548998); + endLine = new LatLng(48.125918, 11.549005); + p = new LatLng(48.125941, 11.549028); + + distance = PolyUtil.distanceToLine(p, startLine, endLine); + assertEquals(1.9733966358947437, distance, 1e-6); + + startLine = new LatLng(78.924669, 11.925521); + endLine = new LatLng(78.924707, 11.929060); + p = new LatLng(78.923164, 11.924029); + + distance = PolyUtil.distanceToLine(p, startLine, endLine); + assertEquals(170.35662670453187, distance, 1e-6); + + startLine = new LatLng(69.664036, 18.957124); + endLine = new LatLng(69.664029, 18.957109); + p = new LatLng(69.672901, 18.967911); + + distance = PolyUtil.distanceToLine(p, startLine, endLine); + assertEquals(1070.222749990837, distance, 1e-6); + + startLine = new LatLng(-0.018200, 109.343282); + endLine = new LatLng(-0.017877, 109.343537); + p = new LatLng(0.058299, 109.408054); + + distance = PolyUtil.distanceToLine(p, startLine, endLine); + assertEquals(11100.157563150981, distance, 1e-6); } @Test