From 7c9a8fc5561eb0234757134177a901623f5e0f07 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 9 Jan 2020 19:38:29 +0200 Subject: [PATCH] Add getVisibleCoordinateBounds method. (#109) * Update visible region constructor doc. * Add getVisibleCoordinateBounds method. * Fix LatLngBounds#contains method, update to use Map::latLngBoundsForCameraUnwrapped(const CameraOptions& camera) on native side. * Revert changes in LatLngBounds#contains. --- .../mapboxsdk/geometry/LatLngBounds.java | 2 +- .../mapboxsdk/geometry/VisibleRegion.java | 6 +- .../com/mapbox/mapboxsdk/maps/NativeMap.java | 2 + .../mapbox/mapboxsdk/maps/NativeMapView.java | 10 ++ .../com/mapbox/mapboxsdk/maps/Projection.java | 13 ++ .../mapboxsdk/maps/VisibleRegionTest.kt | 118 ++++++++++++++++++ vendor/mapbox-gl-native | 2 +- 7 files changed, 148 insertions(+), 5 deletions(-) diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java index 516a9220e..d31fe187f 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/LatLngBounds.java @@ -373,7 +373,7 @@ public class LatLngBounds implements Parcelable { private boolean containsLongitude(final double longitude) { return (longitude <= this.longitudeEast) - && (longitude >= this.longitudeWest); + && (longitude >= this.longitudeWest); } /** diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java index 706bbcd65..09d0bea88 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/geometry/VisibleRegion.java @@ -54,10 +54,10 @@ public class VisibleRegion implements Parcelable { /** * Creates a VisibleRegion given the four corners of the camera. * - * @param farLeft A LatLng object containing the latitude and longitude of the near left corner of the region. - * @param farRight A LatLng object containing the latitude and longitude of the near left corner of the region. + * @param farLeft A LatLng object containing the latitude and longitude of the far left corner of the region. + * @param farRight A LatLng object containing the latitude and longitude of the far right corner of the region. * @param nearLeft A LatLng object containing the latitude and longitude of the near left corner of the region. - * @param nearRight A LatLng object containing the latitude and longitude of the near left corner of the region. + * @param nearRight A LatLng object containing the latitude and longitude of the near right corner of the region. * @param latLngBounds The smallest bounding box that includes the visible region defined in this class. */ public VisibleRegion(LatLng farLeft, LatLng farRight, LatLng nearLeft, LatLng nearRight, LatLngBounds latLngBounds) { diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java index 806421d01..a601096a6 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMap.java @@ -195,6 +195,8 @@ interface NativeMap { void pixelsForLatLngs(@NonNull double[] input, @NonNull double[] output); + void getVisibleCoordinateBounds(@NonNull double[] output); + LatLng latLngForPixel(@NonNull PointF pixel); void latLngsForPixels(@NonNull double[] input, @NonNull double[] output); diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java index 66b0538d6..dfeac768c 100755 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/NativeMapView.java @@ -650,6 +650,13 @@ final class NativeMapView implements NativeMap { } } + @Override + public void getVisibleCoordinateBounds(@NonNull double[] output) { + if (!checkState("getVisibleCoordinateBounds")) { + nativeGetVisibleCoordinateBounds(output); + } + } + @Override public LatLng latLngForPixel(@NonNull PointF pixel) { if (checkState("latLngForPixel")) { @@ -1282,6 +1289,9 @@ final class NativeMapView implements NativeMap { @Keep private native void nativePixelsForLatLngs(double[] input, double[] output, float pixelRatio); + @Keep + private native void nativeGetVisibleCoordinateBounds(double[] output); + @NonNull @Keep private native LatLng nativeLatLngForPixel(float x, float y); diff --git a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java index 66124edee..7de543302 100644 --- a/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java +++ b/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/maps/Projection.java @@ -203,6 +203,19 @@ public class Projection { LatLngBounds.from(north, east, south, west)); } + /** + * Gets a projection of the viewing frustum for converting between screen coordinates and + * geo-latitude/longitude coordinate bounds. + *

+ * This method ignores the content padding. + * + * @param bounds an array of 4 output values representing bounds(in the order of latNorth, + * lonEast, latSouth, lonWest). + */ + public void getVisibleCoordinateBounds(@NonNull double[] bounds) { + nativeMapView.getVisibleCoordinateBounds(bounds); + } + /** * Takes two {@link Point}s and finds the geographic bearing between them. * diff --git a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt index 245d43378..924dd9986 100644 --- a/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt +++ b/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/maps/VisibleRegionTest.kt @@ -4,6 +4,7 @@ import android.graphics.PointF import androidx.test.espresso.UiController import com.mapbox.mapboxsdk.camera.CameraUpdateFactory import com.mapbox.mapboxsdk.geometry.LatLng +import com.mapbox.mapboxsdk.geometry.LatLngBounds import com.mapbox.mapboxsdk.testapp.action.MapboxMapAction.invoke import com.mapbox.mapboxsdk.testapp.activity.BaseTest import com.mapbox.mapboxsdk.testapp.activity.espresso.PixelTestActivity @@ -394,6 +395,123 @@ class VisibleRegionTest : BaseTest() { } } + @Test + fun visibleRegionWithBoundsEqualTest() { + validateTestSetup() + invoke(mapboxMap) { _: UiController, mapboxMap: MapboxMap -> + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0)) + val latLngs = listOf( + mapboxMap.getLatLngFromScreenCoords(0f, 0f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f), + mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f), + mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()), + mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()), + mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f) + ) + val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0) + mapboxMap.projection.getVisibleCoordinateBounds(bounds) + val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3]) + val visibleRegion = mapboxMap.projection.visibleRegion + assertTrue(latLngBounds == visibleRegion.latLngBounds) + assertTrue(latLngs.all { latLngBounds.contains(it) }) + } + } + + @Test + fun visibleRegionBoundsOverDatelineTest() { + validateTestSetup() + invoke(mapboxMap) { _: UiController, mapboxMap: MapboxMap -> + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0)) + val latLngs = listOf( + mapboxMap.getLatLngFromScreenCoords(0f, 0f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, 0f), + mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), 0f) + .also { it.longitude += 360 }, + mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height / 2f) + .also { it.longitude += 360 }, + mapboxMap.getLatLngFromScreenCoords(mapView.width.toFloat(), mapView.height.toFloat()) + .also { it.longitude += 360 }, + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height.toFloat()), + mapboxMap.getLatLngFromScreenCoords(0f, mapView.height.toFloat()), + mapboxMap.getLatLngFromScreenCoords(0f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f) + ) + val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0) + mapboxMap.projection.getVisibleCoordinateBounds(bounds) + val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3]) + assertTrue(latLngs.all { latLngBounds.contains(it) }) + } + } + + @Test + fun visibleRegionBoundsOverDatelineLatitudeZeroTest() { + validateTestSetup() + invoke(mapboxMap) { _: UiController, mapboxMap: MapboxMap -> + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 180.0), 8.0)) + val shift = mapboxMap.getLatLngFromScreenCoords(0f, 0f) + mapboxMap.moveCamera(CameraUpdateFactory.newLatLng(LatLng(0.0, 180.0 - shift.longitude))) + + val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0) + mapboxMap.projection.getVisibleCoordinateBounds(bounds) + val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3]) + val visibleRegion = mapboxMap.projection.visibleRegion + assertTrue(latLngBounds == visibleRegion.latLngBounds) + } + } + + @Test + fun visibleRotatedRegionBoundEqualTest() { + validateTestSetup() + invoke(mapboxMap) { _: UiController, mapboxMap: MapboxMap -> + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 0.0), 8.0)) + val d = Math.min(mapboxMap.width, mapboxMap.height) / 4 + val latLngs = listOf( + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f - d / 2f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f + d / 2f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f - d / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f + d / 2f) + ) + + for (bearing in 45 until 360 step 45) { + mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(bearing.toDouble())) + val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0) + mapboxMap.projection.getVisibleCoordinateBounds(bounds) + val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3]) + val visibleRegion = mapboxMap.projection.visibleRegion + assertTrue(latLngBounds == visibleRegion.latLngBounds) + assertTrue(latLngs.all { latLngBounds.contains(it) }) + } + } + } + + @Test + fun visibleRotatedRegionBoundsOverDatelineTest() { + validateTestSetup() + invoke(mapboxMap) { _: UiController, mapboxMap: MapboxMap -> + mapboxMap.moveCamera(CameraUpdateFactory.newLatLngZoom(LatLng(0.0, 179.0), 8.0)) + val d = Math.min(mapboxMap.width, mapboxMap.height) / 4 + val latLngs = listOf( + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f - d / 2f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f + d / 2f, mapView.height / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f - d / 2f), + mapboxMap.getLatLngFromScreenCoords(mapView.width / 2f, mapView.height / 2f + d / 2f) + ) + + for (bearing in 45 until 360 step 45) { + mapboxMap.moveCamera(CameraUpdateFactory.bearingTo(bearing.toDouble())) + val bounds = doubleArrayOf(0.0, 0.0, 0.0, 0.0) + mapboxMap.projection.getVisibleCoordinateBounds(bounds) + val latLngBounds = LatLngBounds.from(bounds[0], bounds[1], bounds[2], bounds[3]) + assertTrue(latLngs.all { latLngBounds.contains(it) }) + } + } + } + private fun MapboxMap.getLatLngFromScreenCoords(x: Float, y: Float): LatLng { return this.projection.fromScreenLocation(PointF(x, y)) } diff --git a/vendor/mapbox-gl-native b/vendor/mapbox-gl-native index d148908cf..236afc039 160000 --- a/vendor/mapbox-gl-native +++ b/vendor/mapbox-gl-native @@ -1 +1 @@ -Subproject commit d148908cf14c78505ebc924a1783bbd44de0e399 +Subproject commit 236afc03958cc729b3b8121a1c5a72660f0e9fa2