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

iOS Camera API #2159

Closed
wants to merge 16 commits into from
2 changes: 2 additions & 0 deletions gyp/platform-ios.gypi
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
'../include/mbgl/ios/Mapbox.h',
'../platform/ios/MGLMapboxEvents.h',
'../platform/ios/MGLMapboxEvents.m',
'../include/mbgl/ios/MGLMapCamera.h',
'../platform/ios/MGLMapCamera.mm',
'../include/mbgl/ios/MGLMapView.h',
'../include/mbgl/ios/MGLMapView+IBAdditions.h',
'../platform/ios/MGLMapView.mm',
Expand Down
36 changes: 36 additions & 0 deletions include/mbgl/ios/MGLMapCamera.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
#import "Mapbox.h"

#pragma once

NS_ASSUME_NONNULL_BEGIN

/** An `MGLMapCamera` object represents a viewpoint from which the user observes some point on an `MGLMapView`. */
@interface MGLMapCamera : NSObject <NSSecureCoding, NSCopying>

/** Coordinate at the center of the map view. */
@property (nonatomic) CLLocationCoordinate2D centerCoordinate;

/** Heading measured in degrees clockwise from true north. */
@property (nonatomic) CLLocationDirection heading;

/** Pitch toward the horizon measured in degrees, with 0 degrees resulting in a two-dimensional map. */
@property (nonatomic) CGFloat pitch;

/** Meters above ground level. */
@property (nonatomic) CLLocationDistance altitude;

/** Returns a new camera with all properties set to 0. */
+ (instancetype)camera;

+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
eyeAltitude:(CLLocationDistance)eyeAltitude;

+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
fromDistance:(CLLocationDistance)distance
pitch:(CGFloat)pitch
heading:(CLLocationDirection)heading;

@end

NS_ASSUME_NONNULL_END
19 changes: 14 additions & 5 deletions include/mbgl/ios/MGLMapView.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#import "MGLGeometry.h"
#import "MGLMapCamera.h"

#import <UIKit/UIKit.h>
#import <CoreLocation/CoreLocation.h>

NS_ASSUME_NONNULL_BEGIN

@class MGLAnnotationImage;
@class MGLMapCamera;
@class MGLUserLocation;
@class MGLPolyline;
@class MGLPolygon;
Expand Down Expand Up @@ -135,6 +137,8 @@ IB_DESIGNABLE
* @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. */
- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel animated:(BOOL)animated;

- (void)setCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate zoomLevel:(double)zoomLevel direction:(CLLocationDirection)direction animated:(BOOL)animated;

/** The coordinate bounds visible in the receiver’s viewport.
*
* Changing the value of this property updates the receiver immediately. If you want to animate the change, call `setVisibleCoordinateBounds:animated:` instead. */
Expand Down Expand Up @@ -183,17 +187,22 @@ IB_DESIGNABLE
/** The pitch of the map (measured in degrees).
*
* The default value `0` shows a completely flat map. Maximum value is `60`. */
@property (nonatomic) double pitch;
@property (nonatomic) CGFloat pitch;

/** Changes the pitch of the map.
* @param pitch The pitch of the map (measured in degrees) relative to top-down.
*
* Changing the pitch tilts the map without changing the current center coordinate or zoom level. */
- (void)setPitch:(double)pitch;
* @param pitch The pitch of the map (measured in degrees) relative to top-down.
* @param animated Specify `YES` if you want the map view to animate the change to the new pitch or `NO` if you want the map to display the new pitch immediately.
*
* Changing the pitch tilts the map without changing the current center coordinate or zoom level. */
- (void)setPitch:(CGFloat)pitch animated:(BOOL)animated;

/** Resets the map pitch to head-on. */
- (IBAction)resetPitch;

@property (nonatomic, copy) MGLMapCamera *camera;

- (void)setCamera:(MGLMapCamera *)camera animated:(BOOL)animated;

#pragma mark - Converting Map Coordinates

/** @name Converting Map Coordinates */
Expand Down
1 change: 1 addition & 0 deletions include/mbgl/ios/Mapbox.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#import "MGLAccountManager.h"
#import "MGLAnnotation.h"
#import "MGLAnnotationImage.h"
#import "MGLMapCamera.h"
#import "MGLGeometry.h"
#import "MGLMapView.h"
#import "MGLMultiPoint.h"
Expand Down
23 changes: 23 additions & 0 deletions include/mbgl/map/camera.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#ifndef MBGL_MAP_CAMERA
#define MBGL_MAP_CAMERA

#include <mbgl/util/geo.hpp>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/chrono.hpp>

#include <functional>

namespace mbgl {

struct CameraOptions {
mapbox::util::optional<LatLng> center;
mapbox::util::optional<double> zoom;
mapbox::util::optional<double> angle;
mapbox::util::optional<double> pitch;
mapbox::util::optional<Duration> duration;
mapbox::util::optional<std::function<double(double)> > easing;
};

}

#endif /* MBGL_MAP_CAMERA */
10 changes: 7 additions & 3 deletions include/mbgl/map/map.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
#define MBGL_MAP_MAP

#include <mbgl/util/chrono.hpp>
#include <mbgl/map/camera.hpp>
#include <mbgl/map/update.hpp>
#include <mbgl/map/mode.hpp>
#include <mbgl/util/geo.hpp>
Expand Down Expand Up @@ -97,6 +98,9 @@ class Map : private util::noncopyable {
void cancelTransitions();
void setGestureInProgress(bool);

void jumpTo(CameraOptions options);
void easeTo(CameraOptions options);

// Position
void moveBy(double dx, double dy, const Duration& = Duration::zero());
void setLatLng(LatLng latLng, const Duration& = Duration::zero());
Expand All @@ -110,8 +114,8 @@ class Map : private util::noncopyable {
void setZoom(double zoom, const Duration& = Duration::zero());
double getZoom() const;
void setLatLngZoom(LatLng latLng, double zoom, const Duration& = Duration::zero());
void fitBounds(LatLngBounds bounds, EdgeInsets padding, const Duration& duration = Duration::zero());
void fitBounds(AnnotationSegment segment, EdgeInsets padding, const Duration& duration = Duration::zero());
CameraOptions cameraForLatLngBounds(LatLngBounds bounds, EdgeInsets padding);
CameraOptions cameraForLatLngs(std::vector<LatLng> latLngs, EdgeInsets padding);
void resetZoom();
double getMinZoom() const;
double getMaxZoom() const;
Expand All @@ -124,7 +128,7 @@ class Map : private util::noncopyable {
void resetNorth();

// Pitch
void setPitch(double pitch);
void setPitch(double pitch, const Duration& = Duration::zero());
double getPitch() const;

// Size
Expand Down
101 changes: 101 additions & 0 deletions platform/ios/MGLMapCamera.mm
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
#import "MGLMapCamera.h"

#include <mbgl/util/projection.hpp>

@implementation MGLMapCamera

+ (BOOL)supportsSecureCoding
{
return YES;
}

+ (instancetype)camera
{
return [[self alloc] init];
}

+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
fromEyeCoordinate:(CLLocationCoordinate2D)eyeCoordinate
eyeAltitude:(CLLocationDistance)eyeAltitude
{
mbgl::LatLng centerLatLng = mbgl::LatLng(centerCoordinate.latitude, centerCoordinate.longitude);
mbgl::LatLng eyeLatLng = mbgl::LatLng(eyeCoordinate.latitude, eyeCoordinate.longitude);

mbgl::ProjectedMeters centerMeters = mbgl::Projection::projectedMetersForLatLng(centerLatLng);
mbgl::ProjectedMeters eyeMeters = mbgl::Projection::projectedMetersForLatLng(eyeLatLng);
CLLocationDirection heading = std::atan((centerMeters.northing - eyeMeters.northing) /
(centerMeters.easting - eyeMeters.easting));

double groundDistance = std::sqrt(std::pow(centerMeters.northing - eyeMeters.northing, 2) +
std::pow(centerMeters.easting - eyeMeters.easting, 2));
CGFloat pitch = std::atan(eyeAltitude / groundDistance);

return [[self alloc] initWithCenterCoordinate:centerCoordinate
altitude:eyeAltitude
pitch:pitch
heading:heading];
}

+ (instancetype)cameraLookingAtCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
fromDistance:(CLLocationDistance)distance
pitch:(CGFloat)pitch
heading:(CLLocationDirection)heading
{
return [[self alloc] initWithCenterCoordinate:centerCoordinate
altitude:distance
pitch:(CGFloat)pitch
heading:heading];
}

- (instancetype)initWithCenterCoordinate:(CLLocationCoordinate2D)centerCoordinate
altitude:(CLLocationDistance)altitude
pitch:(CGFloat)pitch
heading:(CLLocationDirection)heading
{
if (self = [super init])
{
_centerCoordinate = centerCoordinate;
_altitude = altitude;
_pitch = pitch;
_heading = heading;
}
return self;
}

- (nullable instancetype)initWithCoder:(NSCoder *)coder
{
if (self = [super init])
{
_centerCoordinate = CLLocationCoordinate2DMake([[coder decodeObjectForKey:@"centerLatitude"] doubleValue],
[[coder decodeObjectForKey:@"centerLongitude"] doubleValue]);
_altitude = [[coder decodeObjectForKey:@"altitude"] doubleValue];
_pitch = [[coder decodeObjectForKey:@"pitch"] doubleValue];
_heading = [[coder decodeObjectForKey:@"heading"] doubleValue];
}
return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
[coder encodeDouble:_centerCoordinate.latitude forKey:@"centerLatitude"];
[coder encodeDouble:_centerCoordinate.longitude forKey:@"centerLongitude"];
[coder encodeDouble:_altitude forKey:@"altitude"];
[coder encodeDouble:_pitch forKey:@"pitch"];
[coder encodeDouble:_heading forKey:@"heading"];
}

- (id)copyWithZone:(nullable NSZone *)zone
{
return [[[self class] allocWithZone:zone] initWithCenterCoordinate:_centerCoordinate
altitude:_altitude
pitch:_pitch
heading:_heading];
}

- (NSString *)description
{
return [NSString stringWithFormat:@"<MKMapCamera %p centerCoordinate:%f, %f altitude:%.0fm heading:%.0f° pitch:%.0f°>",
self, _centerCoordinate.latitude, _centerCoordinate.longitude, _altitude, _heading, _pitch];
}

@end
Loading