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

Snap to route #57

Merged
merged 9 commits into from
Mar 22, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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 Examples/Swift/Base.lproj/Main.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="A3N-JT-loC" customClass="MGLMapView">
<view contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="A3N-JT-loC" customClass="NavigationMapView" customModule="MapboxNavigationUI">
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
<subviews>
<button hidden="YES" opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="center" contentVerticalAlignment="center" buttonType="roundedRect" lineBreakMode="middleTruncation" translatesAutoresizingMaskIntoConstraints="NO" id="8Sl-bV-xyU">
Expand Down
2 changes: 1 addition & 1 deletion Examples/Swift/ViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ class ViewController: UIViewController, MGLMapViewDelegate {
var routeViewController: RouteViewController?
var userRoute: Route?

@IBOutlet weak var mapView: MGLMapView!
@IBOutlet weak var mapView: NavigationMapView!
@IBOutlet weak var toggleNavigationButton: UIButton!
@IBOutlet weak var howToBeginLabel: UILabel!

Expand Down
16 changes: 16 additions & 0 deletions MapboxNavigation.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -95,10 +95,14 @@
35C9973F1E732C1B00544D1C /* RouteVoiceController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C9973E1E732C1B00544D1C /* RouteVoiceController.swift */; };
35C997411E732C5400544D1C /* RouteStepFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 35C997401E732C5400544D1C /* RouteStepFormatter.swift */; };
35D457A71E2D253100A89946 /* MBRouteController.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D457A61E2D253100A89946 /* MBRouteController.h */; settings = {ATTRIBUTES = (Public, ); }; };
35D825FB1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D825F91E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h */; settings = {ATTRIBUTES = (Public, ); }; };
35D825FC1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m in Sources */ = {isa = PBXBuildFile; fileRef = 35D825FA1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m */; };
35D825FE1E6A2EC60088F83B /* MapboxNavigationUI.h in Headers */ = {isa = PBXBuildFile; fileRef = 35D825FD1E6A2EC60088F83B /* MapboxNavigationUI.h */; };
C52AC1261DF0E48600396B9F /* RouteProgressTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C52AC1251DF0E48600396B9F /* RouteProgressTests.swift */; };
C52D09CC1DEF444D00BE3C5C /* GeometryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C52D09CB1DEF444D00BE3C5C /* GeometryTests.swift */; };
C52D09CE1DEF5E5100BE3C5C /* route.json in Resources */ = {isa = PBXBuildFile; fileRef = C52D09CD1DEF5E5100BE3C5C /* route.json */; };
C52D09D31DEF636C00BE3C5C /* Fixture.swift in Sources */ = {isa = PBXBuildFile; fileRef = C52D09D21DEF636C00BE3C5C /* Fixture.swift */; };
C53208AB1E81FFB900910266 /* NavigationMapView.swift in Sources */ = {isa = PBXBuildFile; fileRef = C53208AA1E81FFB900910266 /* NavigationMapView.swift */; };
C561DF5D1E650EC8004137B8 /* DistanceFormatter.swift in Sources */ = {isa = PBXBuildFile; fileRef = C561DF5C1E650EC8004137B8 /* DistanceFormatter.swift */; };
C58D6BAD1DDCF2AE00387F53 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = C58D6BAC1DDCF2AE00387F53 /* Constants.swift */; };
C5ADFBD81DDCC7840011824B /* MapboxNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = C5ADFBD71DDCC7840011824B /* MapboxNavigationTests.swift */; };
Expand Down Expand Up @@ -301,10 +305,14 @@
35C9973E1E732C1B00544D1C /* RouteVoiceController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteVoiceController.swift; sourceTree = "<group>"; };
35C997401E732C5400544D1C /* RouteStepFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteStepFormatter.swift; sourceTree = "<group>"; };
35D457A61E2D253100A89946 /* MBRouteController.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MBRouteController.h; sourceTree = "<group>"; };
35D825F91E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = "MGLMapView+MGLNavigationAdditions.h"; sourceTree = "<group>"; };
35D825FA1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = "MGLMapView+MGLNavigationAdditions.m"; sourceTree = "<group>"; };
35D825FD1E6A2EC60088F83B /* MapboxNavigationUI.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MapboxNavigationUI.h; sourceTree = "<group>"; };
C52AC1251DF0E48600396B9F /* RouteProgressTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = RouteProgressTests.swift; sourceTree = "<group>"; };
C52D09CB1DEF444D00BE3C5C /* GeometryTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeometryTests.swift; sourceTree = "<group>"; };
C52D09CD1DEF5E5100BE3C5C /* route.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = route.json; sourceTree = "<group>"; };
C52D09D21DEF636C00BE3C5C /* Fixture.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Fixture.swift; sourceTree = "<group>"; };
C53208AA1E81FFB900910266 /* NavigationMapView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NavigationMapView.swift; sourceTree = "<group>"; };
C561DF5C1E650EC8004137B8 /* DistanceFormatter.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DistanceFormatter.swift; sourceTree = "<group>"; };
C58D6BAC1DDCF2AE00387F53 /* Constants.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = "<group>"; };
C5ADFBC91DDCC7840011824B /* MapboxNavigation.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = MapboxNavigation.framework; sourceTree = BUILT_PRODUCTS_DIR; };
Expand Down Expand Up @@ -426,6 +434,9 @@
351BEBD81E5BCC28006FE110 /* MapboxNavigationUI */ = {
isa = PBXGroup;
children = (
35D825FD1E6A2EC60088F83B /* MapboxNavigationUI.h */,
35D825F91E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h */,
35D825FA1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m */,
351BEC081E5BCC72006FE110 /* Bundle.swift */,
351BEC091E5BCC72006FE110 /* DashedLineView.swift */,
351BEC0A1E5BCC72006FE110 /* Directions.swift */,
Expand All @@ -451,6 +462,7 @@
351BEC201E5BD4DC006FE110 /* Supporting files */,
351BEBEF1E5BCC63006FE110 /* TurnArrowView.swift */,
351BEBF01E5BCC63006FE110 /* UIView.swift */,
C53208AA1E81FFB900910266 /* NavigationMapView.swift */,
);
path = MapboxNavigationUI;
sourceTree = "<group>";
Expand Down Expand Up @@ -629,7 +641,9 @@
isa = PBXHeadersBuildPhase;
buildActionMask = 2147483647;
files = (
35D825FE1E6A2EC60088F83B /* MapboxNavigationUI.h in Headers */,
351BEBDB1E5BCC28006FE110 /* MapboxNavigationUI.h in Headers */,
35D825FB1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.h in Headers */,
);
runOnlyForDeploymentPostprocessing = 0;
};
Expand Down Expand Up @@ -1026,13 +1040,15 @@
352BBC421E5E72C500703DF1 /* Geometry.swift in Sources */,
351BEC061E5BCC6C006FE110 /* ManeuverDirection.swift in Sources */,
351BEBFD1E5BCC63006FE110 /* String.swift in Sources */,
35D825FC1E6A2DBE0088F83B /* MGLMapView+MGLNavigationAdditions.m in Sources */,
351BEBFF1E5BCC63006FE110 /* StyleKitArrows.swift in Sources */,
351BEBF91E5BCC63006FE110 /* RouteTableViewCell.swift in Sources */,
35C9973F1E732C1B00544D1C /* RouteVoiceController.swift in Sources */,
351BEBFC1E5BCC63006FE110 /* RouteViewController.swift in Sources */,
351BEC0F1E5BCC72006FE110 /* Directions.swift in Sources */,
35C997411E732C5400544D1C /* RouteStepFormatter.swift in Sources */,
351BEBFB1E5BCC63006FE110 /* RouteTableViewHeaderView.swift in Sources */,
C53208AB1E81FFB900910266 /* NavigationMapView.swift in Sources */,
351BEBF61E5BCC63006FE110 /* RouteMapViewController.swift in Sources */,
351BEBF51E5BCC63006FE110 /* RouteManeuverViewController.swift in Sources */,
351BEC011E5BCC63006FE110 /* TurnArrowView.swift in Sources */,
Expand Down
4 changes: 4 additions & 0 deletions MapboxNavigation/Constants.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,10 @@ public let RouteControllerShouldReroute = Notification.Name(MBRouteControllerSho
*/
public var RouteControllerMaximumDistanceBeforeRecalculating: CLLocationDistance = 50

/*
Accepted deviation excluding horizontal accuracy before the user is considered to be off route.
*/
public var RouteControllerUserLocationSnappingDistance: CLLocationDistance = 10

/*
Threshold user must be in within to count as completing a step. One of two heuristics used to know when a user completes a step, see `RouteControllerManeuverZoneRadius`.
Expand Down
13 changes: 11 additions & 2 deletions MapboxNavigation/RouteController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ open class RouteController: NSObject {
public var routeProgress: RouteProgress


/*
If true, the user puck is snapped to closest location on the route.
*/
public var snapsUserLocationAnnotationToRoute = false


/*
Intializes a new `RouteController`.

Expand Down Expand Up @@ -107,12 +113,15 @@ extension RouteController: CLLocationManagerDelegate {
monitorStepProgress(location)
}

func userIsOnRoute(_ location: CLLocation) -> Bool {
public func userIsOnRoute(_ location: CLLocation) -> Bool {
// Find future location of user
let metersInFrontOfUser = location.speed * RouteControllerDeadReckoningTimeInterval
let locationInfrontOfUser = location.coordinate.coordinate(at: metersInFrontOfUser, facing: location.course)
let newLocation = CLLocation(latitude: locationInfrontOfUser.latitude, longitude: locationInfrontOfUser.longitude)
return newLocation.isWithin(RouteControllerMaximumDistanceBeforeRecalculating, of: routeProgress.currentLegProgress.currentStep)
let radius = min(RouteControllerMaximumDistanceBeforeRecalculating,
location.horizontalAccuracy + RouteControllerUserLocationSnappingDistance)

return newLocation.isWithin(radius, of: routeProgress.currentLegProgress.currentStep)
}

func monitorStepProgress(_ location: CLLocation) {
Expand Down
8 changes: 8 additions & 0 deletions MapboxNavigationUI/MGLMapView+MGLNavigationAdditions.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
#import <Mapbox/Mapbox.h>

@interface MGLMapView (MGLNavigationAdditions) <CLLocationManagerDelegate>

// FIXME: This will be removed once https://github.com/mapbox/mapbox-gl-native/issues/6867 is implemented
- (void)locationManager:(CLLocationManager *)manager didUpdateLocations:(NSArray *)locations;

@end
5 changes: 5 additions & 0 deletions MapboxNavigationUI/MGLMapView+MGLNavigationAdditions.m
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
#import "MGLMapView+MGLNavigationAdditions.h"

@implementation MGLMapView (MGLNavigationAdditions)

@end
4 changes: 4 additions & 0 deletions MapboxNavigationUI/MGLMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,7 @@ extension MGLMapView {
}
}
}

protocol NavigationMapViewDelegate {
func navigationMapView(_ mapView: NavigationMapView, shouldUpdateTo location: CLLocation) -> CLLocation?
}
3 changes: 1 addition & 2 deletions MapboxNavigationUI/MapboxNavigationUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,5 +6,4 @@ FOUNDATION_EXPORT double MapboxNavigationUIVersionNumber;
//! Project version string for MapboxNavigationUI.
FOUNDATION_EXPORT const unsigned char MapboxNavigationUIVersionString[];



#import "MGLMapView+MGLNavigationAdditions.h"
16 changes: 16 additions & 0 deletions MapboxNavigationUI/NavigationMapView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import Foundation

public class NavigationMapView: MGLMapView {

var navigationMapDelegate: NavigationMapViewDelegate?

public override func locationManager(_ manager: CLLocationManager!, didUpdateLocations locations: [Any]!) {
guard let location = locations.first as? CLLocation else { return }

if let modifiedLocation = navigationMapDelegate?.navigationMapView(self, shouldUpdateTo: location) {
super.locationManager(manager, didUpdateLocations: [modifiedLocation])
} else {
super.locationManager(manager, didUpdateLocations: locations)
}
}
}
2 changes: 1 addition & 1 deletion MapboxNavigationUI/Resources/Navigation.storyboard
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@
<rect key="frame" x="0.0" y="0.0" width="375" height="323.5"/>
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
<subviews>
<view contentMode="scaleToFill" restorationIdentifier="mapView" translatesAutoresizingMaskIntoConstraints="NO" id="nNr-30-cGD" customClass="MGLMapView">
<view contentMode="scaleToFill" restorationIdentifier="mapView" translatesAutoresizingMaskIntoConstraints="NO" id="nNr-30-cGD" customClass="NavigationMapView" customModule="MapboxNavigationUI" customModuleProvider="target">
<rect key="frame" x="0.0" y="0.0" width="375" height="324"/>
<color key="backgroundColor" red="0.90196078430000004" green="0.89019607840000003" blue="0.87450980389999999" alpha="1" colorSpace="custom" customColorSpace="sRGB"/>
<userDefinedRuntimeAttributes>
Expand Down
28 changes: 27 additions & 1 deletion MapboxNavigationUI/RouteMapViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ class ArrowStrokePolyline: ArrowFillPolyline {}


class RouteMapViewController: UIViewController, PulleyPrimaryContentControllerDelegate {
@IBOutlet weak var mapView: MGLMapView!
@IBOutlet weak var mapView: NavigationMapView!
@IBOutlet weak var recenterButton: UIButton!

var routePageViewController: RoutePageViewController!
Expand Down Expand Up @@ -39,6 +39,7 @@ class RouteMapViewController: UIViewController, PulleyPrimaryContentControllerDe
automaticallyAdjustsScrollViewInsets = false

mapView.delegate = self
mapView.navigationMapDelegate = self
mapView.tintColor = NavigationUI.shared.tintColor
recenterButton.tintColor = NavigationUI.shared.tintColor
recenterButton.applyDefaultCornerRadiusShadow(cornerRadius: 22)
Expand Down Expand Up @@ -218,6 +219,31 @@ class RouteMapViewController: UIViewController, PulleyPrimaryContentControllerDe
}
}

// MARK: NavigationMapViewDelegate

extension RouteMapViewController: NavigationMapViewDelegate {

@objc(navigationMapView:shouldUpdateToLocation:)
func navigationMapView(_ mapView: NavigationMapView, shouldUpdateTo location: CLLocation) -> CLLocation? {

guard routeController.userIsOnRoute(location) else { return nil }

let route = routeController.routeProgress.route
guard let coordinates = route.coordinates else { return nil }

var newCoordinate = location.coordinate
if routeController.snapsUserLocationAnnotationToRoute {
// Snap to route
let snappedCoordinate = closestCoordinate(on: coordinates, to: location.coordinate)
if let coordinate = snappedCoordinate?.coordinate {
newCoordinate = coordinate
}
}

return CLLocation(coordinate: newCoordinate, altitude: location.altitude, horizontalAccuracy: location.horizontalAccuracy, verticalAccuracy: location.verticalAccuracy, course: location.course, speed: location.speed, timestamp: location.timestamp)
}
}

// MARK: MGLMapViewDelegate

extension RouteMapViewController: MGLMapViewDelegate {
Expand Down
9 changes: 9 additions & 0 deletions MapboxNavigationUI/RouteViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,8 @@ public class RouteViewController: NavigationPulleyViewController {
var routeTask: URLSessionDataTask?
let routeStepFormatter = RouteStepFormatter()

var lastReRouteLocation: CLLocation?

required public init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
Expand Down Expand Up @@ -151,6 +153,13 @@ public class RouteViewController: NavigationPulleyViewController {

func shouldReroute(notification: NSNotification) {
let location = notification.userInfo![RouteControllerNotificationShouldRerouteKey] as! CLLocation

if let previousLocation = lastReRouteLocation {
guard location.distance(from: previousLocation) >= RouteControllerMaximumDistanceBeforeRecalculating else {
return
}
}

routeTask?.cancel()

let options = RouteOptions.preferredOptions(from: location.coordinate, to: destination.coordinate, heading: location.course, profileIdentifier: route.profileIdentifier)
Expand Down