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.
This commit is contained in:
Peng Liu
2020-01-09 19:38:29 +02:00
committed by GitHub
parent 6470565b66
commit 7c9a8fc556
7 changed files with 148 additions and 5 deletions

View File

@@ -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);
}
/**

View File

@@ -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) {

View File

@@ -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);

View File

@@ -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);

View File

@@ -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.
* <p>
* 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.
*

View File

@@ -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))
}