Skip to content

Routing

This section explains the use of route computing functionality including multiple waypoints and alternate routes within an iOS app.

Car and Pedestrian Routing

Sygic Maps SDK for iOS supports route calculation with multiple waypoints, optimized for walking or driving. A route describes a path between at least two waypoints, the starting point and the destination, with optional intermediate waypoints in between.

Applications can provide route information to users in two ways:

  • A line rendered on a map that displays a connecting path between all waypoints
  • Turn-by-turn directions in text format

Example

This guide explains how to compute a route for a car with one additional waypoint.

Expected to continue in ViewController.swift from Getting Started. The final project is available in Sygic SDK Examples: BasicRouting.

Create waypoints

SYWaypoint represents a waypoint, it's used to specify start, destination, and optional waypoints between them.

func createRoute() {
    let start = SYWaypoint(
        position: SYGeoCoordinate(latitude: 51.500432, longitude: -0.126051),
        type: .start,
        name: nil
    )
    let end = SYWaypoint(
        position: SYGeoCoordinate(latitude: 51.505431, longitude: -0.103124),
        type: .end,
        name: nil
    )
    let via = SYWaypoint(
        position: SYGeoCoordinate(latitude: 51.504387, longitude: -0.115477),
        type: .via,
        name: nil
    )
    // ...
}

Create routing options

SYRoutingOptions class represents routing options applicable for the selected route, transportMode property provides the following modes of routing:

  • Car (default)
  • Pedestrian
  • Other (see SYTransportMode enum)
let routingOptions = SYRoutingOptions()
routingOptions.transportMode = .car
routingOptions.routingType = .fastest

A lot of other settings can be set in the routing options, such as settings that trucks would use:

let routingOptions = SYRoutingOptions()
routingOptions.transportMode = .transportTruck
routingOptions.dimensionalOptions?.totalWeight = 44000 // in kg
routingOptions.dimensionalOptions?.height = 4000 // in mm
routingOptions.emission = .euro6
routingOptions.vehicleYear = 2023
routingOptions.hazmatSettings = SYHazmatSettings.init(isGeneralHazardousMaterial: true, isExplosiveMaterial: false, isGoodsHarmfulToWater: false)
routingOptions.fuel = .diesel
routingOptions.maxSpeed = 90 // in kmh
routingOptions.useEndpointProtection = true

Please note that this will only affect the calculated route. If you would like to see the restrictions on map, you can use the SYMapView's method setLogisticInfoSettings:

let logisticInfoSettings = SYMapLogisticInfoSettings()
logisticInfoSettings.vehicleType = .truck
logisticInfoSettings.hazmatSettings = SYHazmatSettings(isGeneralHazardousMaterial: true, isExplosiveMaterial: false, isGoodsHarmfulToWater: false)
logisticInfoSettings.totalVehicleLength = 16500 // in mm
mapView.setLogisticInfoSettings(logisticInfoSettings)

Define a route

The route is defined by specifying start, destination, waypoints and options (waypoints can be nil).

let request = SYRouteRequest(start: start, destination: end, options: routingOptions, viaPoints: [via])

Prepare a request for primary route

SYPrimaryRequest represents a request for computing the primary route. It also delivers details of the computing process specifically for this route (progress and completion events can be ignored by setting to nil).

let primary = SYPrimaryRequest(request) { (progress) in
    print("Primary progress: \(Int(progress * 100))%")
} completion: { [weak self] (route, computeResult) in
    print("Primary compute done: \(route?.info.length ?? 0) meters")
    // TODO: check computeResult
    // TODO: show route on map
}

Compute the route

Call SYRouting.computeRoute(_:alternatives:completion:) to perform the compute. In our case we request only the primary route. Optionally, you can request alternative routes by providing an array of SYAlternativeRequest.

When computing of all requested routes is finished (each can succeed or fail), the completion is called.

Completion of this method provides only the general info of the results. Closer information is available in corresponding events of SYPrimaryRequest and SYAlternativeRequest.

SYRouting.computeRoute(primary, alternatives: nil) { [weak self] (primaryRoute, alternatives) in
    print("Compute fully finished")
    // TODO: check results, show overview of all routes
}

Show the route on map

Typically the route is represented on map by SYMapRoute and SYMapRouteLabel, they are subclasses of SYMapObject.

First, prepare to keep track of the routes already added to the map, in order to remove them later.

class ViewController: UIViewController {
    var routeMapObjects = [SYMapObject]()
    // ...

    func removeAllRoutesFromMap() {
        mapView.remove(routeMapObjects)
        routeMapObjects.removeAll()
    }

Add newly computed routes to the map.

func addRouteToMap(_ route: SYRoute, type: SYMapRouteType) {
    let text = "My route\n\(route.info.length) meters"

    let newMapObjects = [
        SYMapRoute(route: route, type: type),
        SYMapRouteLabel(text: text, textStyle: nil, placeOn: route)
    ]
    routeMapObjects.append(contentsOf: newMapObjects)
    mapView.add(newMapObjects)
}

Show the overview of a few routes by animating SYMapView to their bounding box:

func showBoundingBox(routes: [SYRoute]) {
    guard let firstRoute = routes.first else { return }

    let unionBox = routes.reduce(firstRoute.box) { partialResult, route in
        partialResult.union(with: route.box) ?? partialResult
    }

    // Zoom out with animation to display full route.
    mapView.camera.setViewBoundingBox(
        unionBox,
        with: UIEdgeInsets.init(top: 0.15, left: 0.15, bottom: 0.15, right: 0.15),
        duration: 1.0,
        curve: .accelerateDecelerate,
        completion: nil
    )
}

Download this project

The final project by this guide is a part of Sygic SDK Examples, find it in BasicRouting directory.

iOS Routing

Route Serialization

Route serialization feature allows to convert an already computed route into JSON representation, to restore it later or transfer to another device.

Example:

func saveRoute(_ route: SYRoute) {
    let serializer = SYRouteSerializerBrief()
    guard let json = serializer.serializeRoute(route) else {
        print("Failed to serialize the route")
        return
    }
    UserDefaults.standard.set(json, forKey: "serializedRoute")
}

func restoreRoute() {
    guard let json = UserDefaults.standard.string(forKey: "serializedRoute") else {
        return
    }
    SYRouting.computeRoute(fromJson: json) { (progress) in
        print("Compute from JSON progress: \(Int(progress * 100))%")
    } completion: { [weak self] (route, result) in
        guard let self = self else { return }
        guard let route = route else {
            print("Failed to compute from JSON: \(result.rawValue)")
            return
        }
        self.addRouteToMap(route, type: .primary)
        self.showBoundingBox(routes: [route])
    }
}

Turn preference setting

It is now possible to choose/let the user choose whether it is preferred to use right turns (left turns in the UK, Australia) or turns that cross the line of opposite direction. This can be easily set in the SYRoutingOptions object. There are two possibilities:

Adjacent road turn: Prefer turns to adjacent roads without crossing the line of opposite driving direction. For example, in countries with driving on the right side of the road this means to prefer right turns.

Crossover line turn: Prefer turns with crossing the line of opposite driving direction. For example, in countries with driving on the right side of the road this means to prefer left turns.

Default is adjacentRoadTurn.

let options = SYRoutingOptions()
options.turnPreference = .crossoverLineTurn