Retrieving horizon data

VERSION 0.45.0

The Navigation SDK for iOS is only available upon request. Contact us to get started.

The Virtual Horizon acts as a digital assistant, extending the driver’s location context beyond their immediate view. It can be used by UI components to improve the driving experience, for example, by informing the driver of what is ahead. This information includes details about upcoming traffic events, vehicle restrictions, and more.

horizon image

While navigating, you can use Virtual Horizon to retrieve detailed information about the horizon ahead of the vehicle, along the possible trajectories of the vehicle across the road network. The HorizonEngine delivers this information as a combination of a HorizonPosition and a HorizonSnapshot.

The SDK provides horizon data for various elements, including:

This guide focuses on demonstrating how to utilize the HorizonEngine for extracting specific speed-related data, including the current speed limit, the distance to the next speed limit, and the value of the upcoming speed limit. These insights allow you to create navigation applications that not only guide users accurately but also enhance their road experience by keeping them well-informed.

For more in-depth information, refer to the HorizonEngine API reference.

Configuring the horizon engine

Incorporating horizon data into your navigation application requires the following steps:

Specifying horizon options

First, define the set of HorizonOptions. To indicate that you are interested in SpeedLimitElement horizon elements, specify HorizonElementType in the list of element types of interest.

1let mainPathSearchOptions = try MainPathSearchOptions(
2 searchTime: Measurement.tt.minutes(10),
3 searchDistancePolicy: ExplicitDistancePolicy(
4 searchDistance: PathSearchDistance(
5 maxHorizonLength: Measurement.tt.kilometers(10)
6 )
7 )
8)
9let subPathSearchOptions = try SubPathSearchOptions(
10 searchTime: Measurement.tt.minutes(5),
11 searchDistance: PathSearchDistance(
12 maxHorizonLength: Measurement.tt.kilometers(1)
13 )
14)
15let elementTypes = [
16 HorizonElementType.pathGeometryType,
17 HorizonElementType.speedLimitsType,
18 HorizonElementType.streetType,
19]
20return try HorizonOptions(
21 id: UUID(),
22 elementTypes: elementTypes,
23 mainPathSearchOptions: mainPathSearchOptions,
24 subPathSearchOptions: [subPathSearchOptions],
25 numberOfPaths: 4
26)

Observing horizon updates

Declare a custom observer for horizon updates:

1class SpeedElementHorizonObserver: NavigationHorizonObserver {
2 private var snapshot: HorizonSnapshot?
3 private var position: HorizonPosition?
4
5 var updateBlock: ((HorizonSnapshot, HorizonPosition) -> ())?
6
7 func didUpdateSnapshot(options: HorizonOptions, snapshot: HorizonSnapshot) {
8 self.snapshot = snapshot
9 }
10
11 func didUpdatePosition(options: HorizonOptions, position: HorizonPosition) {
12 self.position = position
13
14 if let snapshot {
15 updateBlock?(snapshot, position)
16 }
17 }
18
19 func didResetHorizon(options: HorizonOptions) {
20 // do nothing
21 }
22}

Starting navigation

Then, start navigation with a route as described in the Starting navigation guide.

But, instead of adding an observer for route progress updates, make sure you add a NavigationHorizonObserver to listen to horizon updates for the horizon options you defined.

1let navigationConfiguration = OnlineTomTomNavigationFactory.Configuration(
2 locationProvider: locationProvider,
3 routeReplanner: routeReplanner
4)
5
6let tomTomNavigation = try! OnlineTomTomNavigationFactory.create(configuration: navigationConfiguration)
7
8let navigationHorizonObserver = SpeedElementHorizonObserver()
9
10try? tomTomNavigation.addHorizonObserver(navigationHorizonObserver, options: options)
11
12let routePlan = RoutePlan(route: route, routePlanningOptions: routePlanningOptions)
13let navigationOptions = NavigationOptions(activeRoutePlan: routePlan)
14
15try? tomTomNavigation.start()

Retrieving horizon data

With navigation started, you can listen to horizon updates and retrieve horizon data.

1navigationHorizonObserver.updateBlock = { snapshot, position in
2 self.displayCurrentSpeedLimit(snapshot: snapshot, position: position)
3 self.displayNextSpeedLimit(snapshot: snapshot, position: position)
4}

Once you have retrieved the data, you can use it to extract the value of the current speed limit.

1guard let elements = snapshot.mainPath()?.getElements(type: .speedLimitsType),
2 let element = elements.first(where: { element in
3 element.startOffset < position.offset &&
4 element.endOffset >= position.offset
5 }),
6 let speedLimitElement = element as? SpeedLimitElement else { return }
7
8switch speedLimitElement.speedLimit {
9case let .limited(speed: speed):
10 print("Speed limit value: \(speed)")
11case .unlimited:
12 print("Unlimited speed")
13@unknown default:
14 print("Unsupported speed limit type")
15}

You can also extract the distance to the next speed limit and the value of the next speed limit.

1guard let elements = snapshot.mainPath()?.getElements(type: .speedLimitsType),
2 let element = elements.first(where: { element in
3 element.startOffset >= position.offset
4 }),
5 let speedLimitElement = element as? SpeedLimitElement else { return }
6
7print("Distance to the next speed limit : \(snapshot.distance(to: element, from: position))")
8
9switch speedLimitElement.speedLimit {
10case let .limited(speed: speed):
11 print("Next speed limit value: \(speed)")
12case .unlimited:
13 print("Next, unlimited speed")
14@unknown default:
15 print("Next, unsupported speed limit type")
16}

Next steps

Now that you know how to retrieve horizon data, here are the recommendations on what to explore next: