Routing | Mobile SDK | Urbi Documentation
iOS SDK

Routing

This chapter describes how to build a route for different types of transport and display it on the map. To build any type of route, you need to define the following components:

  • Route points: the starting one, the finishing one, and (if needed) intermediate ones. Ways of getting point coordinates are described below.
  • Route search parameters. A set of parameters depends on the transport type that the route is built for.

To create a route on the map, you need to create two objects:

To find a route between two points, call the findRoute() method and specify the coordinates of the start and end points as RouteSearchPoint objects. You can additionally specify route parameters (RouteSearchOptions) and a list of intermediate points (list of RouteSearchPoint objects).

let startPoint = RouteSearchPoint(coordinates: GeoPoint(latitude: 55.759909, longitude: 37.618806))
let finishPoint = RouteSearchPoint(coordinates: GeoPoint(latitude: 55.752425, longitude: 37.613983))
let routeSearchOptions = RouteSearchOptions.car(CarRouteSearchOptions())

let trafficRouter = TrafficRouter(context: sdk.context)
let routesFuture = trafficRouter.findRoute(
	startPoint: startPoint,
	finishPoint: finishPoint,
	routeSearchOptions: routeSearchOptions
)

The findRoute() call returns a deferred result with a list of TrafficRoute objects. To display the found route on the map, you need to use these objects to create RouteMapObject objects and add them to a RouteMapObjectSource data source.

// Create a data source
let routeMapObjectSource = RouteMapObjectSource(context: sdk.context, routeVisualizationType: .normal)
map.addSource(source: routeMapObjectSource)

// Find a route
self.routeSearchCancellable = routesFuture.sink { routes in
	// After receiving the route, add it to the map
	for (index, route) in routes.enumerated() {
		let routeMapObject = RouteMapObject(
			route: route,
			isActive: index == 0,
			index: RouteIndex(value: UInt64(index)),
			displayFlags: nil
		)
		routeMapObjectSource.addObject(item: routeMapObject)
	}
} failure: { error in
	print("Can't find route: \(error)")
}

To build a route, you need to get points of the GeoPoint type, which are then used to create RouteSearchPoint. You can do it in multiple ways, two of them are described below.

To get geographic coordinates of the tap point, get a ScreenPoint like in the example of Getting objects using screen coordinates:

func tap(location: CGPoint) {
	let scale = UIScreen.main.nativeScale
	let point = ScreenPoint(x: Float(location.x * scale), y: Float(location.y * scale))
	...

You can transfer the ScreenPoint into geographic coordinates using the screenToMap() method:

func tap(location: CGPoint) {
	let scale = UIScreen.main.nativeScale
	let point = ScreenPoint(x: Float(location.x * scale), y: Float(location.y * scale))
	let geoPoint = map.camera.projection.screenToMap(point: point)
	...

As a result, you get coordinates of the map tap point.

You can get coordinates of an object (building, road) that is located at the tap point: use the getRenderedObjects() method. In this method, get an object of the DgisMapObject class with the id field. This ID is a stable numerical identifier of a directory object that allows you to send a search request and get information about an object, including geographic coordinates of its center.

private func tap(point: ScreenPoint, tapRadius: ScreenDistance) {
	let scale = UIScreen.main.nativeScale
	let point = ScreenPoint(x: Float(location.x * scale), y: Float(location.y * scale))
	self.getRenderedObjectsCancellable?.cancel()
	let cancel = self.map.getRenderedObjects(centerPoint: point).sink(
		receiveValue: {
			infos in
			// Getting a DgisMapObject class object that is the closest to the tap
				guard let info = infos.first(
					where: {
						$0.item.item is DgisMapObject
					}
				) else { return }
				let mapObject = info.item.item as! DgisMapObject

				// Creating a search request using the ID of this object
				self.searchCancellable?.cancel()
				self.searchCancellable = self.searchManager.searchByDirectoryObjectId(
					objectId: mapObject.id
				)
				// Getting an object from the search request
				.sinkOnMainThread(receiveValue: { [weak self] object in
					self?.directoryObject = object
					// Getting object coordinates as GeoPoint
					let geoPoint = self?.directoryObject?.markerPosition?.point
				},
					failure: { .. }
				)

		},
		failure: { ... }
	)
	...
}

Important

Coordinates of a center of a large object (for example, long and complicated ring roads) can be located far away from the camera viewport as the received object does not contain information on the camera location or the map tap spot. In such cases, use other methods to get coordinates.

For a route with intermediate points, a user is expected to visit each point in the defined order. If an intermediate point is missed, a route may be rebuilt:

  • If the GPS data shows that the user is moving not in the direction of the expected intermediate point, the route is rebuilt relatively to the current user location so that they visit the missed point. When the visit of the intermediate point is detected, the route continues.
  • If the moment of visiting the intermediate point is not detected due to a weak GPS signal but later the user location is detected on the route or next to it towards the next point, the previous intermediate point is considered visited and the route continues.

To build a route for a car, specify the car property in RouteSearchOptions. Additionally, you can configure route search parameters CarRouteSearchOptions:

  • avoidTollRoads: whether to avoid toll roads (false by default).
  • avoidUnpavedRoads: whether to avoid unpaved roads (false by default).
  • avoidFerries: whether to avoid ferry crossings (false by default).
  • avoidLockedRoads: whether to avoid roads that are closed for passage (true by default).
  • routeSearchType: a way of considering traffic jam data when building a route. You can build a route based on real-time (jam) or statistical (statistic) traffic jam data or you can build the shortest route in distance even if it is not optimal in time (shortest).
  • excludedAreas: a list of areas (not more than 25) to avoid when building a route. For details, see the Excluding areas section below.
let carOptions = CarRouteSearchOptions(
    avoidTollRoads: true,
    avoidUnpavedRoads: true,
    avoidFerries: true,
    avoidLockedRoads: true,
    routeSearchType: RouteSearchType.jam,
    excludedAreas: []
)

let routeSearchOptions = RouteSearchOptions.car(carOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

To build a route for a taxi, specify the taxi property in RouteSearchOptions.

Additionally, you can configure route search parameters TaxiRouteSearchOptions. To build a taxi route, you need to define search parameters for common car routes first and then reuse them in the car parameter:

let taxiOptions = TaxiRouteSearchOptions(
    car: carOptions,
)

let routeSearchOptions = RouteSearchOptions.taxi(taxikOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

To build a route for a truck, specify the truck property in RouteSearchOptions. Additionally, you can configure route search parameters TruckRouteSearchOptions:

  • car: common parameters for any car routes: see search parameters for car routes.
  • truckLength: truck length in millimeters.
  • truckHeight: truck height in millimeters.
  • truckWidth: truck width in millimeters.
  • actualMass: actual mass of the truck in kilograms.
  • maxPermittedMass: maximum permitted of a truck in kilograms.
  • axleLoad: axle load in kilograms.
  • dangerousCargo: whether dangerous cargo is transported (false by default).
  • explosiveCargo: whether explosive cargo is transported (false by default).
  • passIds: a list of IDs of the passes that allow the truck to drive through pass zones (TruckPassZonePassId).
  • fallbackOnCar: whether to switch to car routing if a truck route with specified parameters cannot be built (false by default).
let truckOptions = TruckRouteSearchOptions(
    car: carOptions,
    truckLength: 12000,                 // 12 meters
    truckHeight: 4200,                  // 4.2 meters
    truckWidth: 2500,                   // 2.5 meters
    actualMass: 18000,                  // 18 tons
    maxPermittedMass: 20000,            // 20 tons
    axleLoad: 8000,                     // 8 tons
    dangerousCargo: true,
    explosiveCargo: false,
    passIds: Set<TruckPassZonePassId>(),
    fallbackOnCar: true
)

let routeSearchOptions = RouteSearchOptions.truck(truckOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

To build a route for a public transport, specify the publicTransport property in RouteSearchOptions. Additionally, you can configure route search parameters PublicTransportRouteSearchOptions:

  • startTime: route start time in UTC. If not specified, the current time is used.
  • useSchedule: whether to consider the public transport schedule.
  • transportTypes: a list of public transport types that can be used for completing the route. If the list is empty, a route is built for all supported transport types. See the full list of types in PublicTransportTypeOptionSet.
let publicTransportOptions = PublicTransportRouteSearchOptions(
    startTime: Date(),
    useSchedule: true,
    transportTypes: [
        .bus,
        .trolleybus,
        .tram,
        .metro,
        .suburbanTrain
    ]
)

let routeSearchOptions = RouteSearchOptions.publicTransport(publicTransportOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

To build a route for a bicycle, specify the bicycle property in RouteSearchOptions. Additionally, you can configure route search parameters BicycleRouteSearchOptions:

  • avoidCarRoads: whether to avoid car roads (false by default).
  • avoidStairways: whether to avoid stairways (false by default).
  • avoidUnderpassesAndOverpasses: whether to avoid underpasses and overpasses (false by default).
  • excludedAreas: a list of areas (not more than 25) to avoid when building a route. For details, see the Excluding areas section below.
let bicycleOptions = BicycleRouteSearchOptions(
    avoidCarRoads: false,
    avoidStairways: true,
    avoidUnderpassesAndOverpasses: true,
    excludedAreas: []
)

let routeSearchOptions = RouteSearchOptions.bicycle(bicycleOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

To build a route for a scooter, specify the scooter property in RouteSearchOptions. Additionally, you can configure route search parameters ScooterRouteSearchOptions:

  • avoidCarRoads: whether to avoid car roads (true by default).
  • avoidStairways: whether to avoid stairways (true by default).
  • avoidUnderpassesAndOverpasses: whether to avoid underpasses and overpasses (true by default).
  • excludedAreas: a list of areas (not more than 25) to avoid when building a route. For details, see the Excluding areas section below.
let scooterOptions = ScooterRouteSearchOptions(
    avoidCarRoads: true,
    avoidStairways: true,
    avoidUnderpassesAndOverpasses: true,
    excludedAreas: []
)

let routeSearchOptions = RouteSearchOptions.scooter(scooterOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

Pedestrian routes can include moving inside a building (for example, you can build a route to a specific shop in a shopping mall). If some route point is located indoors, you must specify additional parameters for it: objectId (ID of the object to build a route to) and levelId (ID of the building floor where the object is located).

let indoorPoint = RouteSearchPoint(
    coordinates: GeoPoint(latitude: 55.752425, longitude: 37.613983),
    objectId: DgisObjectId(objectId: 67890, entranceId: 2),
    levelId: LevelId(value: 3)
)

For more information about indoor navigation, see the Navigation chapter.

To build a pedestrian route, specify the pedestrian property in RouteSearchOptions. Additionally, you can configure route search parameters PedestrianRouteSearchOptions:

  • avoidStairways: whether to avoid stairways (false by default).
  • avoidUnderpassesAndOverpasses: whether to avoid underpasses and overpasses (false by default).
  • useIndoor: whether to build indoor routes (true by default).
  • excludedAreas: a list of areas (not more than 25) to avoid when building a route. For details, see the Excluding areas section below.
let pedestrianOptions = PedestrianRouteSearchOptions(
    avoidStairways: false,
    avoidUnderpassesAndOverpasses: false,
	useIndoor: false,
    excludedAreas: []
)

let routeSearchOptions = RouteSearchOptions.pedestrian(pedestrianOptions)

Pass the created routeSearchOptions to the findRoute() function call when building a route.

To exclude a specific area when building a route, create an ExcludedArea object with the following parameters:

  • type: geometry of the excluded area. See the list of available values in ExcludedAreaType.
  • severity: priority of excluding the area (how critical it is to avoid the area). See the list of available values in ExcludedAreaSeverity.
  • extent: size of the excluded area (not more than 25 km).
  • points: coordinates of excluded area points of the GeoPoint type (not more than 500).

Example of an excluded polygon:

// Polygon vertices
let points = [
    GeoPoint(latitude: 55.759909, longitude: 37.618806),
    GeoPoint(latitude: 55.752425, longitude: 37.613983),
    GeoPoint(latitude: 55.753567, longitude: 37.622995),
    GeoPoint(latitude: 55.760523, longitude: 37.625613)
]

let polygonExcludedArea = ExcludedArea(
    type: ExcludedAreaType.polygon,
    severity: ExcludedAreaSeverity.hard,
    extent: RouteDistance(value: 500),
    points: points
)

By default, routing works in hybrid mode: using data from Urbi servers is prioritized. If a route cannot be built from online data after a defined timeout, preloaded data is used.

To load data for offline usage, you must get respective rights for your access key and download required territories. See preparation steps for working with offline data.