From 236afc03958cc729b3b8121a1c5a72660f0e9fa2 Mon Sep 17 00:00:00 2001 From: Peng Liu Date: Thu, 9 Jan 2020 15:53:23 +0200 Subject: [android][core] Add `Map::latLngBoundsForCameraUnwrapped` and jni binding for `getVisibleCoordinateBounds`. (#16069) * [android] Add getVisibleCoordinateBounds method. * Fix Map::latLngBoundsForCamera, add Android binding for getVisibleRegionBounds. * Add unit tests for CameraToLatLngBoundsWithRotation and CameraToLatLngBoundsCrossDateLine. * Move API breaking changes to a new method name latLngBoundsForCameraUnwrapped. --- include/mbgl/map/map.hpp | 1 + platform/android/src/native_map_view.cpp | 21 ++++++++++++++++ platform/android/src/native_map_view.hpp | 2 ++ src/mbgl/map/map.cpp | 21 ++++++++++++++++ test/map/map.test.cpp | 42 ++++++++++++++++++++++++++++++++ 5 files changed, 87 insertions(+) mode change 100755 => 100644 platform/android/src/native_map_view.hpp diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index a3794962c6..62bb39e8c2 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -72,6 +72,7 @@ public: CameraOptions cameraForLatLngs(const std::vector&, const EdgeInsets&, optional bearing = {}, optional pitch = {}) const; CameraOptions cameraForGeometry(const Geometry&, const EdgeInsets&, optional bearing = {}, optional pitch = {}) const; LatLngBounds latLngBoundsForCamera(const CameraOptions&) const; + LatLngBounds latLngBoundsForCameraUnwrapped(const CameraOptions&) const; /// @name Bounds /// @{ diff --git a/platform/android/src/native_map_view.cpp b/platform/android/src/native_map_view.cpp index 554a59de76..d7351c7677 100644 --- a/platform/android/src/native_map_view.cpp +++ b/platform/android/src/native_map_view.cpp @@ -529,6 +529,26 @@ void NativeMapView::setVisibleCoordinateBounds(JNIEnv& env, const jni::ArrayeaseTo(cameraOptions, animationOptions); } +void NativeMapView::getVisibleCoordinateBounds(JNIEnv& env, jni::Array& output) { + auto latlngBounds = map->latLngBoundsForCameraUnwrapped(map->getCameraOptions(mbgl::nullopt)); + + double latNorth = latlngBounds.north(); + double lonEast = latlngBounds.east(); + double latSouth = latlngBounds.south(); + double lonWest = latlngBounds.west(); + + std::vector buffer; + buffer.reserve(4); + + // Order of the LatLngBounds: double latNorth, double lonEast, double latSouth, double lonWest + buffer.push_back(latNorth); + buffer.push_back(lonEast); + buffer.push_back(latSouth); + buffer.push_back(lonWest); + + output.SetRegion>(env, 0, buffer); +} + void NativeMapView::scheduleSnapshot(jni::JNIEnv&) { mapRenderer.requestSnapshot([&](PremultipliedImage image) { auto _env = android::AttachEnv(); @@ -1172,6 +1192,7 @@ void NativeMapView::registerNative(jni::JNIEnv& env) { METHOD(&NativeMapView::projectedMetersForLatLng, "nativeProjectedMetersForLatLng"), METHOD(&NativeMapView::pixelForLatLng, "nativePixelForLatLng"), METHOD(&NativeMapView::pixelsForLatLngs, "nativePixelsForLatLngs"), + METHOD(&NativeMapView::getVisibleCoordinateBounds, "nativeGetVisibleCoordinateBounds"), METHOD(&NativeMapView::latLngForProjectedMeters, "nativeLatLngForProjectedMeters"), METHOD(&NativeMapView::latLngForPixel, "nativeLatLngForPixel"), METHOD(&NativeMapView::latLngsForPixels, "nativeLatLngsForPixels"), diff --git a/platform/android/src/native_map_view.hpp b/platform/android/src/native_map_view.hpp old mode 100755 new mode 100644 index 622c454b7d..ab93fab81f --- a/platform/android/src/native_map_view.hpp +++ b/platform/android/src/native_map_view.hpp @@ -139,6 +139,8 @@ public: void setVisibleCoordinateBounds(JNIEnv&, const jni::Array>&, const jni::Object&, jni::jdouble, jni::jlong); + void getVisibleCoordinateBounds(JNIEnv& env, jni::Array& output); + void scheduleSnapshot(jni::JNIEnv&); jni::Local> getCameraPosition(jni::JNIEnv&); diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 9cd9fdc15f..a994af305f 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -256,6 +256,27 @@ LatLngBounds Map::latLngBoundsForCamera(const CameraOptions& camera) const { ); } +LatLngBounds Map::latLngBoundsForCameraUnwrapped(const CameraOptions& camera) const { + Transform shallow{impl->transform.getState()}; + Size size = shallow.getState().getSize(); + + shallow.jumpTo(camera); + LatLng nw = shallow.screenCoordinateToLatLng({}); + LatLng se = shallow.screenCoordinateToLatLng({double(size.width), double(size.height)}); + LatLng ne = shallow.screenCoordinateToLatLng({double(size.width), 0.0}); + LatLng sw = shallow.screenCoordinateToLatLng({0.0, double(size.height)}); + LatLng center = shallow.screenCoordinateToLatLng({double(size.width) / 2, double(size.height) / 2}); + nw.unwrapForShortestPath(center); + se.unwrapForShortestPath(center); + ne.unwrapForShortestPath(center); + sw.unwrapForShortestPath(center); + LatLngBounds bounds = LatLngBounds::hull(nw, se); + bounds.extend(ne); + bounds.extend(sw); + bounds.extend(center); + return bounds; +} + #pragma mark - Bounds void Map::setBounds(const BoundOptions& options) { diff --git a/test/map/map.test.cpp b/test/map/map.test.cpp index 31cc85619e..a0cd64bdd5 100644 --- a/test/map/map.test.cpp +++ b/test/map/map.test.cpp @@ -252,6 +252,48 @@ TEST(Map, CameraToLatLngBounds) { ASSERT_NEAR(camera.center->longitude(), virtualCamera.center->longitude(), 1e-7); } +TEST(Map, CameraToLatLngBoundsUnwrappedWithRotation) { + MapTest<> test; + + test.map.jumpTo(CameraOptions().withCenter(LatLng{45, 90}).withZoom(16.0).withBearing(45.0)); + + const Size size = test.map.getMapOptions().size(); + + CameraOptions camera = test.map.getCameraOptions(); + + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains(test.map.latLngForPixel({}))); + ASSERT_TRUE( + test.map.latLngBoundsForCameraUnwrapped(camera).contains(test.map.latLngForPixel({0.0, double(size.height)}))); + ASSERT_TRUE( + test.map.latLngBoundsForCameraUnwrapped(camera).contains(test.map.latLngForPixel({double(size.width), 0.0}))); + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains( + test.map.latLngForPixel({double(size.width), double(size.height)}))); + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains( + test.map.latLngForPixel({double(size.width) / 2, double(size.height) / 2}))); +} + +TEST(Map, CameraToLatLngBoundsUnwrappedCrossDateLine) { + MapTest<> test; + + test.map.jumpTo(CameraOptions().withCenter(LatLng{0, 180}).withZoom(16.0)); + + const Size size = test.map.getMapOptions().size(); + + CameraOptions camera = test.map.getCameraOptions(); + + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains(test.map.latLngForPixel({}), LatLng::Wrapped)); + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains( + test.map.latLngForPixel({0.0, double(size.height)}), LatLng::Wrapped)); + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains( + test.map.latLngForPixel({double(size.width), 0.0}), LatLng::Wrapped)); + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains( + test.map.latLngForPixel({double(size.width), double(size.height)}), LatLng::Wrapped)); + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).contains( + test.map.latLngForPixel({double(size.width) / 2, double(size.height) / 2}))); + + ASSERT_TRUE(test.map.latLngBoundsForCameraUnwrapped(camera).crossesAntimeridian()); +} + TEST(Map, Offline) { MapTest test {":memory:", "."}; -- cgit v1.2.1