From ac7cf037eb1f6253e33042acd04e6b424bdfbcf1 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Fri, 15 May 2020 17:39:18 -0400 Subject: [PATCH 01/16] Testing new distance to line formula --- .../com/google/maps/android/PolyUtil.java | 76 ++++++++++++++----- .../com/google/maps/android/PolyUtilTest.java | 14 +++- 2 files changed, 70 insertions(+), 20 deletions(-) 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..26f6e3838 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,26 @@ 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 com.google.maps.android.SphericalUtil.computeHeading; +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,25 +483,48 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La return computeDistanceBetween(end, p); } - final double s0lat = toRadians(p.latitude); - final double s0lng = toRadians(p.longitude); - final double s1lat = toRadians(start.latitude); - final double s1lng = toRadians(start.longitude); - final double s2lat = toRadians(end.latitude); - final double s2lng = toRadians(end.longitude); - - double s2s1lat = s2lat - s1lat; - double s2s1lng = s2lng - s1lng; - final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng) - / (s2s1lat * s2s1lat + s2s1lng * s2s1lng); - if (u <= 0) { +// final double s0lat = toRadians(p.latitude); +// final double s0lng = toRadians(p.longitude); +// final double s1lat = toRadians(start.latitude); +// final double s1lng = toRadians(start.longitude); +// final double s2lat = toRadians(end.latitude); +// final double s2lng = toRadians(end.longitude); +// +// double s2s1lat = s2lat - s1lat; +// double s2s1lng = s2lng - s1lng; +// final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng) +// / (s2s1lat * s2s1lat + s2s1lng * s2s1lng); +// if (u <= 0) { +// return computeDistanceBetween(p, start); +// } +// if (u >= 1) { +// return computeDistanceBetween(p, end); +// } +// LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); +// return computeDistanceBetween(p, su); + + // "Along-track" distance formula from https://www.movable-type.co.uk/scripts/latlong.html + double a13 = computeDistanceBetween(start, p); + double b13 = toRadians(computeHeading(start, p)); + double b12 = toRadians(computeHeading(start, end)); + + double axt = Math.asin(Math.sin(a13) * Math.sin(b13 - b12)); + + double distance = axt * EARTH_RADIUS; + + double alongTrackDistance = Math.acos(Math.cos(a13) / Math.cos(distance / EARTH_RADIUS)) * EARTH_RADIUS; + + if (alongTrackDistance <= 0) { return computeDistanceBetween(p, start); } - if (u >= 1) { + if (alongTrackDistance >= 1) { return computeDistanceBetween(p, end); } - LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); - return computeDistanceBetween(p, su); + return distance; + +// double aat = Math.acos(Math.cos(a13) / Math.abs(Math.cos(axt))); +// +// return aat*Math.sign(Math.cos(b12-b13)) * R; } /** 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..15fcc37d3 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 = @@ -476,7 +479,14 @@ 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.947946, 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.5, distance, 1e-6); } @Test From 55374b7e6cfa5271f0c69dae296510b57a5ae8f2 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Fri, 15 May 2020 17:47:18 -0400 Subject: [PATCH 02/16] Getting correct results --- .../main/java/com/google/maps/android/PolyUtil.java | 10 ++-------- .../java/com/google/maps/android/PolyUtilTest.java | 4 ++-- 2 files changed, 4 insertions(+), 10 deletions(-) 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 26f6e3838..184793867 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -504,7 +504,7 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La // return computeDistanceBetween(p, su); // "Along-track" distance formula from https://www.movable-type.co.uk/scripts/latlong.html - double a13 = computeDistanceBetween(start, p); + double a13 = computeDistanceBetween(start, p) / EARTH_RADIUS; double b13 = toRadians(computeHeading(start, p)); double b12 = toRadians(computeHeading(start, end)); @@ -514,13 +514,7 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La double alongTrackDistance = Math.acos(Math.cos(a13) / Math.cos(distance / EARTH_RADIUS)) * EARTH_RADIUS; - if (alongTrackDistance <= 0) { - return computeDistanceBetween(p, start); - } - if (alongTrackDistance >= 1) { - return computeDistanceBetween(p, end); - } - return distance; + return Math.abs(distance); // double aat = Math.acos(Math.cos(a13) / Math.abs(Math.cos(axt))); // 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 15fcc37d3..609bbd381 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -479,14 +479,14 @@ 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.945969, 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.5, distance, 1e-6); + assertEquals(5.55, distance, 1e-6); } @Test From 26e02e3250bc5e5b10cefc93be5e9f593a9052f4 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Mon, 18 May 2020 10:42:28 -0400 Subject: [PATCH 03/16] Code cleanup, fix simply tests --- .../java/com/google/maps/android/PolyUtil.java | 16 +++++++--------- .../com/google/maps/android/PolyUtilTest.java | 6 +++--- 2 files changed, 10 insertions(+), 12 deletions(-) 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 184793867..32aa18f82 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -504,21 +504,19 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La // return computeDistanceBetween(p, su); // "Along-track" distance formula from https://www.movable-type.co.uk/scripts/latlong.html - double a13 = computeDistanceBetween(start, p) / EARTH_RADIUS; + double d13 = computeDistanceBetween(start, p) / EARTH_RADIUS; double b13 = toRadians(computeHeading(start, p)); double b12 = toRadians(computeHeading(start, end)); - double axt = Math.asin(Math.sin(a13) * Math.sin(b13 - b12)); + double axt = Math.asin(Math.sin(d13) * Math.sin(b13 - b12)); - double distance = axt * EARTH_RADIUS; + // Distance from P to great-circle path defined by start and end points + double crossTrackDistance = axt * EARTH_RADIUS; - double alongTrackDistance = Math.acos(Math.cos(a13) / Math.cos(distance / EARTH_RADIUS)) * EARTH_RADIUS; + // Distance from the start point to the closest point on the path to P + double alongTrackDistance = Math.acos(Math.cos(d13) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; - return Math.abs(distance); - -// double aat = Math.acos(Math.cos(a13) / Math.abs(Math.cos(axt))); -// -// return aat*Math.sign(Math.cos(b12-b13)) * R; + return Math.abs(crossTrackDistance); } /** 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 609bbd381..748c0d01d 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -367,7 +367,7 @@ public void testSimplify() { copy = new ArrayList<>(oval); tolerance = 10; // meters List simplifiedOval = PolyUtil.simplify(oval, tolerance); - assertEquals(13, simplifiedOval.size()); + assertEquals(11, simplifiedOval.size()); assertEndPoints(oval, simplifiedOval); assertSimplifiedPointsFromLine(oval, simplifiedOval); assertLineLength(oval, simplifiedOval); @@ -382,7 +382,7 @@ public void testSimplify() { copy = new ArrayList<>(oval); tolerance = 10; // meters simplifiedOval = PolyUtil.simplify(oval, tolerance); - assertEquals(13, simplifiedOval.size()); + assertEquals(10, simplifiedOval.size()); assertEndPoints(oval, simplifiedOval); assertSimplifiedPointsFromLine(oval, simplifiedOval); assertLineLength(oval, simplifiedOval); @@ -486,7 +486,7 @@ public void testDistanceToLine() { p = new LatLng(49.3210674, 12.0978238); distance = PolyUtil.distanceToLine(p, startLine, endLine); - assertEquals(5.55, distance, 1e-6); + assertEquals(5.5594433, distance, 1e-6); } @Test From 3ad5d45079f7c8eae5572f30fa4b31b7fe99c9e2 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Mon, 18 May 2020 10:42:59 -0400 Subject: [PATCH 04/16] Update comments --- library/src/main/java/com/google/maps/android/PolyUtil.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 32aa18f82..437611b5d 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -503,7 +503,7 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La // LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); // return computeDistanceBetween(p, su); - // "Along-track" distance formula from https://www.movable-type.co.uk/scripts/latlong.html + // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html double d13 = computeDistanceBetween(start, p) / EARTH_RADIUS; double b13 = toRadians(computeHeading(start, p)); double b12 = toRadians(computeHeading(start, end)); From 4af9e756290ddb05bc24dcdc3bc822dab9e0e5de Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Mon, 18 May 2020 11:21:55 -0400 Subject: [PATCH 05/16] Refactor variables for clarity --- .../main/java/com/google/maps/android/PolyUtil.java | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) 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 437611b5d..de4b07d5f 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -504,17 +504,17 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La // return computeDistanceBetween(p, su); // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html - double d13 = computeDistanceBetween(start, p) / EARTH_RADIUS; - double b13 = toRadians(computeHeading(start, p)); - double b12 = toRadians(computeHeading(start, end)); + double distanceStartP = computeDistanceBetween(start, p) / EARTH_RADIUS; + double bearingStartP = toRadians(computeHeading(start, p)); + double bearingStartEnd = toRadians(computeHeading(start, end)); - double axt = Math.asin(Math.sin(d13) * Math.sin(b13 - b12)); + double axt = Math.asin(Math.sin(distanceStartP) * Math.sin(bearingStartP - bearingStartEnd)); // Distance from P to great-circle path defined by start and end points double crossTrackDistance = axt * EARTH_RADIUS; // Distance from the start point to the closest point on the path to P - double alongTrackDistance = Math.acos(Math.cos(d13) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; + double alongTrackDistance = Math.acos(Math.cos(distanceStartP) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; return Math.abs(crossTrackDistance); } From 52d0856365c998df360a26c736daa0f2789c894f Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Mon, 18 May 2020 15:53:55 -0400 Subject: [PATCH 06/16] Add original algorithm end, more commented out test data (for now) --- .../main/java/com/google/maps/android/PolyUtil.java | 5 +++++ .../java/com/google/maps/android/PolyUtilTest.java | 11 +++++++++++ 2 files changed, 16 insertions(+) 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 de4b07d5f..88d5347d5 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -503,6 +503,11 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La // LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); // return computeDistanceBetween(p, su); + // ORIGINAL ALGORITHM END +// LatLng sa = new LatLng(p.latitude - start.latitude, p.longitude - start.longitude); +// LatLng sb = new LatLng(u * (end.latitude - start.latitude), u * (end.longitude - start.longitude)); +// return computeDistanceBetween(sa, sb); + // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html double distanceStartP = computeDistanceBetween(start, p) / EARTH_RADIUS; double bearingStartP = toRadians(computeHeading(start, p)); 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 748c0d01d..bf0fc4c92 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -487,6 +487,17 @@ public void testDistanceToLine() { distance = PolyUtil.distanceToLine(p, startLine, endLine); assertEquals(5.5594433, distance, 1e-6); + + // Example data given in https://github.com/googlemaps/android-maps-utils/issues/519 +// startLine = new LatLng(13.383257, 52.498945); +// endLine = new LatLng(13.383194, 52.498748); +// p = new LatLng(13.384526409208775, 52.498523596997146); +// +// distance = PolyUtil.distanceToLine(p, startLine, endLine); +// assertEquals(95.0, distance, 1e-6); + + // QGIS tests - https://github.com/qgis/QGIS/blob/0df50ca979da86f5b8b8ec0eea2f8b67b8e8e63a/python/plugins/processing/tests/testdata/qgis_algorithm_tests2.yaml + // QGIS test data - https://github.com/qgis/QGIS/blob/b3d2619976a69d7fb67b884492da491dfaba287c/python/plugins/processing/tests/testdata/expected/hub_distance_lines.gml } @Test From d7be685a2bf2fc24c37f7c3214e75f79c8a532dc Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Mon, 18 May 2020 15:55:20 -0400 Subject: [PATCH 07/16] Add more commented out test info --- .../src/test/java/com/google/maps/android/PolyUtilTest.java | 3 +++ 1 file changed, 3 insertions(+) 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 bf0fc4c92..302e6bbd9 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -496,6 +496,9 @@ public void testDistanceToLine() { // distance = PolyUtil.distanceToLine(p, startLine, endLine); // assertEquals(95.0, distance, 1e-6); +// If I change the point slightly, like let's say +// LatLng(13.384605534374716, 52.49860503682293) it will return the correct result of about 98-99m + // QGIS tests - https://github.com/qgis/QGIS/blob/0df50ca979da86f5b8b8ec0eea2f8b67b8e8e63a/python/plugins/processing/tests/testdata/qgis_algorithm_tests2.yaml // QGIS test data - https://github.com/qgis/QGIS/blob/b3d2619976a69d7fb67b884492da491dfaba287c/python/plugins/processing/tests/testdata/expected/hub_distance_lines.gml } From 302f7237691a60a584ad558d81257d0fc36ffdfc Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Tue, 19 May 2020 14:55:56 -0400 Subject: [PATCH 08/16] Add links to theory for initial implementation --- library/src/main/java/com/google/maps/android/PolyUtil.java | 1 + 1 file changed, 1 insertion(+) 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 88d5347d5..e636b830f 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -483,6 +483,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); From 2041038e09709aa270784e396c4f7faca3d1aa7d Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Thu, 21 May 2020 11:31:36 -0400 Subject: [PATCH 09/16] Add more sample data --- .../test/java/com/google/maps/android/PolyUtilTest.java | 8 ++++++++ 1 file changed, 8 insertions(+) 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 302e6bbd9..34ad1726d 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -501,6 +501,14 @@ public void testDistanceToLine() { // QGIS tests - https://github.com/qgis/QGIS/blob/0df50ca979da86f5b8b8ec0eea2f8b67b8e8e63a/python/plugins/processing/tests/testdata/qgis_algorithm_tests2.yaml // QGIS test data - https://github.com/qgis/QGIS/blob/b3d2619976a69d7fb67b884492da491dfaba287c/python/plugins/processing/tests/testdata/expected/hub_distance_lines.gml + + // Example data from https://www.movable-type.co.uk/scripts/test/geodesy-test.html + // Cross Track distance - LatLon(53.2611, -0.7972).crossTrackDistanceTo(new LatLon(53.3206, -1.7297), new LatLon(53.1887, 0.1334)).toFixed(1).should.equal('-307.5') + // Along track distance - new LatLon(53.2611, -0.7972).alongTrackDistanceTo(new LatLon(53.3206, -1.7297), new LatLon(53.1887, 0.1334)).toFixed().should.equal('62331') + // Nearest point on segment1 - new LatLon(51.0, 1.9).nearestPointOnSegment(new LatLon(51.0, 1.0), new LatLon(51.0, 2.0)).toString().should.equal('51.0004°N, 001.9000°E') + // Nearest point on segment2 - new LatLon(51.0, 2.1).nearestPointOnSegment(new LatLon(51.0, 1.0), new LatLon(51.0, 2.0)).toString().should.equal('51.0000°N, 002.0000°E') + // Nearest point on segment antip - new LatLon(10, -140).nearestPointOnSegment(new LatLon(0, 20), new LatLon(0, 40)).toString().should.equal('00.0000°N, 020.0000°E') + // More at https://github.com/chrisveness/geodesy/blob/master/test/latlon-nvector-spherical-tests.js#L183 } @Test From 803875609e591cbfe8d8a88203881b7aef68a0b1 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Thu, 21 May 2020 11:37:07 -0400 Subject: [PATCH 10/16] Add another formula note --- library/src/main/java/com/google/maps/android/PolyUtil.java | 2 ++ 1 file changed, 2 insertions(+) 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 e636b830f..c7cf85f6a 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -522,6 +522,8 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La // Distance from the start point to the closest point on the path to P double alongTrackDistance = Math.acos(Math.cos(distanceStartP) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; + // to compute points known distance from a great circle - http://www.edwilliams.org/avform.htm#XTE (also different cross and along track error formulas) + return Math.abs(crossTrackDistance); } From b162dbd6603404741ea6ae4f28ce345cc31523a8 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Thu, 21 May 2020 16:18:01 -0400 Subject: [PATCH 11/16] Add lonCorrection From https://github.com/googlemaps/android-maps-utils/issues/720#issuecomment-631263199 --- .../com/google/maps/android/PolyUtil.java | 72 +++++++++---------- .../com/google/maps/android/PolyUtilTest.java | 2 +- 2 files changed, 37 insertions(+), 37 deletions(-) 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 c7cf85f6a..924fca311 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -33,7 +33,6 @@ 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 com.google.maps.android.SphericalUtil.computeHeading; import static java.lang.Math.PI; import static java.lang.Math.cos; import static java.lang.Math.max; @@ -484,47 +483,48 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La } // 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); -// final double s1lng = toRadians(start.longitude); -// final double s2lat = toRadians(end.latitude); -// final double s2lng = toRadians(end.longitude); -// -// double s2s1lat = s2lat - s1lat; -// double s2s1lng = s2lng - s1lng; -// final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * s2s1lng) -// / (s2s1lat * s2s1lat + s2s1lng * s2s1lng); -// if (u <= 0) { -// return computeDistanceBetween(p, start); -// } -// if (u >= 1) { -// return computeDistanceBetween(p, end); -// } -// LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); -// return computeDistanceBetween(p, su); + final double s0lat = toRadians(p.latitude); + final double s0lng = toRadians(p.longitude); + final double s1lat = toRadians(start.latitude); + final double s1lng = toRadians(start.longitude); + 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) * lonCorrection; + final double u = ((s0lat - s1lat) * s2s1lat + (s0lng - s1lng) * lonCorrection * s2s1lng) + / (s2s1lat * s2s1lat + s2s1lng * s2s1lng); + if (u <= 0) { + return computeDistanceBetween(p, start); + } + if (u >= 1) { + return computeDistanceBetween(p, end); + } + LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); + return computeDistanceBetween(p, su); // ORIGINAL ALGORITHM END // LatLng sa = new LatLng(p.latitude - start.latitude, p.longitude - start.longitude); // LatLng sb = new LatLng(u * (end.latitude - start.latitude), u * (end.longitude - start.longitude)); // return computeDistanceBetween(sa, sb); - // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html - double distanceStartP = computeDistanceBetween(start, p) / EARTH_RADIUS; - double bearingStartP = toRadians(computeHeading(start, p)); - double bearingStartEnd = toRadians(computeHeading(start, end)); - - double axt = Math.asin(Math.sin(distanceStartP) * Math.sin(bearingStartP - bearingStartEnd)); - - // Distance from P to great-circle path defined by start and end points - double crossTrackDistance = axt * EARTH_RADIUS; - - // Distance from the start point to the closest point on the path to P - double alongTrackDistance = Math.acos(Math.cos(distanceStartP) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; - - // to compute points known distance from a great circle - http://www.edwilliams.org/avform.htm#XTE (also different cross and along track error formulas) - - return Math.abs(crossTrackDistance); +// // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html +// double distanceStartP = computeDistanceBetween(start, p) / EARTH_RADIUS; +// double bearingStartP = toRadians(computeHeading(start, p)); +// double bearingStartEnd = toRadians(computeHeading(start, end)); +// +// double axt = Math.asin(Math.sin(distanceStartP) * Math.sin(bearingStartP - bearingStartEnd)); +// +// // Distance from P to great-circle path defined by start and end points +// double crossTrackDistance = axt * EARTH_RADIUS; +// +// // Distance from the start point to the closest point on the path to P +// double alongTrackDistance = Math.acos(Math.cos(distanceStartP) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; +// +// // to compute points known distance from a great circle - http://www.edwilliams.org/avform.htm#XTE (also different cross and along track error formulas) +// +// return Math.abs(crossTrackDistance); } /** 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 34ad1726d..459ddd02c 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -479,7 +479,7 @@ public void testDistanceToLine() { LatLng p = new LatLng(28.05342, -82.41594); double distance = PolyUtil.distanceToLine(p, startLine, endLine); - assertEquals(37.945969, distance, 1e-6); + assertEquals(37.94596795917082, distance, 1e-6); startLine = new LatLng(49.321045, 12.097749); endLine = new LatLng(49.321016, 12.097795); From 05da0026bc29b77986c25668b8617a8f4654d860 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Wed, 22 Jul 2020 17:35:31 -0400 Subject: [PATCH 12/16] chore: Remove old algorithm end commented code --- library/src/main/java/com/google/maps/android/PolyUtil.java | 5 ----- 1 file changed, 5 deletions(-) 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 924fca311..7d51073d0 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -504,11 +504,6 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); return computeDistanceBetween(p, su); - // ORIGINAL ALGORITHM END -// LatLng sa = new LatLng(p.latitude - start.latitude, p.longitude - start.longitude); -// LatLng sb = new LatLng(u * (end.latitude - start.latitude), u * (end.longitude - start.longitude)); -// return computeDistanceBetween(sa, sb); - // // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html // double distanceStartP = computeDistanceBetween(start, p) / EARTH_RADIUS; // double bearingStartP = toRadians(computeHeading(start, p)); From 57a510f5fec6f8c39553f22c3a398988d05aff86 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Wed, 22 Jul 2020 17:36:59 -0400 Subject: [PATCH 13/16] chore: Remove "Cross-track distance" algorithm and test data links --- .../com/google/maps/android/PolyUtil.java | 17 -------------- .../com/google/maps/android/PolyUtilTest.java | 22 ------------------- 2 files changed, 39 deletions(-) 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 7d51073d0..1c32d5a38 100644 --- a/library/src/main/java/com/google/maps/android/PolyUtil.java +++ b/library/src/main/java/com/google/maps/android/PolyUtil.java @@ -503,23 +503,6 @@ public static double distanceToLine(final LatLng p, final LatLng start, final La } LatLng su = new LatLng(start.latitude + u * (end.latitude - start.latitude), start.longitude + u * (end.longitude - start.longitude)); return computeDistanceBetween(p, su); - -// // "Cross-track distance" distance formula from https://www.movable-type.co.uk/scripts/latlong.html -// double distanceStartP = computeDistanceBetween(start, p) / EARTH_RADIUS; -// double bearingStartP = toRadians(computeHeading(start, p)); -// double bearingStartEnd = toRadians(computeHeading(start, end)); -// -// double axt = Math.asin(Math.sin(distanceStartP) * Math.sin(bearingStartP - bearingStartEnd)); -// -// // Distance from P to great-circle path defined by start and end points -// double crossTrackDistance = axt * EARTH_RADIUS; -// -// // Distance from the start point to the closest point on the path to P -// double alongTrackDistance = Math.acos(Math.cos(distanceStartP) / Math.cos(crossTrackDistance / EARTH_RADIUS)) * EARTH_RADIUS; -// -// // to compute points known distance from a great circle - http://www.edwilliams.org/avform.htm#XTE (also different cross and along track error formulas) -// -// return Math.abs(crossTrackDistance); } /** 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 459ddd02c..95b4dc280 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -487,28 +487,6 @@ public void testDistanceToLine() { distance = PolyUtil.distanceToLine(p, startLine, endLine); assertEquals(5.5594433, distance, 1e-6); - - // Example data given in https://github.com/googlemaps/android-maps-utils/issues/519 -// startLine = new LatLng(13.383257, 52.498945); -// endLine = new LatLng(13.383194, 52.498748); -// p = new LatLng(13.384526409208775, 52.498523596997146); -// -// distance = PolyUtil.distanceToLine(p, startLine, endLine); -// assertEquals(95.0, distance, 1e-6); - -// If I change the point slightly, like let's say -// LatLng(13.384605534374716, 52.49860503682293) it will return the correct result of about 98-99m - - // QGIS tests - https://github.com/qgis/QGIS/blob/0df50ca979da86f5b8b8ec0eea2f8b67b8e8e63a/python/plugins/processing/tests/testdata/qgis_algorithm_tests2.yaml - // QGIS test data - https://github.com/qgis/QGIS/blob/b3d2619976a69d7fb67b884492da491dfaba287c/python/plugins/processing/tests/testdata/expected/hub_distance_lines.gml - - // Example data from https://www.movable-type.co.uk/scripts/test/geodesy-test.html - // Cross Track distance - LatLon(53.2611, -0.7972).crossTrackDistanceTo(new LatLon(53.3206, -1.7297), new LatLon(53.1887, 0.1334)).toFixed(1).should.equal('-307.5') - // Along track distance - new LatLon(53.2611, -0.7972).alongTrackDistanceTo(new LatLon(53.3206, -1.7297), new LatLon(53.1887, 0.1334)).toFixed().should.equal('62331') - // Nearest point on segment1 - new LatLon(51.0, 1.9).nearestPointOnSegment(new LatLon(51.0, 1.0), new LatLon(51.0, 2.0)).toString().should.equal('51.0004°N, 001.9000°E') - // Nearest point on segment2 - new LatLon(51.0, 2.1).nearestPointOnSegment(new LatLon(51.0, 1.0), new LatLon(51.0, 2.0)).toString().should.equal('51.0000°N, 002.0000°E') - // Nearest point on segment antip - new LatLon(10, -140).nearestPointOnSegment(new LatLon(0, 20), new LatLon(0, 40)).toString().should.equal('00.0000°N, 020.0000°E') - // More at https://github.com/chrisveness/geodesy/blob/master/test/latlon-nvector-spherical-tests.js#L183 } @Test From 8dc0887ea7b5d2f4a93a9934925bd626599f8f26 Mon Sep 17 00:00:00 2001 From: Sean Barbeau Date: Thu, 23 Jul 2020 09:29:18 -0400 Subject: [PATCH 14/16] chore: Fix simplify test --- .../src/test/java/com/google/maps/android/PolyUtilTest.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 95b4dc280..fe7969a1a 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -367,7 +367,7 @@ public void testSimplify() { copy = new ArrayList<>(oval); tolerance = 10; // meters List simplifiedOval = PolyUtil.simplify(oval, tolerance); - assertEquals(11, simplifiedOval.size()); + assertEquals(13, simplifiedOval.size()); assertEndPoints(oval, simplifiedOval); assertSimplifiedPointsFromLine(oval, simplifiedOval); assertLineLength(oval, simplifiedOval); @@ -382,7 +382,7 @@ public void testSimplify() { copy = new ArrayList<>(oval); tolerance = 10; // meters simplifiedOval = PolyUtil.simplify(oval, tolerance); - assertEquals(10, simplifiedOval.size()); + assertEquals(13, simplifiedOval.size()); assertEndPoints(oval, simplifiedOval); assertSimplifiedPointsFromLine(oval, simplifiedOval); assertLineLength(oval, simplifiedOval); From 7e132550b76f84df7d9cf108af3e36b7818c5fbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Lo=CC=81pez=20Man=CC=83as?= Date: Tue, 3 Jan 2023 14:02:38 +0100 Subject: [PATCH 15/16] chore: addded tests for different latitudes and magnitude --- .../com/google/maps/android/PolyUtilTest.java | 30 ++++++++++++++++++- 1 file changed, 29 insertions(+), 1 deletion(-) 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 fe7969a1a..626ed44ea 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -486,7 +486,35 @@ public void testDistanceToLine() { p = new LatLng(49.3210674, 12.0978238); distance = PolyUtil.distanceToLine(p, startLine, endLine); - assertEquals(5.5594433, distance, 1e-6); + 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 From a8d1da0b70838bc253658b176f899535292fb143 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Enrique=20Lo=CC=81pez=20Man=CC=83as?= Date: Thu, 5 Jan 2023 15:33:59 +0100 Subject: [PATCH 16/16] chore: added javadoc --- .../java/com/google/maps/android/PolyUtilTest.java | 11 +++++++++++ 1 file changed, 11 insertions(+) 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 626ed44ea..cff8c538e 100644 --- a/library/src/test/java/com/google/maps/android/PolyUtilTest.java +++ b/library/src/test/java/com/google/maps/android/PolyUtilTest.java @@ -472,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);