From 466ed55200ffb3a9bc5954b59d87c7dd5630e5ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Minh=20Nguye=CC=82=CC=83n?= Date: Thu, 21 Apr 2016 17:23:09 -0700 Subject: [PATCH] [core, ios, osx] cameraThatFitsCoordinateBounds Added an API to get a camera that you can pass into -[MGLMapView setCamera:] that fits the given coordinate bounds, by analogy with -[MKMapView regionThatFits:] or -[MKMapView mapRectThatFits:edgePadding:]. Added mbgl::Map::getCameraOptions() for getting the current camera options more conveniently. --- include/mbgl/map/map.hpp | 5 ++-- platform/ios/CHANGELOG.md | 1 + platform/ios/include/MGLMapView.h | 25 ++++++++++++++++ platform/ios/src/MGLMapView.mm | 33 +++++++++++++++----- platform/osx/include/MGLMapView.h | 35 ++++++++++++++++++++++ platform/osx/osx.xcodeproj/project.pbxproj | 8 ----- platform/osx/src/MGLMapView.mm | 35 +++++++++++++++++----- src/mbgl/map/map.cpp | 8 +++-- src/mbgl/map/transform.cpp | 10 +++++++ src/mbgl/map/transform.hpp | 2 ++ 10 files changed, 134 insertions(+), 28 deletions(-) diff --git a/include/mbgl/map/map.hpp b/include/mbgl/map/map.hpp index e33b8e49f3d..0b1720e65bd 100644 --- a/include/mbgl/map/map.hpp +++ b/include/mbgl/map/map.hpp @@ -69,6 +69,7 @@ class Map : private util::noncopyable { bool isPanning() const; // Camera + CameraOptions getCameraOptions(optional) const; void jumpTo(const CameraOptions&); void easeTo(const CameraOptions&, const AnimationOptions&); void flyTo(const CameraOptions&, const AnimationOptions&); @@ -90,8 +91,8 @@ class Map : private util::noncopyable { double getZoom() const; void setLatLngZoom(const LatLng&, double zoom, const Duration& = Duration::zero()); void setLatLngZoom(const LatLng&, double zoom, optional, const Duration& = Duration::zero()); - CameraOptions cameraForLatLngBounds(const LatLngBounds&, optional); - CameraOptions cameraForLatLngs(const std::vector&, optional); + CameraOptions cameraForLatLngBounds(const LatLngBounds&, optional) const; + CameraOptions cameraForLatLngs(const std::vector&, optional) const; void resetZoom(); void setMinZoom(const double minZoom); double getMinZoom() const; diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index ba1c6351750..3af7c24b02f 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -13,6 +13,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CON - Rendering now occurs on the main thread, fixing a hang when calling `-[MGLMapView styleURL]` before the map view has fully loaded or while the application is in the background. ([#2909](https://github.com/mapbox/mapbox-gl-native/pull/2909)) - Added category methods on NSValue for converting to and from the structure types defined in MGLGeometry.h. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802)) - Added NSFormatter subclasses for converting geographic coordinates and directions into display strings. ([#4802](https://github.com/mapbox/mapbox-gl-native/pull/4802)) +- Added a new method, `-[MGLMapView cameraThatFitsCoordinateBounds:]`, to get a camera that you can pass into `-setCamera:` that fits the given coordinate bounds. ([#4790](https://github.com/mapbox/mapbox-gl-native/pull/4790)) - Added a `-reloadStyle:` action to MGLMapView to force a reload of the current style. ([#4728](https://github.com/mapbox/mapbox-gl-native/pull/4728)) - A more specific user agent string is now sent with style and tile requests. ([#4012](https://github.com/mapbox/mapbox-gl-native/pull/4012)) - Mapbox Telemetry is automatically disabled while the host application is running in the iOS Simulator. ([#4726](https://github.com/mapbox/mapbox-gl-native/pull/4726)) diff --git a/platform/ios/include/MGLMapView.h b/platform/ios/include/MGLMapView.h index 8518018fd9f..0339a5457b6 100644 --- a/platform/ios/include/MGLMapView.h +++ b/platform/ios/include/MGLMapView.h @@ -732,6 +732,31 @@ IB_DESIGNABLE */ - (void)flyToCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration peakAltitude:(CLLocationDistance)peakAltitude completionHandler:(nullable void (^)(void))completion; +/** + Returns the camera that best fits the given coordinate bounds. + + @param bounds The coordinate bounds to fit to the receiver’s viewport. + @return A camera object centered on the same location as the coordinate + bounds with zoom level as high (close to the ground) as possible while still + including the entire coordinate bounds. The camera object uses the current + direction and pitch. + */ +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds; + +/** + Returns the camera that best fits the given coordinate bounds, optionally with + some additional padding on each side. + + @param bounds The coordinate bounds to fit to the receiver’s viewport. + @param insets The minimum padding (in screen points) that would be visible + around the returned camera object if it were set as the receiver’s camera. + @return A camera object centered on the same location as the coordinate bounds + with zoom level as high (close to the ground) as possible while still + including the entire coordinate bounds. The camera object uses the current + direction and pitch. + */ +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets; + /** The distance from the edges of the map view’s frame to the edges of the map view’s logical viewport. diff --git a/platform/ios/src/MGLMapView.mm b/platform/ios/src/MGLMapView.mm index 5ae2d79fc41..65db976a244 100644 --- a/platform/ios/src/MGLMapView.mm +++ b/platform/ios/src/MGLMapView.mm @@ -2023,14 +2023,8 @@ - (void)setDirection:(CLLocationDirection)direction - (MGLMapCamera *)camera { - CGFloat pitch = _mbglMap->getPitch(); - CLLocationDistance altitude = MGLAltitudeForZoomLevel(self.zoomLevel, pitch, - self.centerCoordinate.latitude, - self.frame.size); - return [MGLMapCamera cameraLookingAtCenterCoordinate:self.centerCoordinate - fromDistance:altitude - pitch:pitch - heading:self.direction]; + mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInset); + return [self cameraForCameraOptions:_mbglMap->getCameraOptions(padding)]; } - (void)setCamera:(MGLMapCamera *)camera @@ -2130,6 +2124,29 @@ - (void)_flyToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets wit [self didChangeValueForKey:@"camera"]; } +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds +{ + return [self cameraThatFitsCoordinateBounds:bounds edgePadding:UIEdgeInsetsZero]; +} + +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(UIEdgeInsets)insets +{ + mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets); + padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInset); + mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngBounds(MGLLatLngBoundsFromCoordinateBounds(bounds), padding); + return [self cameraForCameraOptions:cameraOptions]; +} + +- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions +{ + CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng()); + double zoomLevel = cameraOptions.zoom ? *cameraOptions.zoom : self.zoomLevel; + CLLocationDirection direction = cameraOptions.angle ? *cameraOptions.angle : self.direction; + CGFloat pitch = cameraOptions.pitch ? *cameraOptions.pitch : _mbglMap->getPitch(); + CLLocationDistance altitude = MGLAltitudeForZoomLevel(zoomLevel, pitch, centerCoordinate.latitude, self.frame.size); + return [MGLMapCamera cameraLookingAtCenterCoordinate:centerCoordinate fromDistance:altitude pitch:pitch heading:direction]; +} + /// Returns a CameraOptions object that specifies parameters for animating to /// the given camera. - (mbgl::CameraOptions)cameraOptionsObjectForAnimatingToCamera:(MGLMapCamera *)camera edgePadding:(UIEdgeInsets)insets diff --git a/platform/osx/include/MGLMapView.h b/platform/osx/include/MGLMapView.h index f6ba86fb050..a8fd24ab5bb 100644 --- a/platform/osx/include/MGLMapView.h +++ b/platform/osx/include/MGLMapView.h @@ -325,6 +325,41 @@ IB_DESIGNABLE and zooming or `NO` to immediately display the given bounds. */ - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds animated:(BOOL)animated; +/** Changes the receiver’s viewport to fit the given coordinate bounds and + optionally some additional padding on each side. + + @param bounds The bounds that the viewport will show in its entirety. + @param insets The minimum padding (in screen points) that will be visible + around the given coordinate bounds. + @param animated Specify `YES` to animate the change by smoothly scrolling and + zooming or `NO` to immediately display the given bounds. + */ +- (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets animated:(BOOL)animated; + +/** Returns the camera that best fits the given coordinate bounds. + + @param bounds The coordinate bounds to fit to the receiver’s viewport. + @return A camera object centered on the same location as the coordinate + bounds with zoom level as high (close to the ground) as possible while + still including the entire coordinate bounds. The camera object uses the + current direction and pitch. + */ +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds; + +/** Returns the camera that best fits the given coordinate bounds, optionally + with some additional padding on each side. + + @param bounds The coordinate bounds to fit to the receiver’s viewport. + @param insets The minimum padding (in screen points) that would be visible + around the returned camera object if it were set as the receiver’s + camera. + @return A camera object centered on the same location as the coordinate + bounds with zoom level as high (close to the ground) as possible while + still including the entire coordinate bounds. The camera object uses the + current direction and pitch. + */ +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets; + /** A Boolean value indicating whether the receiver automatically adjusts its content insets. diff --git a/platform/osx/osx.xcodeproj/project.pbxproj b/platform/osx/osx.xcodeproj/project.pbxproj index 456625f5742..67fd7a20641 100644 --- a/platform/osx/osx.xcodeproj/project.pbxproj +++ b/platform/osx/osx.xcodeproj/project.pbxproj @@ -284,7 +284,6 @@ DA839E891CC2E3400062CAFB = { isa = PBXGroup; children = ( - DAE6C3C31CC31F6900DB3429 /* Configuration */, DA839E941CC2E3400062CAFB /* Demo App */, DAE6C3291CC30DB200DB3429 /* SDK */, DAE6C3371CC30DB200DB3429 /* SDK Tests */, @@ -461,13 +460,6 @@ name = Kit; sourceTree = ""; }; - DAE6C3C31CC31F6900DB3429 /* Configuration */ = { - isa = PBXGroup; - children = ( - ); - name = Configuration; - sourceTree = ""; - }; DAE6C3C41CC31F7800DB3429 /* Configuration */ = { isa = PBXGroup; children = ( diff --git a/platform/osx/src/MGLMapView.mm b/platform/osx/src/MGLMapView.mm index c707ef25719..5a1a108feeb 100644 --- a/platform/osx/src/MGLMapView.mm +++ b/platform/osx/src/MGLMapView.mm @@ -1003,14 +1003,8 @@ - (void)offsetDirectionBy:(CLLocationDegrees)delta animated:(BOOL)animated { } - (MGLMapCamera *)camera { - CGFloat pitch = _mbglMap->getPitch(); - CLLocationDistance altitude = MGLAltitudeForZoomLevel(self.zoomLevel, pitch, - self.centerCoordinate.latitude, - self.frame.size); - return [MGLMapCamera cameraLookingAtCenterCoordinate:self.centerCoordinate - fromDistance:altitude - pitch:pitch - heading:self.direction]; + mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets); + return [self cameraForCameraOptions:_mbglMap->getCameraOptions(padding)]; } - (void)setCamera:(MGLMapCamera *)camera { @@ -1142,6 +1136,31 @@ - (void)setVisibleCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEd _mbglMap->easeTo(cameraOptions, animationOptions); } +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds { + return [self cameraThatFitsCoordinateBounds:bounds edgePadding:NSEdgeInsetsZero]; +} + +- (MGLMapCamera *)cameraThatFitsCoordinateBounds:(MGLCoordinateBounds)bounds edgePadding:(NSEdgeInsets)insets { + mbgl::EdgeInsets padding = MGLEdgeInsetsFromNSEdgeInsets(insets); + padding += MGLEdgeInsetsFromNSEdgeInsets(self.contentInsets); + mbgl::CameraOptions cameraOptions = _mbglMap->cameraForLatLngBounds(MGLLatLngBoundsFromCoordinateBounds(bounds), padding); + return [self cameraForCameraOptions:cameraOptions]; +} + +- (MGLMapCamera *)cameraForCameraOptions:(const mbgl::CameraOptions &)cameraOptions { + CLLocationCoordinate2D centerCoordinate = MGLLocationCoordinate2DFromLatLng(cameraOptions.center ? *cameraOptions.center : _mbglMap->getLatLng()); + double zoomLevel = cameraOptions.zoom ? *cameraOptions.zoom : self.zoomLevel; + CLLocationDirection direction = cameraOptions.angle ? *cameraOptions.angle : self.direction; + CGFloat pitch = cameraOptions.pitch ? *cameraOptions.pitch : _mbglMap->getPitch(); + CLLocationDistance altitude = MGLAltitudeForZoomLevel(zoomLevel, pitch, + centerCoordinate.latitude, + self.frame.size); + return [MGLMapCamera cameraLookingAtCenterCoordinate:centerCoordinate + fromDistance:altitude + pitch:pitch + heading:direction]; +} + - (void)setAutomaticallyAdjustsContentInsets:(BOOL)automaticallyAdjustsContentInsets { _automaticallyAdjustsContentInsets = automaticallyAdjustsContentInsets; [self adjustContentInsets]; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index aab09a98e9d..46eaa5049bc 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -375,6 +375,10 @@ bool Map::isPanning() const { } #pragma mark - + +CameraOptions Map::getCameraOptions(optional padding) const { + return impl->transform.getCameraOptions(padding); +} void Map::jumpTo(const CameraOptions& camera) { impl->transform.jumpTo(camera); @@ -466,7 +470,7 @@ void Map::setLatLngZoom(const LatLng& latLng, double zoom, optional update(Update::RecalculateStyle); } -CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional padding) { +CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional padding) const { AnnotationSegment segment = { bounds.northwest(), bounds.southwest(), @@ -476,7 +480,7 @@ CameraOptions Map::cameraForLatLngBounds(const LatLngBounds& bounds, optional& latLngs, optional padding) { +CameraOptions Map::cameraForLatLngs(const std::vector& latLngs, optional padding) const { CameraOptions options; if (latLngs.empty()) { return options; diff --git a/src/mbgl/map/transform.cpp b/src/mbgl/map/transform.cpp index 136209c937e..7dcfee65dc4 100644 --- a/src/mbgl/map/transform.cpp +++ b/src/mbgl/map/transform.cpp @@ -62,6 +62,16 @@ bool Transform::resize(const std::array size) { #pragma mark - Camera +CameraOptions Transform::getCameraOptions(optional padding) const { + CameraOptions camera; + camera.center = getLatLng(padding); + camera.padding = padding; + camera.zoom = getZoom(); + camera.angle = getAngle(); + camera.pitch = getPitch(); + return camera; +} + /** * Change any combination of center, zoom, bearing, and pitch, without * a transition. The map will retain the current values for any options diff --git a/src/mbgl/map/transform.hpp b/src/mbgl/map/transform.hpp index 79421117b6d..7d3bdceb998 100644 --- a/src/mbgl/map/transform.hpp +++ b/src/mbgl/map/transform.hpp @@ -26,6 +26,8 @@ class Transform : private util::noncopyable { bool resize(std::array size); // Camera + /** Returns the current camera options. */ + CameraOptions getCameraOptions(optional) const; /** Instantaneously, synchronously applies the given camera options. */ void jumpTo(const CameraOptions&);