From e4888ce3a2d8ac6e4324de351f82968ddadba07b Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 8 Feb 2018 11:31:19 +0300 Subject: [PATCH 1/3] Add accurate check for user location within visible map region 1. Get map bounds from mapView size 2. Implement better point-in-polygon checking algorithm --- library/build.gradle | 2 +- .../io/ona/kujaku/activities/MapActivity.java | 38 +++++-- .../io/ona/kujaku/utils/CoordinateUtils.java | 64 ++++++----- .../ona/kujaku/utils/CoordinateUtilsTest.java | 106 ++++++++++++++++++ 4 files changed, 172 insertions(+), 38 deletions(-) create mode 100644 utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java diff --git a/library/build.gradle b/library/build.gradle index 95c0ffb31..612e57f10 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -151,7 +151,7 @@ dependencies { transitive = true; exclude group: 'com.android.support', module: 'support-v4' } - compile('io.ona.kujaku:utils:0.2.6-11-3') { + compile(project(":utils")) {//('io.ona.kujaku:utils:0.2.6-11-3') { transitive = true; exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' exclude group: 'com.android.support', module: 'support-v4' diff --git a/library/src/main/java/io/ona/kujaku/activities/MapActivity.java b/library/src/main/java/io/ona/kujaku/activities/MapActivity.java index e8c7b249c..a419d8169 100644 --- a/library/src/main/java/io/ona/kujaku/activities/MapActivity.java +++ b/library/src/main/java/io/ona/kujaku/activities/MapActivity.java @@ -38,10 +38,10 @@ import com.mapbox.mapboxsdk.camera.CameraPosition; import com.mapbox.mapboxsdk.camera.CameraUpdateFactory; import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.VisibleRegion; import com.mapbox.mapboxsdk.maps.MapView; import com.mapbox.mapboxsdk.maps.MapboxMap; import com.mapbox.mapboxsdk.maps.OnMapReadyCallback; +import com.mapbox.mapboxsdk.maps.Projection; import com.mapbox.services.commons.geojson.Feature; import org.json.JSONArray; @@ -122,7 +122,7 @@ public class MapActivity extends AppCompatActivity implements MapboxMap.OnMapCli private Marker myLocationMarker; private LatLng focusedLocation; - private boolean focusedOnOfMyLocation = false; + private boolean focusedOnMyLocation = false; //Todo: Move reading data to another Thread @@ -222,7 +222,7 @@ private void initMapBoxSdk(Bundle savedInstanceState, String mapBoxStylePath, mapView.getMapAsync(new OnMapReadyCallback() { @Override - public void onMapReady(MapboxMap mapboxMap) { + public void onMapReady(final MapboxMap mapboxMap) { //Set listener for markers MapActivity.this.mapboxMap = mapboxMap; mapboxMap.setOnMapClickListener(MapActivity.this); @@ -230,12 +230,10 @@ public void onMapReady(MapboxMap mapboxMap) { @Override public void onScroll() { if (focusedLocation != null) { - VisibleRegion mapsVisibleRegion = MapActivity.this.mapboxMap.getProjection().getVisibleRegion(); - //LatLngBounds mapBounds = LatLngBounds.from(mapsVisibleRegion.l); - //Todo: Use the actual corners instead of the smallest bounding box (latLngBounds) which are more accurate + LatLng[] mapBounds = getMapBounds(mapboxMap, mapView); - if (!CoordinateUtils.isLocationInBounds(focusedLocation, mapsVisibleRegion.latLngBounds) && focusedOnOfMyLocation) { - focusedOnOfMyLocation = false; + if (!CoordinateUtils.isLocationInBounds(focusedLocation, mapBounds[0], mapBounds[1], mapBounds[2], mapBounds[3]) && focusedOnMyLocation) { + focusedOnMyLocation = false; // Revert the icon to the non-focused grey one changeTargetIcon(R.drawable.ic_my_location); @@ -604,7 +602,6 @@ public void onAnimationUpdate(ValueAnimator animation) { }); valueAnimator.start(); - //Todo Figure out how to handle another item being selected while this one is being animated } private int getScreenWidth(Activity activity) { @@ -730,7 +727,7 @@ private void focusOnMyLocation(@NonNull MapboxMap mapboxMap) { if (lastLocation != null) { LatLng newTarget = new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()); focusedLocation = new LatLng(lastLocation.getLatitude(), lastLocation.getLongitude()); - focusedOnOfMyLocation = true; + focusedOnMyLocation = true; // Change the icon to the blue one changeTargetIcon(R.drawable.ic_my_location_focused); @@ -837,4 +834,25 @@ private void changeDrawable(@NonNull View view, int drawableId) { } } } + + public LatLng[] getMapBounds(MapboxMap mapboxMap, MapView mapView) { + float left = 0; + float right = mapView.getWidth(); + float top = 0; + float bottom = mapView.getHeight(); + + Projection projection = mapboxMap.getProjection(); + + LatLng topLeft = projection.fromScreenLocation(new PointF(left, top)); + LatLng topRight = projection.fromScreenLocation(new PointF(right, top)); + LatLng bottomRight = projection.fromScreenLocation(new PointF(right, bottom)); + LatLng bottomLeft = projection.fromScreenLocation(new PointF(left, bottom)); + + return new LatLng[]{ + topLeft, + topRight, + bottomRight, + bottomLeft + }; + } } diff --git a/utils/src/main/java/io/ona/kujaku/utils/CoordinateUtils.java b/utils/src/main/java/io/ona/kujaku/utils/CoordinateUtils.java index 3db7c5c5c..4101a2bb3 100644 --- a/utils/src/main/java/io/ona/kujaku/utils/CoordinateUtils.java +++ b/utils/src/main/java/io/ona/kujaku/utils/CoordinateUtils.java @@ -1,52 +1,62 @@ package io.ona.kujaku.utils; import android.support.annotation.NonNull; - import com.mapbox.mapboxsdk.geometry.LatLng; -import com.mapbox.mapboxsdk.geometry.LatLngBounds; /** * Created by Ephraim Kigamba - ekigamba@ona.io on 19/12/2017. */ - public class CoordinateUtils { /** * Checks whether the provided coordinate is within the provided Bounds. * + * NOTE: The order in which you provide the bounds does not affect the results + * * @param positionInQuestion - * @param myMapBounds + * @param topLeft + * @param topRight + * @param bottomLeft + * @param bottomRight * @return */ - public static boolean isLocationInBounds(@NonNull LatLng positionInQuestion, @NonNull LatLngBounds myMapBounds) { - return isLocationInBounds(positionInQuestion, myMapBounds.getLatNorth(), myMapBounds.getLatSouth(), myMapBounds.getLonEast(), myMapBounds.getLonWest()); + public static boolean isLocationInBounds(@NonNull LatLng positionInQuestion, LatLng topLeft, LatLng topRight, LatLng bottomLeft, LatLng bottomRight) { + Point[] bounds = new Point[4]; + bounds[0] = new Point(topLeft.getLongitude(), topLeft.getLatitude()); + bounds[1] = new Point(topRight.getLongitude(), topRight.getLatitude()); + bounds[2] = new Point(bottomLeft.getLongitude(), bottomLeft.getLatitude()); + bounds[3] = new Point(bottomRight.getLongitude(), bottomRight.getLatitude()); + + return contains(bounds, new Point(positionInQuestion.getLongitude(), positionInQuestion.getLatitude())); } /** - * Checks whether the provided coordinate is within the provided Bounds. + * Return true if the given point is contained inside the boundary. + * See: http://www.ecse.rpi.edu/Homepages/wrf/Research/Short_Notes/pnpoly.html + * @param needle The point to check + * @return true if the point is inside the boundary, false otherwise * - * @param positionInQuestion - * @param latNorth - * @param latSouth - * @param lonEast - * @param lonWest - * @return */ - public static boolean isLocationInBounds(@NonNull LatLng positionInQuestion, double latNorth, double latSouth, double lonEast, double lonWest) { - return (positionInQuestion.getLatitude() <= latNorth - && positionInQuestion.getLatitude() >= latSouth - && positionInQuestion.getLongitude() <= lonEast - && positionInQuestion.getLongitude() >= lonWest); + public static boolean contains(Point[] bounds, Point needle) { + int i; + int j; + boolean result = false; + for (i = 0, j = bounds.length - 1; i < bounds.length; j = i++) { + if ((bounds[i].y > needle.y) != (bounds[j].y > needle.y) && + (needle.x < (bounds[j].x - bounds[i].x) * (needle.y - bounds[i].y) / (bounds[j].y-bounds[i].y) + bounds[i].x)) { + result = !result; + } + } + return result; } - /*public static boolean isLocationInBounds(@NonNull LatLng positionInQuestion, @NonNull LatLng topLeft, @NonNull LatLng topRight, @NonNull LatLng bottomRight, @NonNull LatLng bottomLeft) { - if (positionInQuestion.getLatitude() <= latNorth - && positionInQuestion.getLatitude() >= latSouth - && positionInQuestion.getLongitude() <= lonEast - && positionInQuestion.getLongitude() >= lonWest) { - return true; - } + static class Point { + public final double x; + public final double y; - return false; - }*/ + public Point(double x, double y) { + this.x = x; + this.y = y; + } + } } diff --git a/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java b/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java new file mode 100644 index 000000000..cbaea2fbe --- /dev/null +++ b/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java @@ -0,0 +1,106 @@ +package io.ona.kujaku.utils; + +import com.mapbox.mapboxsdk.geometry.LatLng; + +import org.junit.Test; +import org.junit.runner.RunWith; + +import static org.junit.Assert.*; + +/** + * Created by Ephraim Kigamba - ekigamba@ona.io on 07/02/2018. + */ +public class CoordinateUtilsTest { + + @Test + public void isLocationInBounds() { + LatLng bound1 = new LatLng(1.053254883591756, + 0.339202880859375); + LatLng bound2 = new LatLng(0.707226913459037, + -0.11260986328124999); + LatLng bound3 = new LatLng(0.20324664405209258, + 0.278778076171875); + LatLng bound4 = new LatLng(0.546561534676349, + 0.98052978515625); + + LatLng point1 = new LatLng(0.7218541012920028, + 0.6592279349874681); + LatLng point2 = new LatLng(0.2881703637723443, + 0.25182564298088916); + LatLng point3 = new LatLng(0.9615897751356437, + 0.8265933408660401); + LatLng point4 = new LatLng(0.8249779712917739, + 0.33087073166495085); + LatLng point5 = new LatLng(0.37328038670170727, + 0.8613979983001541); + LatLng point6 = new LatLng(0.5828701459957611, + 0.6038155629767493); + LatLng point7 = new LatLng(0.6336809580943352, + 0.41203384171480817); + LatLng point8 = new LatLng(0.5391318208772033, + 0.12247511076740225); + LatLng point9 = new LatLng(0.6852696915452708, + 0.9021573945160707); + LatLng point10 = new LatLng(0.8761255229096057, + 0.58502197265625); + LatLng point11 = new LatLng(0.49513234253574634, + 0.8266160519883283); + + assertEquals(true, CoordinateUtils.isLocationInBounds(point1, bound1, bound2, bound3, bound4)); + assertEquals(true, CoordinateUtils.isLocationInBounds(point2, bound1, bound2, bound3, bound4)); + assertEquals(false, CoordinateUtils.isLocationInBounds(point3, bound1, bound2, bound3, bound4)); + assertEquals(true, CoordinateUtils.isLocationInBounds(point4, bound1, bound2, bound3, bound4)); + assertEquals(false, CoordinateUtils.isLocationInBounds(point5, bound1, bound2, bound3, bound4)); + assertEquals(true, CoordinateUtils.isLocationInBounds(point6, bound1, bound2, bound3, bound4)); + assertEquals(true, CoordinateUtils.isLocationInBounds(point7, bound1, bound2, bound3, bound4)); + assertEquals(true, CoordinateUtils.isLocationInBounds(point8, bound1, bound2, bound3, bound4)); + assertEquals(false, CoordinateUtils.isLocationInBounds(point9, bound1, bound2, bound3, bound4)); + assertEquals(false, CoordinateUtils.isLocationInBounds(point10, bound1, bound2, bound3, bound4)); + assertEquals(true, CoordinateUtils.isLocationInBounds(point11, bound1, bound2, bound3, bound4)); + } + + @Test + public void contains() throws Exception { + CoordinateUtils.Point[] bounds = new CoordinateUtils.Point[4]; + bounds[0] = new CoordinateUtils.Point(0.339202880859375, + 1.053254883591756); + bounds[1] = new CoordinateUtils.Point(-0.11260986328124999, + 0.707226913459037); + bounds[2] = new CoordinateUtils.Point(0.278778076171875, + 0.20324664405209258); + bounds[3] = new CoordinateUtils.Point(0.98052978515625, + 0.546561534676349); + + CoordinateUtils.Point points[] = new CoordinateUtils.Point[11]; + + points[0] = new CoordinateUtils.Point(0.6592279349874681, + 0.7218541012920028); + points[1] = new CoordinateUtils.Point(0.25182564298088916, + 0.2881703637723443); + points[2] = new CoordinateUtils.Point(0.8265933408660401, + 0.9615897751356437); + points[3] = new CoordinateUtils.Point(0.33087073166495085, + 0.8249779712917739); + points[4] = new CoordinateUtils.Point(0.8613979983001541, + 0.37328038670170727); + points[5] = new CoordinateUtils.Point(0.6038155629767493, + 0.5828701459957611); + points[6] = new CoordinateUtils.Point(0.41203384171480817, + 0.6336809580943352); + points[7] = new CoordinateUtils.Point(0.12247511076740225, + 0.5391318208772033); + points[8] = new CoordinateUtils.Point(0.9021573945160707, + 0.6852696915452708); + points[9] = new CoordinateUtils.Point(0.58502197265625, + 0.8761255229096057); + points[10] = new CoordinateUtils.Point(0.8266160519883283, + 0.49513234253574634); + + boolean[] expectedResults = new boolean[]{true, true, false, true, false, true, true, true, false, false, true}; + + for(int i = 0; i < 11; i++) { + assertEquals(expectedResults[i], CoordinateUtils.contains(bounds, points[i])); + } + } + +} \ No newline at end of file From 38364705d7935a19e5862bddd61062ab51f8e016 Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 8 Feb 2018 11:50:08 +0300 Subject: [PATCH 2/3] Change library & utils version to pre-release 1 of issue 43 --- library/build.gradle | 2 +- utils/build.gradle | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/library/build.gradle b/library/build.gradle index 612e57f10..4e825ef3a 100644 --- a/library/build.gradle +++ b/library/build.gradle @@ -151,7 +151,7 @@ dependencies { transitive = true; exclude group: 'com.android.support', module: 'support-v4' } - compile(project(":utils")) {//('io.ona.kujaku:utils:0.2.6-11-3') { + compile('io.ona.kujaku:utils:0.2.6-43-1') { transitive = true; exclude group: 'com.mapbox.mapboxsdk', module: 'mapbox-android-sdk' exclude group: 'com.android.support', module: 'support-v4' diff --git a/utils/build.gradle b/utils/build.gradle index b98e6a530..33ed63bb7 100644 --- a/utils/build.gradle +++ b/utils/build.gradle @@ -2,7 +2,7 @@ apply plugin: 'com.android.library' apply plugin: 'com.jfrog.bintray' apply plugin: 'maven-publish' -version '0.2.6-11-3' +version '0.2.6-43-1' project.version = this.version task sourceJar(type: Jar) { From 7a727d72f47ffb01a92427deffff11509651d86e Mon Sep 17 00:00:00 2001 From: Ephraim Kigamba Date: Thu, 8 Feb 2018 12:09:22 +0300 Subject: [PATCH 3/3] Fix codacy issues --- .../src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java b/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java index cbaea2fbe..3adb64c0e 100644 --- a/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java +++ b/utils/src/test/java/io/ona/kujaku/utils/CoordinateUtilsTest.java @@ -3,9 +3,8 @@ import com.mapbox.mapboxsdk.geometry.LatLng; import org.junit.Test; -import org.junit.runner.RunWith; -import static org.junit.Assert.*; +import static org.junit.Assert.assertEquals; /** * Created by Ephraim Kigamba - ekigamba@ona.io on 07/02/2018.