Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Navigation camera viewport API. #2826

Merged
merged 72 commits into from
Apr 13, 2021
Merged
Show file tree
Hide file tree
Changes from 32 commits
Commits
Show all changes
72 commits
Select commit Hold shift + click to select a range
ff65ef0
Add initial version of Navigation Viewport Camera API.
MaximAlien Mar 3, 2021
98254fc
Replace `NavigationCameraStateObserver` with NSNotification.
MaximAlien Mar 5, 2021
8d75a02
Update only user puck when arriving to highlighted building.
MaximAlien Mar 9, 2021
8d91b36
Remove `NavigationMapViewCourseTrackingDelegate`.
MaximAlien Mar 9, 2021
7850866
Update `CustomViewController` to new camera behavior.
MaximAlien Mar 9, 2021
6766e5d
Add ability to cancel pending camera transitions.
MaximAlien Mar 10, 2021
cb2147c
Add array extensions required for transitions.
MaximAlien Mar 10, 2021
d576504
Add basic implementation of transitions functionality.
MaximAlien Mar 12, 2021
e32c678
Simplify transition completion.
MaximAlien Mar 12, 2021
35ff55e
Fix issue related to incorrect camera positioning right before maneuver.
MaximAlien Mar 12, 2021
91786ec
Improve `UserCourseView` handling during camera transitions.
MaximAlien Mar 12, 2021
2c0fbf8
Refactor `NavigationViewportDataSource` to simplify `CameraOptions` c…
MaximAlien Mar 16, 2021
4007e68
Add `NavigationCameraDebugView` to be able to debug viewport related …
MaximAlien Mar 17, 2021
fab68e9
Improve `UserCourseView` updates when running long animations. Improv…
MaximAlien Mar 18, 2021
3caf811
Make frame by frame `UserCourseView` location updates default.
MaximAlien Mar 18, 2021
7a60c97
Simplify `NavigationCameraStateTransition`.
MaximAlien Mar 19, 2021
0956c59
Fix crash in `CameraView` in case if location is invalid.
MaximAlien Mar 19, 2021
8ea2d04
Improve viewport padding related issues.
MaximAlien Mar 19, 2021
d1d7315
Improve notifications handling in `NavigationCameraDebugView`.
MaximAlien Mar 19, 2021
24c5a3e
Rename method.
MaximAlien Mar 19, 2021
d6b60fb
Introduce `ViewportDataSourceType` to be able to control what locatio…
MaximAlien Mar 23, 2021
203601c
Integrate Mapbox Maps CameraView and CameraAnimator. (#2886)
MaximAlien Mar 30, 2021
f26520e
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Mar 30, 2021
9c02e73
Improve indentation.
MaximAlien Mar 30, 2021
ded2563
Add ability to use `PassiveLocationManager` on CarPlay.
MaximAlien Mar 30, 2021
a06b8eb
Improve camera viewport related issues on iOS and CarPlay.
MaximAlien Mar 30, 2021
81812a2
Add more debugging instruments.
MaximAlien Mar 30, 2021
7b2ed48
Minor improvements.
MaximAlien Mar 31, 2021
876093b
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Mar 31, 2021
41093db
Resolve outstanding conflicts in CocoaPodsTest.
MaximAlien Mar 31, 2021
aad725f
Minor properties renaming.
MaximAlien Mar 31, 2021
f2d5268
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Mar 31, 2021
d0497d8
Set initial coordinate for active guidance navigation on CarPlay.
MaximAlien Apr 1, 2021
38d9827
Update changelog and jazzy docs.
MaximAlien Apr 1, 2021
e32f4dd
Minor improvements in camera transitions.
MaximAlien Apr 1, 2021
f59cb67
Fix camera related issues in `CustomViewController`.
MaximAlien Apr 1, 2021
fdc8724
Make `NavigationCameraDebugView` part of MapboxNavigation to simplify…
MaximAlien Apr 1, 2021
8a28635
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 1, 2021
c513aaf
Update jazzy docs.
MaximAlien Apr 1, 2021
cfb8516
Add more information to navigation camera debugging view.
MaximAlien Apr 2, 2021
c9d698e
Further improvements to make debugging easier.
MaximAlien Apr 2, 2021
2e2a348
Fix padding in active guidance for CarPlay.
MaximAlien Apr 2, 2021
6ad741f
Make navigationViewportDataSource weak.
MaximAlien Apr 2, 2021
5714845
Fix issues related to fitting camera on CarPlay while previewing rout…
MaximAlien Apr 2, 2021
0e41bac
Fix issue related to switching between overview and following modes o…
MaximAlien Apr 2, 2021
0f462de
Use non-zero padding in overview mode on CarPlay.
MaximAlien Apr 2, 2021
cd4aad2
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 2, 2021
6e5dca7
Remove the ability to control visibility of `NavigationCameraDebugVie…
MaximAlien Apr 5, 2021
8340599
Remove custom implementations of navigation camera as they were moved…
MaximAlien Apr 6, 2021
a019037
Improve `PassiveLocationDataSource` management.
MaximAlien Apr 6, 2021
cd82049
Fix issue, which was causing incorrect position of camera on CarPlay …
MaximAlien Apr 7, 2021
4410a3b
Improve NavigationCameraDebugView drawing.
MaximAlien Apr 7, 2021
1edae74
Fix issue, which was causing incorrect camera behavior on CarPlay in …
MaximAlien Apr 7, 2021
1360d07
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 8, 2021
3cc94c5
Fix memory leak in `NavigationMapView`.
MaximAlien Apr 8, 2021
77e68c8
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 8, 2021
3de06ff
Address code review feedback.
MaximAlien Apr 8, 2021
6900f49
Remove excessive `CLLocation` extension.
MaximAlien Apr 8, 2021
4e47417
Address code review feedback.
MaximAlien Apr 8, 2021
56cd738
Update `FrameIntervalOptions`.
MaximAlien Apr 8, 2021
8bb685d
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 8, 2021
578b705
Update changelog.
MaximAlien Apr 8, 2021
c4ae428
Disable debugging view, improve comments.
MaximAlien Apr 8, 2021
1f60266
Fix issue related to incorrect `UserCourseView` bearing.
MaximAlien Apr 8, 2021
242ee49
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 8, 2021
465fb2a
Add `defaultAltitude`, which is used as part of initial `NavigationCa…
MaximAlien Apr 9, 2021
3fff0f1
Remove unused `CLLocationDegrees` extension.
MaximAlien Apr 9, 2021
41d0db0
Add two new public properties: `useBearingSmoothing` and `maxBearingA…
MaximAlien Apr 12, 2021
dc51870
Change default value for `maxBearingAngleDiffWhenSmoothing`.
MaximAlien Apr 12, 2021
c4ee83a
Add `distanceToFrameAfterManeuver` public property.
MaximAlien Apr 12, 2021
04694a9
Simplify `NavigationViewportDataSource` public APIs and improve docum…
MaximAlien Apr 12, 2021
371729d
Merge branch 'release-v2.0' into maxim/camera-viewport-api
MaximAlien Apr 13, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Example/AppDelegate+CarPlay.swift
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ extension AppDelegate: CarPlayManagerDelegate {

// MARK: CarPlayManagerDelegate
func carPlayManager(_ carPlayManager: CarPlayManager, didBeginNavigationWith service: NavigationService) {
currentAppRootViewController?.beginNavigationWithCarplay(navigationService: service)
currentAppRootViewController?.beginNavigationWithCarPlay(navigationService: service)
carPlayManager.currentNavigator?.compassView.isHidden = false

// Render part of the route that has been traversed with full transparency, to give the illusion of a disappearing route.
Expand Down
47 changes: 47 additions & 0 deletions Example/CustomCameraStateTransition.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import MapboxMaps
import MapboxNavigation

class CustomCameraStateTransition: CameraStateTransition {

weak var mapView: MapView?

required init(_ mapView: MapView) {
self.mapView = mapView
}

func transitionToFollowing(_ cameraOptions: CameraOptions, completion: @escaping (() -> Void)) {
mapView?.cameraManager.setCamera(to: cameraOptions,
animated: true,
duration: 0.5,
completion: { _ in
completion()
})
}

func transitionToOverview(_ cameraOptions: CameraOptions, completion: @escaping (() -> Void)) {
mapView?.cameraManager.setCamera(to: cameraOptions,
animated: true,
duration: 0.5,
completion: { _ in
completion()
})
}

func updateForFollowing(_ cameraOptions: CameraOptions) {
mapView?.cameraManager.setCamera(to: cameraOptions,
animated: true,
duration: 0.5,
completion: nil)
}

func updateForOverview(_ cameraOptions: CameraOptions) {
mapView?.cameraManager.setCamera(to: cameraOptions,
animated: true,
duration: 0.5,
completion: nil)
}

func cancelPendingTransition() {
mapView?.cameraManager.cancelTransitions()
}
}
21 changes: 12 additions & 9 deletions Example/CustomViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,16 @@ import MapboxNavigation
import MapboxDirections
import MapboxMaps

// FIXME: Currently if `MapView` is created using storyboard crash occurs.
class CustomViewController: UIViewController {

var destinationAnnotation: PointAnnotation! {
didSet {
navigationMapView.mapView.annotationManager.addAnnotation(destinationAnnotation)
}
}

var navigationService: NavigationService!

var simulateLocation = false

var userIndexedRoute: IndexedRoute?
Expand All @@ -34,6 +40,7 @@ class CustomViewController: UIViewController {
super.viewDidLoad()

navigationMapView.mapView.style.styleURL = .custom(url: URL(string: "mapbox://styles/mapbox-map-design/ckd6dqf981hi71iqlyn3e896y")!)
navigationMapView.userCourseView.isHidden = false

let locationManager = simulateLocation ? SimulatedLocationManager(route: userIndexedRoute!.0) : NavigationLocationManager()
navigationService = MapboxNavigationService(route: userIndexedRoute!.0, routeIndex: userIndexedRoute!.1, routeOptions: userRouteOptions!, locationSource: locationManager, simulating: simulateLocation ? .always : .onPoorGPS)
Expand All @@ -51,9 +58,6 @@ class CustomViewController: UIViewController {
// Start navigation
navigationService.start()

// Center map on user
navigationMapView.recenterMap()

navigationMapView.mapView.on(.styleLoaded, handler: { [weak self] _ in
guard let route = self?.navigationService.route else { return }
self?.navigationMapView.show([route])
Expand Down Expand Up @@ -101,8 +105,8 @@ class CustomViewController: UIViewController {
instructionsBannerView.updateDistance(for: routeProgress.currentLegProgress.currentStepProgress)
instructionsBannerView.isHidden = false

// Update the user puck
navigationMapView.updateCourseTracking(location: location, animated: true)
// Update `UserCourseView` to be placed on the most recent location.
navigationMapView.updateUserCourseView(location, animated: true)
}

@objc func updateInstructionsBanner(notification: NSNotification) {
Expand All @@ -122,7 +126,7 @@ class CustomViewController: UIViewController {
}

@IBAction func recenterMap(_ sender: Any) {
navigationMapView.recenterMap()
navigationMapView.navigationCamera.requestNavigationCameraToFollowing()
}

@IBAction func showFeedback(_ sender: Any) {
Expand Down Expand Up @@ -172,8 +176,7 @@ class CustomViewController: UIViewController {
updatePreviewBannerWith(step: step, maneuverStep: maneuverStep)

// stop tracking user, and move camera to step location
navigationMapView.tracksUserCourse = false
navigationMapView.enableFrameByFrameCourseViewTracking(for: 1)
navigationMapView.navigationCamera.requestNavigationCameraToIdle()
navigationMapView.mapView.cameraManager.setCamera(centerCoordinate: maneuverStep.maneuverLocation,
bearing: maneuverStep.initialHeading!,
animated: true)
Expand Down
94 changes: 94 additions & 0 deletions Example/CustomViewportDataSource.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
import MapboxMaps
import MapboxNavigation
import MapboxCoreNavigation

class CustomViewportDataSource: ViewportDataSource {

public var delegate: ViewportDataSourceDelegate?

public var followingMobileCamera: CameraOptions = CameraOptions()

public var followingHeadUnitCamera: CameraOptions = CameraOptions()

public var overviewMobileCamera: CameraOptions = CameraOptions()

public var overviewHeadUnitCamera: CameraOptions = CameraOptions()

weak var mapView: MapView?

// MARK: - Initializer methods

public required init(_ mapView: MapView) {
self.mapView = mapView
self.mapView?.locationManager.addLocationConsumer(newConsumer: self)

subscribeForNotifications()
}

deinit {
unsubscribeFromNotifications()
}

// MARK: - Notifications observer methods

func subscribeForNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(progressDidChange(_:)),
name: .routeControllerProgressDidChange,
object: nil)
}

func unsubscribeFromNotifications() {
NotificationCenter.default.removeObserver(self,
name: .routeControllerProgressDidChange,
object: nil)
}

@objc func progressDidChange(_ notification: NSNotification) {
let location = notification.userInfo?[RouteController.NotificationUserInfoKey.locationKey] as? CLLocation
let routeProgress = notification.userInfo?[RouteController.NotificationUserInfoKey.routeProgressKey] as? RouteProgress
let cameraOptions = self.cameraOptions(location, routeProgress: routeProgress)

delegate?.viewportDataSource(self, didUpdate: cameraOptions)
}

func cameraOptions(_ location: CLLocation?, routeProgress: RouteProgress? = nil) -> [String: CameraOptions] {
followingMobileCamera.center = location?.coordinate
followingMobileCamera.bearing = location?.course
followingMobileCamera.padding = .zero
followingMobileCamera.zoom = 14.0
followingMobileCamera.pitch = 0.0

overviewMobileCamera.center = location?.coordinate
overviewMobileCamera.bearing = 0.0
overviewMobileCamera.padding = .zero
overviewMobileCamera.zoom = 10.0
overviewMobileCamera.pitch = 0.0

let cameraOptions = [
CameraOptions.followingMobileCameraKey: followingMobileCamera,
CameraOptions.overviewMobileCameraKey: overviewMobileCamera
]

return cameraOptions
}
}

// MARK: - LocationConsumer delegate

extension CustomViewportDataSource: LocationConsumer {

var shouldTrackLocation: Bool {
get {
return true
}
set(newValue) {
// No-op
}
}

func locationUpdate(newLocation: Location) {
let cameraOptions = self.cameraOptions(newLocation.internalLocation)
delegate?.viewportDataSource(self, didUpdate: cameraOptions)
}
}
151 changes: 151 additions & 0 deletions Example/NavigationCameraDebugView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
import UIKit
import MapboxMaps
import MapboxNavigation

class NavigationCameraDebugView: UIView {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Used for debugging purposes. I intend to keep using it in example application.


weak var mapView: MapView?

var viewportLayer = CALayer()
var anchorLayer = CALayer()
var anchorTextLayer = CATextLayer()
var centerLayer = CALayer()
var centerTextLayer = CATextLayer()
var pitchTextLayer = CATextLayer()
var zoomTextLayer = CATextLayer()
var bearingTextLayer = CATextLayer()

required init(_ mapView: MapView, frame: CGRect) {
self.mapView = mapView

super.init(frame: frame)

isUserInteractionEnabled = false
backgroundColor = .clear
subscribeForNotifications()

viewportLayer.borderWidth = 3.0
viewportLayer.borderColor = UIColor.green.cgColor
layer.addSublayer(viewportLayer)

anchorLayer.backgroundColor = UIColor.red.cgColor
anchorLayer.frame = .init(x: 0.0, y: 0.0, width: 6.0, height: 6.0)
anchorLayer.cornerRadius = 3.0
layer.addSublayer(anchorLayer)

anchorTextLayer = CATextLayer()
anchorTextLayer.string = "Anchor"
anchorTextLayer.fontSize = UIFont.systemFontSize
anchorTextLayer.backgroundColor = UIColor.clear.cgColor
anchorTextLayer.foregroundColor = UIColor.red.cgColor
anchorTextLayer.frame = .zero
layer.addSublayer(anchorTextLayer)

centerLayer.backgroundColor = UIColor.blue.cgColor
centerLayer.frame = .init(x: 0.0, y: 0.0, width: 6.0, height: 6.0)
centerLayer.cornerRadius = 3.0
layer.addSublayer(centerLayer)

centerTextLayer = CATextLayer()
centerTextLayer.string = "Center"
centerTextLayer.fontSize = UIFont.systemFontSize
centerTextLayer.backgroundColor = UIColor.clear.cgColor
centerTextLayer.foregroundColor = UIColor.blue.cgColor
centerTextLayer.frame = .zero
layer.addSublayer(centerTextLayer)

pitchTextLayer = createDefaultTextLayer()
layer.addSublayer(pitchTextLayer)

zoomTextLayer = createDefaultTextLayer()
layer.addSublayer(zoomTextLayer)

bearingTextLayer = createDefaultTextLayer()
layer.addSublayer(bearingTextLayer)
}

func createDefaultTextLayer() -> CATextLayer {
let textLayer = CATextLayer()
textLayer.string = ""
textLayer.fontSize = UIFont.systemFontSize
textLayer.backgroundColor = UIColor.clear.cgColor
textLayer.foregroundColor = UIColor.black.cgColor
textLayer.frame = .zero

return textLayer
}

required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}

deinit {
unsubscribeFromNotifications()
}

func subscribeForNotifications() {
NotificationCenter.default.addObserver(self,
selector: #selector(navigationCameraViewportDidChange(_:)),
name: .navigationCameraViewportDidChange,
object: nil)
}

func unsubscribeFromNotifications() {
NotificationCenter.default.removeObserver(self,
name: .navigationCameraViewportDidChange,
object: nil)
}

@objc func navigationCameraViewportDidChange(_ notification: NSNotification) {
guard let mapView = mapView,
let cameraOptions = notification.userInfo?[NavigationCamera.NotificationUserInfoKey.cameraOptionsKey] as? Dictionary<String, CameraOptions>,
let followingMobileCamera = cameraOptions[CameraOptions.followingMobileCameraKey] else { return }

if let edgeInsets = followingMobileCamera.padding {
viewportLayer.frame = CGRect(x: edgeInsets.left,
y: edgeInsets.top,
width: mapView.frame.width - edgeInsets.left - edgeInsets.right,
height: mapView.frame.height - edgeInsets.top - edgeInsets.bottom)
}

if let anchorPosition = followingMobileCamera.anchor {
anchorLayer.position = anchorPosition
anchorTextLayer.frame = .init(x: anchorLayer.frame.origin.x + 5.0,
y: anchorLayer.frame.origin.y + 5.0,
width: 80.0,
height: 20.0)
}

if let centerCoordinate = followingMobileCamera.center {
centerLayer.position = mapView.point(for: centerCoordinate)
centerTextLayer.frame = .init(x: centerLayer.frame.origin.x + 5.0,
y: centerLayer.frame.origin.y + 5.0,
width: 80.0,
height: 20.0)
}

if let pitch = followingMobileCamera.pitch {
pitchTextLayer.frame = .init(x: viewportLayer.frame.origin.x + 5.0,
y: viewportLayer.frame.origin.y + 5.0,
width: viewportLayer.frame.size.width - 10.0,
height: 20.0)
pitchTextLayer.string = "Pitch: \(pitch)"
}

if let zoom = followingMobileCamera.zoom {
zoomTextLayer.frame = .init(x: viewportLayer.frame.origin.x + 5.0,
y: viewportLayer.frame.origin.y + 30.0,
width: viewportLayer.frame.size.width - 10.0,
height: 20.0)
zoomTextLayer.string = "Zoom: \(zoom)"
}

if let bearing = followingMobileCamera.bearing {
bearingTextLayer.frame = .init(x: viewportLayer.frame.origin.x + 5.0,
y: viewportLayer.frame.origin.y + 55.0,
width: viewportLayer.frame.size.width - 10.0,
height: 20.0)
bearingTextLayer.string = "Bearing: \(bearing)º"
}
}
}
Loading