Draw a route line with the alternative route on NavigationMapView

Draw multiple route lines on NavigationMapView, with one of them designated as an alternative route.

This example shows how to draw multiple route lines with one selected as an alternative route on NavigationMapView

  • We first request a route with NavigationRouteOptions and enable includesAlternativeRoutes as true, this allows the backend to calculate and provide multiple route lines

  • Then we can set one of the routes as an alternative route and show all the route info on NavigationMapView

documentation image

For all code examples, refer to Navigation Code Examples

DrawAlternativeRouteController view source

1
import UIKit
2
import NbmapNavigation
3
import NbmapCoreNavigation
4
import Nbmap
5
6
class DrawAlternativeRouteController: UIViewController, NGLMapViewDelegate {
7
8
var navigationMapView: NavigationMapView? {
9
didSet {
10
oldValue?.removeFromSuperview()
11
if let navigationMapView = navigationMapView {
12
view.insertSubview(navigationMapView, at: 0)
13
}
14
}
15
}
16
17
var routes : [Route]? {
18
didSet {
19
guard let routes = routes,
20
let current = routes.first else { navigationMapView?.removeRoutes(); return }
21
22
navigationMapView?.showRoutes(routes)
23
navigationMapView?.showWaypoints(current)
24
navigationMapView?.showRouteDurationSymbol(routes)
25
}
26
}
27
28
override func viewDidLoad() {
29
super.viewDidLoad()
30
31
let origin = CLLocation(latitude: 37.77440680146262, longitude: -122.43539772352648)
32
let destination = CLLocation(latitude: 37.779664450073, longitude: -122.43245802247301)
33
34
self.navigationMapView = NavigationMapView(frame: view.bounds)
35
navigationMapView?.userTrackingMode = .followWithHeading
36
navigationMapView?.delegate = self
37
navigationMapView?.navigationMapDelegate = self
38
39
self.view.setNeedsLayout()
40
41
requestRoutes(origin: origin, destination: destination)
42
}
43
44
func requestRoutes(origin: CLLocation, destination: CLLocation){
45
46
let options = NavigationRouteOptions(origin: origin, destination: destination)
47
/**
48
Sets whether the route contains an alternate route
49
By default , it set to `false`
50
*/
51
options.includesAlternativeRoutes = true
52
53
/**
54
The route classes that the calculated routes will avoid.
55
We can set an array road class to avoid. This property can be set to `.toll`,`.ferry`,`.highway`
56
By default , this property is set to empty
57
*/
58
options.roadClassesToAvoid = [.toll,.ferry,.highway]
59
/**
60
Set up the navigation measurement unit
61
This property should be set to `.metric` or `.imperial`
62
By default , this property is set to the unit follow with current system locale.
63
*/
64
options.distanceMeasurementSystem = .imperial
65
66
/**
67
Set specifying the primary mode of transportation for the routes.
68
This property should be set to `NBNavigationModeCar`, `NBNavigationModeAuto`, `NBNavigationModeBike`, `NBNavigationMode4W`,`NBNavigationMode2W`,`NBNavigationMode6W`, or `NBNavigationModeEscooter`. The default value of this property is `NBNavigationMode4W`, which specifies driving directions.
69
*/
70
options.profileIdentifier = NBNavigationMode.mode4W
71
/**
72
Set the departureTime of the route , By default , it sets the current timestamp since from 1970
73
*/
74
options.departureTime = Int(Date().timeIntervalSince1970)
75
76
/**
77
Set up the locale in which the route's instructions are written.
78
If you use NbmapDirections.swift with the Nbmap Directions API, this property affects the sentence contained within the `RouteStep.instructions` property, but it does not affect any road names contained in that property or other properties such as `RouteStep.name`.
79
The Navigation API can provide instructions in [a number of languages]. Set this property to `Bundle.main.preferredLocalizations.first` or `Locale.autoupdatingCurrent` to match the application's language or the system language, respectively.
80
By default, this property is set to the current system locale.
81
*/
82
options.locale = Locale.autoupdatingCurrent
83
84
/**
85
Set map options, This property may make the ETA more accurate. If set to `NBMapOption.valhala`, shapeFormat needs to be set to `polyline`.
86
By default , the value of this property is `NBMapOption.none`
87
*/
88
options.mapOption = NBMapOption.none
89
/**
90
Format of the data from which the shapes of the returned route and its steps are derived.
91
92
This property has no effect on the returned shape objects, although the choice of format can significantly affect the size of the underlying HTTP response.
93
94
The default value of this property is `polyline6`.
95
*/
96
options.shapeFormat = .polyline6
97
98
Directions.shared.calculate(options) { [weak self] routes, error in
99
guard let weakSelf = self else {
100
return
101
}
102
guard error == nil else {
103
print(error!)
104
return
105
}
106
107
guard let routes = routes else { return }
108
109
110
// Process or display routes information.For example, display the routes, waypoints and duration symbol on the map
111
weakSelf.navigationMapView?.showRoutes(routes)
112
weakSelf.navigationMapView?.showRouteDurationSymbol(routes)
113
114
guard let current = routes.first else { return }
115
weakSelf.navigationMapView?.showWaypoints(current)
116
weakSelf.routes = routes
117
}
118
}
119
}
120
121
extension DrawAlternativeRouteController: NavigationMapViewDelegate {
122
// Delegate method called when the user selects a route
123
func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) {
124
guard let routes = routes else { return }
125
guard let index = routes.firstIndex(where: { $0 == route }) else { return }
126
self.routes!.remove(at: index)
127
self.routes!.insert(route, at: 0)
128
}
129
}

The code first creates a NavigationRouteOptions object. This object specifies the parameters for the route calculation. In this case, the options specify that the route should include an alternate route, that it should avoid toll roads, ferries, and highways, and that it should be calculated in imperial units. The code then calls the calculate() method on the Directions object. This method calculates the routes and returns them in an array. The code then loops through the array and displays the routes on the map. The code also defines a delegate method called navigationMapView(_:didSelect:). This method is called when the user selects a route. The method removes the selected route from the array and inserts it at the beginning of the array. This ensures that the selected route is always displayed at the top of the map. Here is a more detailed explanation of each step in the code:

  • Create a NavigationRouteOptions object.

    1
    let options = NavigationRouteOptions(origin: origin, destination: destination)

    This code creates a NavigationRouteOptions object. The origin property specifies the starting location of the route, and the destination property specifies the ending location of the route.

  • Set the options for the route calculation.

    1
    options.includesAlternativeRoutes = true
    2
    options.roadClassesToAvoid = [.toll, .ferry, .highway]
    3
    options.distanceMeasurementSystem = .imperial
    4
    options.profileIdentifier = NBNavigationMode.mode4W
    5
    options.departureTime = Int(Date().timeIntervalSince1970)
    6
    options.locale = Locale.autoupdatingCurrent
    7
    options.mapOption = NBMapOption.none
    8
    options.shapeFormat = .polyline6

    This code sets the options for the route calculation. The includesAlternativeRoutes property specifies that the route calculation should include an alternate route. The roadClassesToAvoid property specifies that the route should avoid toll roads, ferries, and highways. The distanceMeasurementSystem property specifies that the distance should be measured in imperial units. The profileIdentifier property specifies that the route should be calculated for a car. The departureTime property specifies the time at which the route should start. The locale property specifies the locale in which the route instructions should be written. The mapOption property specifies that the route should be calculated using Valhalla. The shapeFormat property specifies that the route should be returned as a polyline.

  • Calculate the routes.

    1
    Directions.shared.calculate(options) { [weak self] routes, error in
    2
    guard let weakSelf = self else {
    3
    return
    4
    }
    5
    guard error == nil else {
    6
    print(error!)
    7
    return
    8
    }
    9
    10
    guard let routes = routes else { return }

    This code calls the calculate() method on the Directions object. The calculate() method calculates the routes and returns them in an array. The code then checks to make sure that the calculation was successful. If the calculation was successful, the code stores the routes in a variable.

  • Display the routes on the map.

    1
    weakSelf.navigationMapView?.showRoutes(routes)
    2
    weakSelf.navigationMapView?.showRouteDurationSymbol(routes)
    3
    4
    guard let current = routes.first else { return }
    5
    weakSelf.navigationMapView?.showWaypoints(current)
    6
    weakSelf.routes = routes
    7
    }

    This code displays the routes on the map. The showRoutes() method displays the routes as lines on the map. The showRouteDurationSymbol() method displays a symbol on the map that shows the duration of each route. The showWaypoints() method displays the waypoints for each route on the map.

  • Define a delegate method to handle route selection.

    1
    extension DrawAlternativeRouteController: NavigationMapViewDelegate {
    2
    // Delegate method called when the user selects a route
    3
    func navigationMapView(_ mapView: NavigationMapView, didSelect route: Route) {
    4
    guard let routes = routes else { return }
    5
    guard let index = routes.firstIndex(where: { $0 == route }) else { return }
    6
    self.routes!.remove(at: index)
    7
    self.routes!.insert(route, at: 0)
    8
    }
    9
    }

    This code defines a delegate method called navigationMapView(_:didSelect:)