Skip to content
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,13 @@
* Fixed an issue where the CarPlay navigation map’s vanishing point and user puck initially remained centered on screen, instead of accounting for the maneuver panel, until the navigation bar was shown. ([#1856](https://github.com/mapbox/mapbox-navigation-ios/pull/1856))
* Fixed an issue where route shields and exit numbers appeared blurry in the maneuver panel on CarPlay devices and failed to appear in the CarPlay simulator. ([#1868](https://github.com/mapbox/mapbox-navigation-ios/pull/1868))
* Added `VisualInstruction.containsLaneIndications`, `VisualInstruction.maneuverImageSet(side:)`, `VisualInstruction.shouldFlipImage(side:)`, and `VisualInstruction.carPlayManeuverLabelAttributedText(bounds:shieldHeight:window:)`. ([#1860](https://github.com/mapbox/mapbox-navigation-ios/pull/1860))
* `RouteLegProgress.upComingStep` has been renamed to `upcomingStep`. ([#1860](https://github.com/mapbox/mapbox-navigation-ios/pull/1860))

### Other changes

* The `NavigationSettings.shared` property is now accessible in Objective-C code as `MBNavigationSettings.sharedSettings`. ([#1882](https://github.com/mapbox/mapbox-navigation-ios/pull/1882))
* Fixed spurious rerouting on multi-leg routes. ([#1884](https://github.com/mapbox/mapbox-navigation-ios/pull/1884))
* Adding property `RouteController.nearbyCoordinates`, which offers similar behavior to `RouteLegProgress.nearbyCoordinates`, which the addition of step lookahead/lookbehind in multi-leg routes. ([#1883](https://github.com/mapbox/mapbox-navigation-ios/pull/1883))

## v0.25.0 (November 22, 2018)

Expand Down
12 changes: 7 additions & 5 deletions MapboxCoreNavigation/CLLocation.swift
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,9 @@ extension CLLocation {

//MARK: - Route Snapping

func snapped(to legProgress: RouteLegProgress) -> CLLocation? {
let coords = coordinates(for: legProgress)
func snapped(to routeProgress: RouteProgress) -> CLLocation? {
let legProgress = routeProgress.currentLegProgress
let coords = coordinates(for: routeProgress)

guard let closest = Polyline(coords).closestCoordinate(to: coordinate) else { return nil }
guard let calculatedCourseForLocationOnStep = interpolatedCourse(along: coords) else { return nil }
Expand All @@ -104,13 +105,14 @@ extension CLLocation {
/**
Calculates the proper coordinates to use when calculating a snapped location.
*/
func coordinates(for legProgress: RouteLegProgress) -> [CLLocationCoordinate2D] {
let nearbyCoordinates = legProgress.nearbyCoordinates
func coordinates(for routeProgress: RouteProgress) -> [CLLocationCoordinate2D] {
let legProgress = routeProgress.currentLegProgress
let nearbyCoordinates = routeProgress.nearbyCoordinates
let stepCoordinates = legProgress.currentStep.coordinates!

// If the upcoming maneuver a sharp turn, only look at the current step for snapping.
// Otherwise, we may get false positives from nearby step coordinates
if let upcomingStep = legProgress.upComingStep,
if let upcomingStep = legProgress.upcomingStep,
let initialHeading = upcomingStep.initialHeading,
let finalHeading = upcomingStep.finalHeading {

Expand Down
8 changes: 4 additions & 4 deletions MapboxCoreNavigation/EventDetails.swift
Original file line number Diff line number Diff line change
Expand Up @@ -271,10 +271,10 @@ extension RouteLegProgress: Encodable {

public func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encodeIfPresent(upComingStep?.instructions, forKey: .upcomingInstruction)
try container.encodeIfPresent(upComingStep?.maneuverType.description, forKey: .upcomingType)
try container.encodeIfPresent(upComingStep?.maneuverDirection.description, forKey: .upcomingModifier)
try container.encodeIfPresent(upComingStep?.names?.joined(separator: ";"), forKey: .upcomingName)
try container.encodeIfPresent(upcomingStep?.instructions, forKey: .upcomingInstruction)
try container.encodeIfPresent(upcomingStep?.maneuverType.description, forKey: .upcomingType)
try container.encodeIfPresent(upcomingStep?.maneuverDirection.description, forKey: .upcomingModifier)
try container.encodeIfPresent(upcomingStep?.names?.joined(separator: ";"), forKey: .upcomingName)
try container.encodeIfPresent(currentStep.instructions, forKey: .previousInstruction)
try container.encode(currentStep.maneuverType.description, forKey: .previousType)
try container.encode(currentStep.maneuverDirection.description, forKey: .previousModifier)
Expand Down
20 changes: 7 additions & 13 deletions MapboxCoreNavigation/RouteController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ open class RouteController: NSObject, Router {
- important: If the rawLocation is outside of the route snapping tolerances, this value is nil.
*/
var snappedLocation: CLLocation? {
return rawLocation?.snapped(to: routeProgress.currentLegProgress)
return rawLocation?.snapped(to: routeProgress)
}

var heading: CLHeading?
Expand Down Expand Up @@ -221,14 +221,8 @@ open class RouteController: NSObject, Router {
Monitors the user's course to see if it is consistantly moving away from what we expect the course to be at a given point.
*/
func userCourseIsOnRoute(_ location: CLLocation) -> Bool {
// if we have yet to travel along the current leg, don't check for heading conformance
guard routeProgress.currentLegProgress.distanceTraveled > 0 else {
movementsAwayFromRoute = 0
return true
}

let nearByCoordinates = routeProgress.currentLegProgress.nearbyCoordinates
guard let calculatedCourseForLocationOnStep = location.interpolatedCourse(along: nearByCoordinates) else { return true }
let nearbyCoordinates = routeProgress.nearbyCoordinates
guard let calculatedCourseForLocationOnStep = location.interpolatedCourse(along: nearbyCoordinates) else { return true }

let maxUpdatesAwayFromRouteGivenAccuracy = Int(location.horizontalAccuracy / Double(RouteControllerIncorrectCourseMultiplier))

Expand Down Expand Up @@ -435,7 +429,7 @@ extension RouteController: CLLocationManagerDelegate {


func checkForFasterRoute(from location: CLLocation) {
guard let currentUpcomingManeuver = routeProgress.currentLegProgress.upComingStep else {
guard let currentUpcomingManeuver = routeProgress.currentLegProgress.upcomingStep else {
return
}

Expand Down Expand Up @@ -553,7 +547,7 @@ extension RouteController: CLLocationManagerDelegate {
let currentStepProgress = routeProgress.currentLegProgress.currentStepProgress

// The intersections array does not include the upcoming maneuver intersection.
if let upcomingStep = routeProgress.currentLegProgress.upComingStep, let upcomingIntersection = upcomingStep.intersections, let firstUpcomingIntersection = upcomingIntersection.first {
if let upcomingStep = routeProgress.currentLegProgress.upcomingStep, let upcomingIntersection = upcomingStep.intersections, let firstUpcomingIntersection = upcomingIntersection.first {
intersections += [firstUpcomingIntersection]
}

Expand All @@ -577,7 +571,7 @@ extension RouteController: CLLocationManagerDelegate {

// Bearings need to normalized so when the `finalHeading` is 359 and the user heading is 1,
// we count this as within the `RouteControllerMaximumAllowedDegreeOffsetForTurnCompletion`
if let upcomingStep = routeProgress.currentLegProgress.upComingStep, let finalHeading = upcomingStep.finalHeading, let initialHeading = upcomingStep.initialHeading {
if let upcomingStep = routeProgress.currentLegProgress.upcomingStep, let finalHeading = upcomingStep.finalHeading, let initialHeading = upcomingStep.initialHeading {
let initialHeadingNormalized = initialHeading.wrap(min: 0, max: 360)
let finalHeadingNormalized = finalHeading.wrap(min: 0, max: 360)
let userHeadingNormalized = location.course.wrap(min: 0, max: 360)
Expand All @@ -596,7 +590,7 @@ extension RouteController: CLLocationManagerDelegate {
}
}

let step = routeProgress.currentLegProgress.upComingStep?.maneuverLocation ?? routeProgress.currentLegProgress.currentStep.maneuverLocation
let step = routeProgress.currentLegProgress.upcomingStep?.maneuverLocation ?? routeProgress.currentLegProgress.currentStep.maneuverLocation
let userAbsoluteDistance = step.distance(to: location.coordinate)
let lastKnownUserAbsoluteDistance = routeProgress.currentLegProgress.currentStepProgress.userDistanceToManeuverLocation

Expand Down
56 changes: 55 additions & 1 deletion MapboxCoreNavigation/RouteProgress.swift
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,54 @@ open class RouteProgress: NSObject {
Returns the progress along the current `RouteLeg`.
*/
@objc public var currentLegProgress: RouteLegProgress

@objc public var priorLeg: RouteLeg? {
return legIndex > 0 ? route.legs[legIndex - 1] : nil
}

/**
The step prior to the current step along this route.

The prior step may be part of a different RouteLeg than the current step. If the current step is the first step along the route, this property is set to nil.
*/

@objc public var priorStep: RouteStep? {
return currentLegProgress.priorStep ?? priorLeg?.steps.last
}

/**
The leg following the current leg along this route.

If this leg is the last leg of the route, this property is set to nil.
*/

@objc public var upcomingLeg: RouteLeg? {
return legIndex + 1 < route.legs.endIndex ? route.legs[legIndex + 1] : nil
}

/**
The step following the current step along this route.

The upcoming step may be part of a different RouteLeg than the current step. If it is the last step along the route, this property is set to nil.
*/

public var upcomingStep: RouteStep? {
return currentLegProgress.upcomingStep ?? upcomingLeg?.steps.first
}

/**
Returns an array of `CLLocationCoordinate2D` of the coordinates along the current step and any adjacent steps.

- important: The adjacent steps may be part of legs other than the current leg.
*/

@objc public var nearbyCoordinates: [CLLocationCoordinate2D] {
let priorCoordinates = priorStep?.coordinates?.dropLast() ?? []
let currentCoordinates = currentLegProgress.currentStep.coordinates ?? []
let upcomingCoordinates = upcomingStep?.coordinates?.dropFirst() ?? []
return priorCoordinates + currentCoordinates + upcomingCoordinates
}

/**
Tuple containing a `CongestionLevel` and a corresponding `TimeInterval` representing the expected travel time for this segment.
*/
Expand Down Expand Up @@ -321,7 +368,12 @@ open class RouteLegProgress: NSObject {

If there is no `upcomingStep`, nil is returned.
*/
@available(*, deprecated, renamed: "upcomingStep")
@objc public var upComingStep: RouteStep? {
return upcomingStep
}

@objc public var upcomingStep: RouteStep? {
guard stepIndex + 1 < leg.steps.endIndex else {
return nil
}
Expand Down Expand Up @@ -367,9 +419,11 @@ open class RouteLegProgress: NSObject {
/**
Returns an array of `CLLocationCoordinate2D` of the prior, current and upcoming step geometry.
*/

@available(*, deprecated: 0.1, message: "Use RouteProgress.nearbyCoordinates")
@objc public var nearbyCoordinates: [CLLocationCoordinate2D] {
let priorCoords = priorStep?.coordinates ?? []
let upcomingCoords = upComingStep?.coordinates ?? []
let upcomingCoords = upcomingStep?.coordinates ?? []
let currentCoords = currentStep.coordinates ?? []
let nearby = priorCoords + currentCoords + upcomingCoords
assert(!nearby.isEmpty, "Step must have coordinates")
Expand Down
4 changes: 2 additions & 2 deletions MapboxCoreNavigationTests/LocationTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ class LocationTests: XCTestCase {

var setup: (progress: RouteProgress, firstLocation: CLLocation) {
let progress = RouteProgress(route: route)
let firstCoord = progress.currentLegProgress.nearbyCoordinates.first!
let firstCoord = progress.nearbyCoordinates.first!
let firstLocation = CLLocation(latitude: firstCoord.latitude, longitude: firstCoord.longitude)

return (progress, firstLocation)
Expand Down Expand Up @@ -47,7 +47,7 @@ class LocationTests: XCTestCase {
let initialHeadingOnFirstStep = progress.currentLegProgress.currentStep.finalHeading!
let coordinateAlongFirstStep = firstLocation.coordinate.coordinate(at: 100, facing: initialHeadingOnFirstStep)
let locationAlongFirstStep = CLLocation(latitude: coordinateAlongFirstStep.latitude, longitude: coordinateAlongFirstStep.longitude)
guard let snapped = locationAlongFirstStep.snapped(to: progress.currentLegProgress) else {
guard let snapped = locationAlongFirstStep.snapped(to: progress) else {
return XCTFail("Location should have snapped to route")
}

Expand Down
16 changes: 8 additions & 8 deletions MapboxCoreNavigationTests/NavigationServiceTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class NavigationServiceTests: XCTestCase {

let legProgress: RouteLegProgress = navigationService.router.routeProgress.currentLegProgress

let firstCoord = legProgress.nearbyCoordinates.first!
let firstCoord = navigationService.router.routeProgress.nearbyCoordinates.first!
let firstLocation = CLLocation(coordinate: firstCoord, altitude: 5, horizontalAccuracy: 10, verticalAccuracy: 5, course: 20, speed: 4, timestamp: Date())

let remainingStepCount = legProgress.remainingSteps.count
Expand Down Expand Up @@ -124,7 +124,7 @@ class NavigationServiceTests: XCTestCase {
navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocation])
XCTAssertEqual(navigation.router.location!.coordinate, firstLocation.coordinate, "Check snapped location is working")

let firstCoordinateOnUpcomingStep = navigation.router.routeProgress.currentLegProgress.upComingStep!.coordinates!.first!
let firstCoordinateOnUpcomingStep = navigation.router.routeProgress.currentLegProgress.upcomingStep!.coordinates!.first!
let firstLocationOnNextStepWithNoSpeed = CLLocation(coordinate: firstCoordinateOnUpcomingStep, altitude: 0, horizontalAccuracy: 10, verticalAccuracy: 10, course: 10, speed: 0, timestamp: Date())

navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocationOnNextStepWithNoSpeed])
Expand All @@ -144,9 +144,9 @@ class NavigationServiceTests: XCTestCase {
navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocation])
XCTAssertEqual(navigation.router.location!.coordinate, firstLocation.coordinate, "Check snapped location is working")

let firstCoordinateOnUpcomingStep = navigation.router.routeProgress.currentLegProgress.upComingStep!.coordinates!.first!
let firstCoordinateOnUpcomingStep = navigation.router.routeProgress.currentLegProgress.upcomingStep!.coordinates!.first!

let finalHeading = navigation.router.routeProgress.currentLegProgress.upComingStep!.finalHeading!
let finalHeading = navigation.router.routeProgress.currentLegProgress.upcomingStep!.finalHeading!
let firstLocationOnNextStepWithDifferentCourse = CLLocation(coordinate: firstCoordinateOnUpcomingStep, altitude: 0, horizontalAccuracy: 30, verticalAccuracy: 10, course: -finalHeading, speed: 5, timestamp: Date())

navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocationOnNextStepWithDifferentCourse])
Expand All @@ -163,7 +163,7 @@ class NavigationServiceTests: XCTestCase {
navigation.locationManager!(navigation.locationManager, didUpdateLocations: [firstLocation])
XCTAssertEqual(navigation.router.location!.coordinate, firstLocation.coordinate, "Check snapped location is working")

let futureCoord = Polyline(navigation.router.routeProgress.currentLegProgress.nearbyCoordinates).coordinateFromStart(distance: 100)!
let futureCoord = Polyline(navigation.router.routeProgress.nearbyCoordinates).coordinateFromStart(distance: 100)!
let futureInaccurateLocation = CLLocation(coordinate: futureCoord, altitude: 0, horizontalAccuracy: 1, verticalAccuracy: 200, course: 0, speed: 5, timestamp: Date())

navigation.locationManager!(navigation.locationManager, didUpdateLocations: [futureInaccurateLocation])
Expand All @@ -181,9 +181,9 @@ class NavigationServiceTests: XCTestCase {
route.accessToken = "foo"
let navigation = MapboxNavigationService(route: route, directions: directions)
let router = navigation.router!
let firstCoord = router.routeProgress.currentLegProgress.nearbyCoordinates.first!
let firstCoord = router.routeProgress.nearbyCoordinates.first!
let firstLocation = CLLocation(latitude: firstCoord.latitude, longitude: firstCoord.longitude)
let coordNearStart = Polyline(router.routeProgress.currentLegProgress.nearbyCoordinates).coordinateFromStart(distance: 10)!
let coordNearStart = Polyline(router.routeProgress.nearbyCoordinates).coordinateFromStart(distance: 10)!

navigation.locationManager(navigation.locationManager, didUpdateLocations: [firstLocation])

Expand All @@ -196,7 +196,7 @@ class NavigationServiceTests: XCTestCase {

// The course should not be the interpolated course, rather the raw course.
XCTAssertEqual(directionToStart, router.location!.course, "The course should be the raw course and not an interpolated course")
XCTAssertFalse(facingTowardsStartLocation.shouldSnap(toRouteWith: facingTowardsStartLocation.interpolatedCourse(along: router.routeProgress.currentLegProgress.nearbyCoordinates)!, distanceToFirstCoordinateOnLeg: facingTowardsStartLocation.distance(from: firstLocation)), "Should not snap")
XCTAssertFalse(facingTowardsStartLocation.shouldSnap(toRouteWith: facingTowardsStartLocation.interpolatedCourse(along: router.routeProgress.nearbyCoordinates)!, distanceToFirstCoordinateOnLeg: facingTowardsStartLocation.distance(from: firstLocation)), "Should not snap")
}

//TODO: Broken by PortableRoutecontroller & MBNavigator -- needs team discussion.
Expand Down
2 changes: 1 addition & 1 deletion MapboxCoreNavigationTests/RouteProgressTests.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class RouteProgressTests: XCTestCase {
XCTAssertEqual(routeProgress.currentLegProgress.fractionTraveled, 0)
XCTAssertEqual(routeProgress.currentLegProgress.stepIndex, 0)
XCTAssertEqual(routeProgress.currentLegProgress.followOnStep?.description, "Turn left onto Gough Street")
XCTAssertEqual(routeProgress.currentLegProgress.upComingStep?.description, "Turn right onto Sacramento Street")
XCTAssertEqual(routeProgress.currentLegProgress.upcomingStep?.description, "Turn right onto Sacramento Street")
}

func testRouteStepProgress() {
Expand Down
2 changes: 1 addition & 1 deletion MapboxNavigation/CarPlayNavigationViewController.swift
Original file line number Diff line number Diff line change
Expand Up @@ -351,7 +351,7 @@ public class CarPlayNavigationViewController: UIViewController {
tertiaryManeuver.attributedInstructionVariants = [attributedTertiary]
}

if let upcomingStep = navService.routeProgress.currentLegProgress.upComingStep {
if let upcomingStep = navService.routeProgress.currentLegProgress.upcomingStep {
let distance = distanceFormatter.measurement(of: upcomingStep.distance)
tertiaryManeuver.initialTravelEstimates = CPTravelEstimates(distanceRemaining: distance, timeRemaining: upcomingStep.expectedTravelTime)
}
Expand Down
2 changes: 1 addition & 1 deletion MapboxNavigation/NavigationMapView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -289,7 +289,7 @@ open class NavigationMapView: MGLMapView, UIGestureRecognizerDelegate {

if UIDevice.current.isPluggedIn {
preferredFramesPerSecond = FrameIntervalOptions.pluggedInFramesPerSecond
} else if let upcomingStep = routeProgress.currentLegProgress.upComingStep,
} else if let upcomingStep = routeProgress.currentLegProgress.upcomingStep,
upcomingStep.maneuverDirection == .straightAhead || upcomingStep.maneuverDirection == .slightLeft || upcomingStep.maneuverDirection == .slightRight {
preferredFramesPerSecond = shouldPositionCourseViewFrameByFrame ? FrameIntervalOptions.defaultFramesPerSecond : minimumFramesPerSecond
} else if durationUntilNextManeuver > FrameIntervalOptions.durationUntilNextManeuver &&
Expand Down
Loading