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

docs-image

For all code examples, refer to Navigation Code Examples

DrawAlternativeRouteController view source

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

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.

    1let 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.

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

    1Directions.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.

    1weakSelf.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:)