diff --git a/maps-utils-ktx/src/main/java/com/google/maps/android/ktx/LatLng.kt b/maps-utils-ktx/src/main/java/com/google/maps/android/ktx/LatLng.kt index 56b949fb..f468e98c 100644 --- a/maps-utils-ktx/src/main/java/com/google/maps/android/ktx/LatLng.kt +++ b/maps-utils-ktx/src/main/java/com/google/maps/android/ktx/LatLng.kt @@ -15,12 +15,65 @@ * */ @file:Suppress("NOTHING_TO_INLINE") + package com.google.maps.android.ktx import com.google.android.gms.maps.model.LatLng import com.google.maps.android.PolyUtil import com.google.maps.android.SphericalUtil +/** + * Computes whether the given [latLng] lies on or is near this polyline within [tolerance] (in + * meters). + * + * @param latLng the LatLng to inspect + * @param geodesic if this polyline is geodesic or not + * @param tolerance the tolerance in meters + * @return true if [latLng] is on this path, otherwise, false + * + * @see PolyUtil.isLocationOnPath + */ +inline fun List.isLocationOnPath( + latLng: LatLng, + geodesic: Boolean, + tolerance: Double = 0.1 +): Boolean = PolyUtil.isLocationOnPath(latLng, this, geodesic, tolerance) + +/** + * Checks whether or not [latLng] lies on or is near the edge of this polygon within the [tolerance] + * (in meters). The default value is [PolyUtil.DEFAULT_TOLERANCE]. + * + * @param latLng the LatLng to inspect + * @param geodesic if this polygon is geodesic or not + * @param tolerance the tolerance in meters + * @return true if [latLng] lies on or is near the edge of this Polygon, otherwise, false + * + * @see PolyUtil.isLocationOnEdge + */ +inline fun List.isOnEdge( + latLng: LatLng, + geodesic: Boolean, + tolerance: Double = 0.1 +): Boolean = PolyUtil.isLocationOnEdge(latLng, this, geodesic, tolerance) + +/** + * Computes whether the [latLng] lies inside this. + * + * The polygon is always considered closed, regardless of whether the last point equals + * the first or not. + * + * Inside is defined as not containing the South Pole -- the South Pole is always outside. + * The polygon is formed of great circle segments if [geodesic] is true, and of rhumb + * (loxodromic) segments otherwise. + * + * @param latLng the LatLng to check if it is contained within this polygon + * @param geodesic if this Polygon is geodesic or not + * + * @see PolyUtil.containsLocation + */ +inline fun List.containsLocation(latLng: LatLng, geodesic: Boolean): Boolean = + PolyUtil.containsLocation(latLng, this, geodesic) + /** * Simplifies this list of LatLng using the Douglas-Peucker decimation. Increasing the value of * [tolerance] will result in fewer points. @@ -30,7 +83,8 @@ import com.google.maps.android.SphericalUtil * * @see PolyUtil.simplify */ -inline fun List.simplify(tolerance: Double): List = PolyUtil.simplify(this, tolerance) +inline fun List.simplify(tolerance: Double): List = + PolyUtil.simplify(this, tolerance) /** * Decodes this encoded string into a [LatLng] list. @@ -48,7 +102,6 @@ inline fun String.toLatLngList(): List = PolyUtil.decode(this) * @return the encoded String * * @see [Polyline Algorithm Format](https://developers.google.com/maps/documentation/utilities/polylinealgorithm) - * */ inline fun List.latLngListEncode(): String = PolyUtil.encode(this) @@ -75,7 +128,6 @@ inline fun List.sphericalPathLength(): Double = SphericalUtil.computeLen */ inline fun List.sphericalPolygonArea(): Double = SphericalUtil.computeArea(this) - /** * Computes the signed area under a closed path on Earth. The sign of the area may be used to * determine the orientation of the path. diff --git a/maps-utils-ktx/src/test/java/com/google/maps/android/ktx/LatLngTest.kt b/maps-utils-ktx/src/test/java/com/google/maps/android/ktx/LatLngTest.kt index 269b45e5..14767ccc 100644 --- a/maps-utils-ktx/src/test/java/com/google/maps/android/ktx/LatLngTest.kt +++ b/maps-utils-ktx/src/test/java/com/google/maps/android/ktx/LatLngTest.kt @@ -144,4 +144,40 @@ class LatLngTest { 1e-6 ) } + + @Test + fun `contains location evaluates to true`() { + val latLngList = listOf(LatLng(0.0, 0.0), LatLng(0.0, 90.0), LatLng(-90.0, 45.0)) + assertTrue(latLngList.containsLocation(LatLng(30.0, 45.0), geodesic = true)) + } + + @Test + fun `contains location evaluates to false`() { + val latLngList = listOf(LatLng(0.0, 0.0), LatLng(0.0, 90.0), LatLng(-90.0, 45.0)) + assertFalse(latLngList.containsLocation(LatLng(-30.0, 45.0), geodesic = true)) + } + + @Test + fun `isOnEdge location evaluates to true`() { + val latLngList = listOf(LatLng(0.0, 0.0), LatLng(0.0, 90.0), LatLng(-90.0, 45.0)) + assertTrue(latLngList.isOnEdge(LatLng(0.0, 45.0), geodesic = true)) + } + + @Test + fun `isOnEdge location evaluates to false`() { + val latLngList = listOf(LatLng(0.0, 0.0), LatLng(0.0, 90.0), LatLng(-90.0, 45.0)) + assertFalse(latLngList.isOnEdge(LatLng(0.0, -45.0), geodesic = true)) + } + + @Test + fun `isLocationOnPath location evaluates to true`() { + val latLngList = listOf(LatLng(0.0, 0.0), LatLng(0.0, 90.0), LatLng(0.0, 180.0)) + assertTrue(latLngList.isLocationOnPath(LatLng(0.0, 45.0), geodesic = true)) + } + + @Test + fun `isLocationOnPath location evaluates to false`() { + val latLngList = listOf(LatLng(0.0, 0.0), LatLng(0.0, 90.0), LatLng(0.0, 180.0)) + assertFalse(latLngList.isLocationOnPath(LatLng(0.0, -45.0), geodesic = true)) + } } \ No newline at end of file