Skip to content
This repository was archived by the owner on Aug 8, 2023. It is now read-only.

Commit 0a172a2

Browse files
1ec5incanus
authored andcommitted
CameraOptions
Plumbed camera options all the way through to MGLMapView. Added a method that lets you specify a direction in addition to center point and zoom level. Added Map::jumpTo() for parity with mapbox-gl-js. Replaced usage of Map::setLatLng() and Map::setLatLngZoom() with Map::jumpTo() or Map::easeTo() within MGLMapView. Replaced MGLMapView.pitch with MGLMapCamera for setting all supported degrees of freedom simultaneously. Simultaneously move and rotate with course. Support customizable timing functions on iOS. iosapp now persists an archived MGLMapCamera instead of separate viewpoint properties and also synchronizes user defaults on termination. This change implements persistence entirely in Objective-C, eliminating the use of the Objective-C++ implementation. Fixes #1643, fixes #1834. Ref #1581.
1 parent 584c36f commit 0a172a2

16 files changed

+588
-203
lines changed

gyp/platform-ios.gypi

+2
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@
2020
'../include/mbgl/ios/Mapbox.h',
2121
'../platform/ios/MGLMapboxEvents.h',
2222
'../platform/ios/MGLMapboxEvents.m',
23+
'../include/mbgl/ios/MGLMapCamera.h',
24+
'../platform/ios/MGLMapCamera.mm',
2325
'../include/mbgl/ios/MGLMapView.h',
2426
'../include/mbgl/ios/MGLMapView+IBAdditions.h',
2527
'../platform/ios/MGLMapView.mm',

include/mbgl/ios/MGLMapCamera.h

+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
#import "Mapbox.h"
2+
3+
#pragma once
4+
5+
NS_ASSUME_NONNULL_BEGIN
6+
7+
/** An `MGLMapCamera` object represents a viewpoint from which the user observes some point on an `MGLMapView`. */
8+
@interface MGLMapCamera : NSObject <NSSecureCoding, NSCopying>
9+
10+
/** Coordinate at the center of the map view. */
11+
@property (nonatomic) CLLocationCoordinate2D centerCoordinate;
12+
13+
/** Heading measured in degrees clockwise from true north. */
14+
@property (nonatomic) CLLocationDirection heading;
15+
16+
/** Pitch toward the horizon measured in degrees, with 0 degrees resulting in a two-dimensional map. */
17+
@property (nonatomic) CGFloat pitch;
18+
19+
/** Meters above ground level. */
20+
@property (nonatomic) CLLocationDistance altitude;
21+
22+
/** Returns a new camera with all properties set to 0. */
23+
+ (instancetype)camera;
24+
25+
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
26+
fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
27+
eyeAltitude:(CLLocationDistance)eyeAltitude;
28+
29+
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
30+
fromDistance:(CLLocationDistance)distance
31+
pitch:(CGFloat)pitch
32+
heading:(CLLocationDirection)heading;
33+
34+
@end
35+
36+
NS_ASSUME_NONNULL_END

include/mbgl/ios/MGLMapView.h

+15-11
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
#import "MGLGeometry.h"
2+
#import "MGLMapCamera.h"
23

34
#import <UIKit/UIKit.h>
45
#import <CoreLocation/CoreLocation.h>
56

67
NS_ASSUME_NONNULL_BEGIN
78

89
@class MGLAnnotationImage;
10+
@class MGLMapCamera;
911
@class MGLUserLocation;
1012
@class MGLPolyline;
1113
@class MGLPolygon;
@@ -136,6 +138,8 @@ IB_DESIGNABLE
136138
* @param animated Specify `YES` if you want the map view to animate scrolling and zooming to the new location or `NO` if you want the map to display the new location immediately. */
137139
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated;
138140

141+
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction animated:(BOOL)animated;
142+
139143
/** The coordinate bounds visible in the receiver’s viewport.
140144
*
141145
* Changing the value of this property updates the receiver immediately. If you want to animate the change, call `setVisibleCoordinateBounds:animated:` instead. */
@@ -181,19 +185,19 @@ IB_DESIGNABLE
181185
/** Resets the map rotation to a northern heading. */
182186
- (IBAction)resetNorth;
183187

184-
/** The pitch of the map (measured in degrees).
185-
*
186-
* The default value `0` shows a completely flat map. Maximum value is `60`. */
187-
@property (nonatomic) double pitch;
188+
/** A camera representing the current viewpoint of the map. */
189+
@property (nonatomic, copy) MGLMapCamera *camera;
188190

189-
/** Changes the pitch of the map.
190-
* @param pitch The pitch of the map (measured in degrees) relative to top-down.
191-
*
192-
* Changing the pitch tilts the map without changing the current center coordinate or zoom level. */
193-
- (void)setPitch:(double)pitch;
191+
/** Moves the viewpoint to a different location with respect to the map with an optional transition animation.
192+
* @param camera The new viewpoint.
193+
* @param animated Specify `YES` if you want the map view to animate the change to the new viewpoint or `NO` if you want the map to display the new viewpoint immediately. */
194+
- (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;
194195

195-
/** Resets the map pitch to head-on. */
196-
- (IBAction)resetPitch;
196+
/** Moves the viewpoint to a different location with respect to the map with an optional transition duration and timing function.
197+
* @param camera The new viewpoint.
198+
* @param duration The amount of time, measured in seconds, that the transition animation should take. Specify `0` to jump to the new viewpoint instantaneously.
199+
* @param function A timing function used for the animation. Set this parameter to `nil` for a transition that matches most system animations. If the duration is `0`, this parameter is ignored. */
200+
- (void)setCamera:(MGLMapCamera *)camera withDuration:(NSTimeInterval)duration animationTimingFunction:(nullable CAMediaTimingFunction *)function;
197201

198202
#pragma mark - Converting Map Coordinates
199203

include/mbgl/ios/Mapbox.h

+1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
#import "MGLAccountManager.h"
22
#import "MGLAnnotation.h"
33
#import "MGLAnnotationImage.h"
4+
#import "MGLMapCamera.h"
45
#import "MGLGeometry.h"
56
#import "MGLMapView.h"
67
#import "MGLMultiPoint.h"

include/mbgl/map/camera.hpp

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
#ifndef MBGL_MAP_CAMERA
2+
#define MBGL_MAP_CAMERA
3+
4+
#include <mbgl/util/geo.hpp>
5+
#include <mbgl/util/optional.hpp>
6+
#include <mbgl/util/chrono.hpp>
7+
#include <mbgl/util/unitbezier.hpp>
8+
9+
namespace mbgl {
10+
11+
struct CameraOptions {
12+
mapbox::util::optional<LatLng> center;
13+
mapbox::util::optional<double> zoom;
14+
mapbox::util::optional<double> angle;
15+
mapbox::util::optional<double> pitch;
16+
mapbox::util::optional<Duration> duration;
17+
mapbox::util::optional<mbgl::util::UnitBezier> easing;
18+
};
19+
20+
}
21+
22+
#endif /* MBGL_MAP_CAMERA */

include/mbgl/map/map.hpp

+8-3
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
#define MBGL_MAP_MAP
33

44
#include <mbgl/util/chrono.hpp>
5+
#include <mbgl/map/camera.hpp>
56
#include <mbgl/map/update.hpp>
67
#include <mbgl/map/mode.hpp>
78
#include <mbgl/util/geo.hpp>
@@ -94,6 +95,10 @@ class Map : private util::noncopyable {
9495
void cancelTransitions();
9596
void setGestureInProgress(bool);
9697

98+
// Camera
99+
void jumpTo(CameraOptions options);
100+
void easeTo(CameraOptions options);
101+
97102
// Position
98103
void moveBy(double dx, double dy, const Duration& = Duration::zero());
99104
void setLatLng(LatLng latLng, const Duration& = Duration::zero());
@@ -107,8 +112,8 @@ class Map : private util::noncopyable {
107112
void setZoom(double zoom, const Duration& = Duration::zero());
108113
double getZoom() const;
109114
void setLatLngZoom(LatLng latLng, double zoom, const Duration& = Duration::zero());
110-
void fitBounds(LatLngBounds bounds, EdgeInsets padding, const Duration& duration = Duration::zero());
111-
void fitBounds(AnnotationSegment segment, EdgeInsets padding, const Duration& duration = Duration::zero());
115+
CameraOptions cameraForLatLngBounds(LatLngBounds bounds, EdgeInsets padding);
116+
CameraOptions cameraForLatLngs(std::vector<LatLng> latLngs, EdgeInsets padding);
112117
void resetZoom();
113118
double getMinZoom() const;
114119
double getMaxZoom() const;
@@ -121,7 +126,7 @@ class Map : private util::noncopyable {
121126
void resetNorth();
122127

123128
// Pitch
124-
void setPitch(double pitch);
129+
void setPitch(double pitch, const Duration& = Duration::zero());
125130
double getPitch() const;
126131

127132
// Size
File renamed without changes.
File renamed without changes.

ios/app/MBXViewController.mm

+39-29
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22

33
#import <mbgl/ios/Mapbox.h>
44

5-
#import <mbgl/platform/darwin/settings_nsuserdefaults.hpp>
6-
75
#import <CoreLocation/CoreLocation.h>
86

97
static UIColor *const kTintColor = [UIColor colorWithRed:0.120 green:0.550 blue:0.670 alpha:1.000];
@@ -26,10 +24,20 @@ @interface MBXViewController () <UIActionSheetDelegate, MGLMapViewDelegate>
2624

2725
@implementation MBXViewController
2826

29-
mbgl::Settings_NSUserDefaults *settings = nullptr;
30-
3127
#pragma mark - Setup
3228

29+
+ (void)initialize
30+
{
31+
if (self == [MBXViewController class])
32+
{
33+
[[NSUserDefaults standardUserDefaults] registerDefaults:@{
34+
@"userTrackingMode": @(MGLUserTrackingModeNone),
35+
@"showsUserLocation": @NO,
36+
@"debug": @NO,
37+
}];
38+
}
39+
}
40+
3341
- (id)init
3442
{
3543
self = [super init];
@@ -38,6 +46,7 @@ - (id)init
3846
{
3947
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationDidEnterBackgroundNotification object:nil];
4048
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(restoreState:) name:UIApplicationWillEnterForegroundNotification object:nil];
49+
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(saveState:) name:UIApplicationWillTerminateNotification object:nil];
4150
}
4251

4352
return self;
@@ -73,7 +82,6 @@ - (void)viewDidLoad
7382
target:self
7483
action:@selector(locateUser)];
7584

76-
settings = new mbgl::Settings_NSUserDefaults();
7785
[self restoreState:nil];
7886

7987
if ( ! settings->showsUserLocation)
@@ -86,30 +94,37 @@ - (void)viewDidLoad
8694

8795
- (void)saveState:(__unused NSNotification *)notification
8896
{
89-
if (self.mapView && settings)
97+
if (self.mapView)
9098
{
91-
settings->longitude = self.mapView.centerCoordinate.longitude;
92-
settings->latitude = self.mapView.centerCoordinate.latitude;
93-
settings->zoom = self.mapView.zoomLevel;
94-
settings->bearing = self.mapView.direction;
95-
settings->pitch = self.mapView.pitch;
96-
settings->debug = self.mapView.isDebugActive;
97-
settings->userTrackingMode = self.mapView.userTrackingMode;
98-
settings->showsUserLocation = self.mapView.showsUserLocation;
99-
settings->save();
99+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
100+
NSData *archivedCamera = [NSKeyedArchiver archivedDataWithRootObject:self.mapView.camera];
101+
[defaults setObject:archivedCamera forKey:@"camera"];
102+
[defaults setInteger:self.mapView.userTrackingMode forKey:@"userTrackingMode"];
103+
[defaults setBool:self.mapView.showsUserLocation forKey:@"showsUserLocation"];
104+
[defaults setBool:self.mapView.debugActive forKey:@"debug"];
105+
[defaults synchronize];
100106
}
101107
}
102108

103109
- (void)restoreState:(__unused NSNotification *)notification
104110
{
105-
if (self.mapView && settings) {
106-
settings->load();
107-
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(settings->latitude, settings->longitude) zoomLevel:settings->zoom animated:NO];
108-
self.mapView.direction = settings->bearing;
109-
self.mapView.pitch = settings->pitch;
110-
self.mapView.userTrackingMode = settings->userTrackingMode;
111-
self.mapView.showsUserLocation = settings->showsUserLocation;
112-
[self.mapView setDebugActive:settings->debug];
111+
if (self.mapView) {
112+
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
113+
NSData *archivedCamera = [defaults objectForKey:@"camera"];
114+
MGLMapCamera *camera = archivedCamera ? [NSKeyedUnarchiver unarchiveObjectWithData:archivedCamera] : nil;
115+
if (camera)
116+
{
117+
self.mapView.camera = camera;
118+
}
119+
NSInteger uncheckedTrackingMode = [defaults integerForKey:@"userTrackingMode"];
120+
if (uncheckedTrackingMode >= 0 &&
121+
(NSUInteger)uncheckedTrackingMode >= MGLUserTrackingModeNone &&
122+
(NSUInteger)uncheckedTrackingMode <= MGLUserTrackingModeFollowWithCourse)
123+
{
124+
self.mapView.userTrackingMode = (MGLUserTrackingMode)uncheckedTrackingMode;
125+
}
126+
self.mapView.showsUserLocation = [defaults boolForKey:@"showsUserLocation"];
127+
self.mapView.debugActive = [defaults boolForKey:@"debug"];
113128
}
114129
}
115130

@@ -339,12 +354,7 @@ - (void)dealloc
339354
{
340355
[[NSNotificationCenter defaultCenter] removeObserver:self];
341356

342-
if (settings)
343-
{
344-
[self saveState:nil];
345-
delete settings;
346-
settings = nullptr;
347-
}
357+
[self saveState:nil];
348358
}
349359

350360
#pragma mark - MGLMapViewDelegate

ios/app/mapboxgl-app.gypi

-1
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@
3232
'./MBXAppDelegate.m',
3333
'./MBXViewController.h',
3434
'./MBXViewController.mm',
35-
'../../platform/darwin/settings_nsuserdefaults.mm',
3635
],
3736

3837
'xcode_settings': {

platform/ios/MGLMapCamera.mm

+101
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
#import "MGLMapCamera.h"
2+
3+
#include <mbgl/util/projection.hpp>
4+
5+
@implementation MGLMapCamera
6+
7+
+ (BOOL)supportsSecureCoding
8+
{
9+
return YES;
10+
}
11+
12+
+ (instancetype)camera
13+
{
14+
return [[self alloc] init];
15+
}
16+
17+
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
18+
fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
19+
eyeAltitude:(CLLocationDistance)eyeAltitude
20+
{
21+
mbgl::LatLng centerLatLng = mbgl::LatLng(centerCoordinate.latitude, centerCoordinate.longitude);
22+
mbgl::LatLng eyeLatLng = mbgl::LatLng(eyeCoordinate.latitude, eyeCoordinate.longitude);
23+
24+
mbgl::ProjectedMeters centerMeters = mbgl::Projection::projectedMetersForLatLng(centerLatLng);
25+
mbgl::ProjectedMeters eyeMeters = mbgl::Projection::projectedMetersForLatLng(eyeLatLng);
26+
CLLocationDirection heading = std::atan((centerMeters.northing - eyeMeters.northing) /
27+
(centerMeters.easting - eyeMeters.easting));
28+
29+
double groundDistance = std::hypot(centerMeters.northing - eyeMeters.northing,
30+
centerMeters.easting - eyeMeters.easting);
31+
CGFloat pitch = std::atan(eyeAltitude / groundDistance);
32+
33+
return [[self alloc] initWithCenterCoordinate:centerCoordinate
34+
altitude:eyeAltitude
35+
pitch:pitch
36+
heading:heading];
37+
}
38+
39+
+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
40+
fromDistance:(CLLocationDistance)distance
41+
pitch:(CGFloat)pitch
42+
heading:(CLLocationDirection)heading
43+
{
44+
return [[self alloc] initWithCenterCoordinate:centerCoordinate
45+
altitude:distance
46+
pitch:(CGFloat)pitch
47+
heading:heading];
48+
}
49+
50+
- (instancetype)initWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
51+
altitude:(CLLocationDistance)altitude
52+
pitch:(CGFloat)pitch
53+
heading:(CLLocationDirection)heading
54+
{
55+
if (self = [super init])
56+
{
57+
_centerCoordinate = centerCoordinate;
58+
_altitude = altitude;
59+
_pitch = pitch;
60+
_heading = heading;
61+
}
62+
return self;
63+
}
64+
65+
- (nullable instancetype)initWithCoder:(NSCoder *)coder
66+
{
67+
if (self = [super init])
68+
{
69+
_centerCoordinate = CLLocationCoordinate2DMake([coder decodeDoubleForKey:@"centerLatitude"],
70+
[coder decodeDoubleForKey:@"centerLongitude"]);
71+
_altitude = [coder decodeDoubleForKey:@"altitude"];
72+
_pitch = [coder decodeDoubleForKey:@"pitch"];
73+
_heading = [coder decodeDoubleForKey:@"heading"];
74+
}
75+
return self;
76+
}
77+
78+
- (void)encodeWithCoder:(NSCoder *)coder
79+
{
80+
[coder encodeDouble:_centerCoordinate.latitude forKey:@"centerLatitude"];
81+
[coder encodeDouble:_centerCoordinate.longitude forKey:@"centerLongitude"];
82+
[coder encodeDouble:_altitude forKey:@"altitude"];
83+
[coder encodeDouble:_pitch forKey:@"pitch"];
84+
[coder encodeDouble:_heading forKey:@"heading"];
85+
}
86+
87+
- (id)copyWithZone:(nullable NSZone *)zone
88+
{
89+
return [[[self class] allocWithZone:zone] initWithCenterCoordinate:_centerCoordinate
90+
altitude:_altitude
91+
pitch:_pitch
92+
heading:_heading];
93+
}
94+
95+
- (NSString *)description
96+
{
97+
return [NSString stringWithFormat:@"<MKMapCamera %p centerCoordinate:%f, %f altitude:%.0fm heading:%.0f° pitch:%.0f°>",
98+
self, _centerCoordinate.latitude, _centerCoordinate.longitude, _altitude, _heading, _pitch];
99+
}
100+
101+
@end

0 commit comments

Comments
 (0)