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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
import UIKit
import NbmapNavigation
import NbmapCoreNavigation
import NbmapDirections
import Nbmap

class DrawAlternativeRouteController: UIViewController, NGLMapViewDelegate {
    
    var navigationMapView: NavigationMapView? {
        didSet {
            oldValue?.removeFromSuperview()
            if let navigationMapView = navigationMapView {
                view.insertSubview(navigationMapView, at: 0)
            }
        }
    }
    
    var routes : [Route]? {
        didSet {
            guard let routes = routes,
                  let current = routes.first else { navigationMapView?.removeRoutes(); return }
            
            navigationMapView?.showRoutes(routes)
            navigationMapView?.showWaypoints(current)
            navigationMapView?.showRouteDurationSymbol(routes)
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
        
        let origin = CLLocation(latitude: 37.77440680146262, longitude: -122.43539772352648)
        let destination = CLLocation(latitude: 37.779664450073, longitude: -122.43245802247301)
        
        self.navigationMapView = NavigationMapView(frame: view.bounds)
        navigationMapView?.userTrackingMode = .followWithHeading
        navigationMapView?.delegate = self
        navigationMapView?.navigationMapDelegate = self
        
        self.view.setNeedsLayout()
        
        requestRoutes(origin: origin, destination: destination)
    }
    
    func requestRoutes(origin: CLLocation, destination: CLLocation){
        
        let options = NavigationRouteOptions(origin: origin, destination: destination)
        /**
         Sets whether the  route contains an alternate route
         By default , it set to `false`
         */
        options.includesAlternativeRoutes = true
        
        /**
         The route classes that the calculated routes will avoid.
         We can set an array road class to avoid.  This property can be set to `.toll`,`.ferry`,`.highway`
         By default ,  this property is set to empty
         */
        options.roadClassesToAvoid = [.toll,.ferry,.highway]
        /**
         Set up the navigation measurement unit
         This property should be set to `.metric` or `.imperial`
         By default ,  this property is set to the unit follow with current system locale.
         */
        options.distanceMeasurementSystem = .imperial
        
        /**
         Set  specifying the primary mode of transportation for the routes.
         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.
         */
        options.profileIdentifier = NBNavigationMode.mode4W
        /**
         Set the  departureTime of the route , By default ,  it sets the current timestamp since from 1970
         */
        options.departureTime = Int(Date().timeIntervalSince1970)
        
        /**
         Set up the locale in which the route's instructions are written.
         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`.
         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.
         By default, this property is set to the current system locale.
         */
        options.locale = Locale.autoupdatingCurrent
        
        /**
         Set map options, This property may make the ETA more accurate.  If set to `NBMapOption.valhala`, shapeFormat needs to be set to `polyline`.
         By default , the value of this  property is `NBMapOption.none`
         */
        options.mapOption = NBMapOption.none
        /**
         Format of the data from which the shapes of the returned route and its steps are derived.
         
         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.
         
         The default value of this property is `polyline6`.
         */
        options.shapeFormat = .polyline6
        
        Directions.shared.calculate(options) { [weak self] routes, error in
            guard let weakSelf = self else {
                return
            }
            guard error == nil else {
                print(error!)
                return
            }
            
            guard let routes = routes else { return }
            
            
            // Process or display routes information.For example, display the routes, waypoints and duration symbol on the map
            weakSelf.navigationMapView?.showRoutes(routes)
            weakSelf.navigationMapView?.showRouteDurationSymbol(routes)
            
            guard let current = routes.first else { return }
            weakSelf.navigationMapView?.showWaypoints(current)
            weakSelf.routes = routes
        }
    }
}

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

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.

    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
    2
    3
    4
    5
    6
    7
    8
    options.includesAlternativeRoutes = true
    options.roadClassesToAvoid = [.toll, .ferry, .highway]
    options.distanceMeasurementSystem = .imperial
    options.profileIdentifier = NBNavigationMode.mode4W
    options.departureTime = Int(Date().timeIntervalSince1970)
    options.locale = Locale.autoupdatingCurrent
    options.mapOption = NBMapOption.none
    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
    2
    3
    4
    5
    6
    7
    8
    9
    10
    Directions.shared.calculate(options) { [weak self] routes, error in
        guard let weakSelf = self else {
            return
        }
        guard error == nil else {
            print(error!)
            return
        }
    
        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
    2
    3
    4
    5
    6
    7
    weakSelf.navigationMapView?.showRoutes(routes)
        weakSelf.navigationMapView?.showRouteDurationSymbol(routes)
    
        guard let current = routes.first else { return }
        weakSelf.navigationMapView?.showWaypoints(current)
        weakSelf.routes = routes
    }

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

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

Have Questions ?