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

Update MapboxDirections #89

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
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
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@
{
"identity" : "mapbox-directions-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/flitsmeister/mapbox-directions-swift",
"location" : "https://github.com/mapbox/mapbox-directions-swift",
"state" : {
"revision" : "6c19ecc4e1324887ae3250802b8d13d8d8b3ff2d",
"version" : "0.23.3"
"revision" : "6512320ee7f0091a4c55abe2bc6414f078da88c8",
"version" : "2.14.0"
}
},
{
Expand Down Expand Up @@ -36,6 +36,15 @@
"version" : "3.0.1"
}
},
{
"identity" : "swift-argument-parser",
"kind" : "remoteSourceControl",
"location" : "https://github.com/apple/swift-argument-parser",
"state" : {
"revision" : "41982a3656a71c768319979febd796c6fd111d5c",
"version" : "1.5.0"
}
},
{
"identity" : "swiftformat",
"kind" : "remoteSourceControl",
Expand All @@ -48,10 +57,10 @@
{
"identity" : "turf-swift",
"kind" : "remoteSourceControl",
"location" : "https://github.com/flitsmeister/turf-swift",
"location" : "https://github.com/mapbox/turf-swift.git",
"state" : {
"revision" : "b05b4658d1b48eac4127a0d9ebbb5a6f965a8251",
"version" : "0.2.2"
"revision" : "213050191cfcb3d5aa76e1fa90c6ff1e182a42ca",
"version" : "2.8.0"
}
}
],
Expand Down
38 changes: 23 additions & 15 deletions MapboxCoreNavigation/CLLocation.swift
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import CoreLocation
import MapboxDirections
import Polyline
import Turf

extension CLLocation {
Expand Down Expand Up @@ -61,39 +62,46 @@ extension CLLocation {
Returns a Boolean value indicating whether the receiver is within a given distance of a route step.
*/
func isWithin(_ maximumDistance: CLLocationDistance, of routeStep: RouteStep) -> Bool {
guard let closestCoordinate = Polyline(routeStep.coordinates!).closestCoordinate(to: coordinate) else {
guard let closestCoordinate = routeStep.shape?.closestCoordinate(to: self.coordinate) else {
return false
}
return closestCoordinate.distance < maximumDistance

return closestCoordinate.coordinate.distance(to: self.coordinate) < maximumDistance
}

// MARK: - Route Snapping

func snapped(to legProgress: RouteLegProgress) -> CLLocation? {
let coords = self.coordinates(for: legProgress)
guard let closest = Polyline(coords).closestCoordinate(to: coordinate) else { return nil }
guard let calculatedCourseForLocationOnStep = interpolatedCourse(along: coords) else { return nil }
guard let closest = LineString(coords).closestCoordinate(to: coordinate) else { return nil }
guard let calculatedCourseForLocationOnStep = self.interpolatedCourse(along: coords) else { return nil }

let userCourse = calculatedCourseForLocationOnStep
let userCoordinate = closest.coordinate
guard let firstCoordinate = legProgress.leg.steps.first?.coordinates?.first else { return nil }
guard let firstCoordinate = legProgress.leg.steps.first?.shape?.coordinates.first else { return nil }

guard self.shouldSnapCourse(toRouteWith: calculatedCourseForLocationOnStep, distanceToFirstCoordinateOnLeg: coordinate.distance(to: firstCoordinate)) else { return nil }

guard closest.distance <= (RouteControllerUserLocationSnappingDistance + horizontalAccuracy) else {
guard closest.distance <= (RouteControllerUserLocationSnappingDistance + self.horizontalAccuracy) else {
return nil
}

return CLLocation(coordinate: userCoordinate, altitude: altitude, horizontalAccuracy: horizontalAccuracy, verticalAccuracy: verticalAccuracy, course: userCourse, speed: speed, timestamp: timestamp)
return CLLocation(coordinate: userCoordinate,
altitude: self.altitude,
horizontalAccuracy: self.horizontalAccuracy,
verticalAccuracy: self.verticalAccuracy,
course: userCourse,
speed: self.speed,
timestamp: self.timestamp)
}

/**
Calculates the proper coordinates to use when calculating a snapped location.
*/
func coordinates(for legProgress: RouteLegProgress) -> [CLLocationCoordinate2D] {
let nearbyCoordinates = legProgress.nearbyCoordinates
let stepCoordinates = legProgress.currentStep.coordinates!
let stepCoordinates = legProgress.currentStep.shape!.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
Expand All @@ -110,7 +118,7 @@ extension CLLocation {
}
}

if speed <= RouteControllerMaximumSpeedForUsingCurrentStep {
if self.speed <= RouteControllerMaximumSpeedForUsingCurrentStep {
return stepCoordinates
}

Expand All @@ -121,17 +129,17 @@ extension CLLocation {
Given a location and a series of coordinates, compute what the course should be for a the location.
*/
func interpolatedCourse(along coordinates: [CLLocationCoordinate2D]) -> CLLocationDirection? {
let nearByPolyline = Polyline(coordinates)
let nearByPolyline = LineString(coordinates)

guard let closest = nearByPolyline.closestCoordinate(to: coordinate) else { return nil }

let slicedLineBehind = Polyline(coordinates.reversed()).sliced(from: closest.coordinate, to: coordinates.reversed().last)
let slicedLineBehind = LineString(coordinates.reversed()).sliced(from: closest.coordinate, to: coordinates.reversed().last)
let slicedLineInFront = nearByPolyline.sliced(from: closest.coordinate, to: coordinates.last)
let userDistanceBuffer: CLLocationDistance = max(speed * RouteControllerDeadReckoningTimeInterval / 2, RouteControllerUserLocationSnappingDistance / 2)

guard let pointBehind = slicedLineBehind.coordinateFromStart(distance: userDistanceBuffer) else { return nil }
guard let pointBehind = slicedLineBehind?.coordinateFromStart(distance: userDistanceBuffer) else { return nil }
guard let pointBehindClosest = nearByPolyline.closestCoordinate(to: pointBehind) else { return nil }
guard let pointAhead = slicedLineInFront.coordinateFromStart(distance: userDistanceBuffer) else { return nil }
guard let pointAhead = slicedLineInFront?.coordinateFromStart(distance: userDistanceBuffer) else { return nil }
guard let pointAheadClosest = nearByPolyline.closestCoordinate(to: pointAhead) else { return nil }

// Get direction of these points
Expand Down
2 changes: 1 addition & 1 deletion MapboxCoreNavigation/CoreFeedbackEvent.swift
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ class FeedbackEvent: CoreFeedbackEvent {

class RerouteEvent: CoreFeedbackEvent {
func update(newRoute: Route) {
if let geometry = newRoute.coordinates {
if let geometry = newRoute.shape?.coordinates {
eventDictionary["newGeometry"] = Polyline(coordinates: geometry).encodedPolyline
eventDictionary["newDistanceRemaining"] = round(newRoute.distance)
eventDictionary["newDurationRemaining"] = round(newRoute.expectedTravelTime)
Expand Down
9 changes: 4 additions & 5 deletions MapboxCoreNavigation/DistanceFormatter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@

func threshold(for distance: CLLocationDistance) -> Threshold {
for threshold in self.thresholds {
if distance < threshold.maximumDistance {

Check warning on line 112 in MapboxCoreNavigation/DistanceFormatter.swift

View workflow job for this annotation

GitHub Actions / Code Style

`where` clauses are preferred over a single `if` inside a `for` (for_where)
return threshold
}
}
Expand All @@ -122,7 +122,6 @@
}

/// Provides appropriately formatted, localized descriptions of linear distances.
@objc(MBDistanceFormatter)
open class DistanceFormatter: LengthFormatter {
/// True to favor brevity over precision.
var approx: Bool
Expand All @@ -133,7 +132,7 @@
public private(set) var unit: LengthFormatter.Unit = .millimeter

public var locale: Locale {
set {

Check warning on line 135 in MapboxCoreNavigation/DistanceFormatter.swift

View workflow job for this annotation

GitHub Actions / Code Style

Computed properties should first declare the getter and then the setter (computed_accessors_order)
self.numberFormatter.locale = newValue
self.nonFractionalLengthFormatter.numberFormatter.locale = newValue
}
Expand Down Expand Up @@ -164,7 +163,7 @@

- parameter approximate: approximates the distances.
*/
@objc public init(approximate: Bool = false) {
public init(approximate: Bool = false) {
self.approx = approximate
super.init()
self.locale = .nationalizedCurrent
Expand Down Expand Up @@ -199,7 +198,7 @@

The user’s `Locale` is used here to set the units.
*/
@objc public func string(from distance: CLLocationDistance) -> String {
public func string(from distance: CLLocationDistance) -> String {
numberFormatter.positivePrefix = ""
numberFormatter.positiveSuffix = ""
numberFormatter.decimalSeparator = self.nonFractionalLengthFormatter.numberFormatter.decimalSeparator
Expand All @@ -208,7 +207,7 @@
return self.formattedDistance(distance)
}

@objc override open func string(fromMeters numberInMeters: Double) -> String {
override open func string(fromMeters numberInMeters: Double) -> String {
self.string(from: numberInMeters)
}

Expand All @@ -235,7 +234,7 @@

`NSAttributedStringKey.quantity` is applied to the numeric quantity.
*/
@objc override open func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString? {
override open func attributedString(for obj: Any, withDefaultAttributes attrs: [NSAttributedString.Key: Any]? = nil) -> NSAttributedString? {
guard let distance = obj as? CLLocationDistance else {
return nil
}
Expand Down
36 changes: 21 additions & 15 deletions MapboxCoreNavigation/NavigationRouteOptions.swift
Original file line number Diff line number Diff line change
@@ -1,21 +1,20 @@
import CoreLocation
import Foundation
import MapboxDirections
import MapboxDirectionsObjc

/**
A `NavigationRouteOptions` object specifies turn-by-turn-optimized criteria for results returned by the Mapbox Directions API.

`NavigationRouteOptions` is a subclass of `RouteOptions` that has been optimized for navigation. Pass an instance of this class into the `Directions.calculate(_:completionHandler:)` method.
*/
@objc(MBNavigationRouteOptions)
open class NavigationRouteOptions: RouteOptions {
/**
Initializes a navigation route options object for routes between the given waypoints and an optional profile identifier optimized for navigation.

- SeeAlso:
[RouteOptions](https://www.mapbox.com/mapbox-navigation-ios/directions/0.10.1/Classes/RouteOptions.html)
*/
@objc public required init(waypoints: [Waypoint], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) {
public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {
super.init(waypoints: waypoints.map {
$0.coordinateAccuracy = -1
return $0
Expand All @@ -37,7 +36,7 @@ open class NavigationRouteOptions: RouteOptions {
- SeeAlso:
[RouteOptions](https://www.mapbox.com/mapbox-navigation-ios/directions/0.19.0/Classes/RouteOptions.html)
*/
@objc public convenience init(locations: [CLLocation], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) {
public convenience init(locations: [CLLocation], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {
self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier)
}

Expand All @@ -47,12 +46,16 @@ open class NavigationRouteOptions: RouteOptions {
- SeeAlso:
[RouteOptions](https://www.mapbox.com/mapbox-navigation-ios/directions/0.19.0/Classes/RouteOptions.html)
*/
@objc public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) {
public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {
self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier)
}

@objc public required init?(coder decoder: NSCoder) {
super.init(coder: decoder)

public required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}

public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = nil, queryItems: [URLQueryItem]? = nil) {
super.init(waypoints: waypoints, profileIdentifier: profileIdentifier, queryItems: queryItems)
}
}

Expand All @@ -63,15 +66,14 @@ open class NavigationRouteOptions: RouteOptions {

Note: it is very important you specify the `waypoints` for the route. Usually the only two values for this `IndexSet` will be 0 and the length of the coordinates. Otherwise, all coordinates passed through will be considered waypoints.
*/
@objc(MBNavigationMatchOptions)
open class NavigationMatchOptions: MatchOptions {
/**
Initializes a navigation route options object for routes between the given waypoints and an optional profile identifier optimized for navigation.

- SeeAlso:
[MatchOptions](https://www.mapbox.com/mapbox-navigation-ios/directions/0.19.0/Classes/MatchOptions.html)
*/
@objc public required init(waypoints: [Waypoint], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) {
public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {
super.init(waypoints: waypoints.map {
$0.coordinateAccuracy = -1
return $0
Expand All @@ -91,7 +93,7 @@ open class NavigationMatchOptions: MatchOptions {
- SeeAlso:
[MatchOptions](https://www.mapbox.com/mapbox-navigation-ios/directions/0.19.0/Classes/MatchOptions.html)
*/
@objc public convenience init(locations: [CLLocation], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) {
public convenience init(locations: [CLLocation], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {
self.init(waypoints: locations.map { Waypoint(location: $0) }, profileIdentifier: profileIdentifier)
}

Expand All @@ -101,11 +103,15 @@ open class NavigationMatchOptions: MatchOptions {
- SeeAlso:
[MatchOptions](https://www.mapbox.com/mapbox-navigation-ios/directions/0.19.0/Classes/MatchOptions.html)
*/
@objc public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: MBDirectionsProfileIdentifier? = .automobileAvoidingTraffic) {
public convenience init(coordinates: [CLLocationCoordinate2D], profileIdentifier: ProfileIdentifier? = .automobileAvoidingTraffic) {
self.init(waypoints: coordinates.map { Waypoint(coordinate: $0) }, profileIdentifier: profileIdentifier)
}

@objc public required init?(coder decoder: NSCoder) {
super.init(coder: decoder)

public required init(waypoints: [Waypoint], profileIdentifier: ProfileIdentifier? = nil, queryItems: [URLQueryItem]? = nil) {
super.init(waypoints: waypoints, profileIdentifier: profileIdentifier, queryItems: queryItems)
}

public required init(from decoder: any Decoder) throws {
try super.init(from: decoder)
}
}
11 changes: 9 additions & 2 deletions MapboxCoreNavigation/Route.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,19 @@ import CoreLocation
import MapboxDirections

extension Route {
convenience init(jsonFileName: String, waypoints: [CLLocationCoordinate2D], polylineShapeFormat: RouteShapeFormat = .polyline6, bundle: Bundle = .main, accessToken: String) {
static func from(jsonFileName: String, waypoints: [CLLocationCoordinate2D], polylineShapeFormat: RouteShapeFormat = .polyline6, bundle: Bundle = .main, accessToken: String) throws -> Route {
let convertedWaypoints = waypoints.compactMap { waypoint in
Waypoint(coordinate: waypoint)
}
let routeOptions = NavigationRouteOptions(waypoints: convertedWaypoints)
routeOptions.shapeFormat = polylineShapeFormat
self.init(json: Fixture.JSONFromFileNamed(name: jsonFileName, bundle: bundle), waypoints: convertedWaypoints, options: routeOptions)

let path = bundle.url(forResource: jsonFileName, withExtension: "json") ?? bundle.url(forResource: jsonFileName, withExtension: "geojson")!
let data = try Data(contentsOf: path)

let decoder = JSONDecoder()
let result = try decoder.decode(RouteResponse.self, from: data)

return result.routes!.first!
}
}
Loading
Loading