Horizon safety locations

VERSION 1.1.0
PUBLIC PREVIEW

The Safety Cameras SDK for Android is only available upon request. Contact us to get started.

Legal Restrictions.

Local law in certain jurisdictions can prohibit the use of speed camera warning and detection systems. To the extent a TomTom customer uses the Speed Camera Data in connection with any such warning or detection system, the TomTom customer shall do so entirely at their own risk. TomTom shall be entitled to immediately cease the delivery of the Speed Camera Data or part thereof in the event delivery of such Speed Camera Data infringes applicable law. In order to comply with the applicable legal requirements, application developers can use the Navigation SDK to filter safety cameras with the purpose of:

  • disabling/enabling all types of safety cameras for an individual country
  • disabling/enabling all types of safety cameras for a region of a country
  • disabling/enabling some types of safety cameras (fixed/mobile) for a specific country

Safety locations are used worldwide to enforce speed limits and other traffic regulations. Cameras are frequently placed in high-risk areas to minimize the impact of difficult and dangerous stretches of road.

The Safety Cameras SDK provides data for various types of safety locations, including:

Configuring the horizon engine

Having completed the Retrieving horizon data guide, you are now able to set up the horizon engine and start navigation to retrieve horizon data.

Specifying horizon options

To subscribe to safety location horizon elements, specify SafetyLocationElementType in the list of element types of interest.

1private val horizonOptionsSafetyLocations = HorizonOptions(
2 elementTypes = listOf(SafetyLocationElementType),
3 mainPathSearchOptions = MainPathSearchOptions(
4 searchDistancePolicy = RouteLengthPolicy
5 )
6)

Configuring navigation

The Safety Cameras feature requires configuring the safety locations data source before starting navigation. You need to create a SafetyLocationConfiguration object and inject it into the navigation Configuration constructor. When you set up the SafetyLocationConfiguration object, specify the API key that grants your application access to the TomTom Safety Cameras service.

In addition to configuring the safety locations data source, you also need to increase the navigation tile prefetching radius to 15 km. This ensures that enough map data is prefetched to allow exposing safety location data at the current vehicle position. With a smaller prefetching radius there is no guarantee that a zone safety location is exposed in the horizon.

1val navigationTileStore = NavigationTileStore.create(
2 this,
3 NavigationTileStoreConfiguration(
4 apiKey = TOMTOM_API_KEY,
5 prefetchingConfiguration = PrefetchingConfiguration(prefetchedAreaRadius = PREFETCHED_AREA_RADIUS)
6 )
7)
8val navigationConfiguration = Configuration(
9 context = this,
10 locationProvider = locationProvider,
11 navigationTileStore = navigationTileStore,
12 routePlanner = routePlanner,
13 safetyLocationsConfiguration = SafetyLocationsConfiguration(TOMTOM_API_KEY)
14)

Registering a horizon updated listener

Before starting navigation, register a HorizonUpdatedListener to listen to horizon updates for the horizon options you defined.

val tomTomNavigation = OnlineTomTomNavigationFactory.create(navigationConfiguration)
tomTomNavigation.addHorizonUpdatedListener(horizonOptionsSafetyLocations, horizonUpdatedListener)

Starting navigation

Then, start navigation with a route as described in the Retrieving horizon data guide.

1val routePlan = RoutePlan(route, routePlanningOptions)
2val navigationOptions = NavigationOptions(routePlan)
3tomTomNavigation.start(navigationOptions)

Retrieving safety locations

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

If a zone safety location stretches beyond the end of the horizon, the SDK only provides data for the part of the zone within the horizon length. As the horizon is extended ahead of the vehicle while the vehicle moves along the zone safety location, the end offset of the safety location horizon element is updated accordingly.

Filtering safety locations

Some of the retrieved safety locations might be located in a country where it is prohibited to use devices that alert the driver about safety locations. Moreover, the legislation of a country may also be ambiguous with respect to safety locations. You have to assess the situation and, if necessary, filter out safety locations for which it is illegal to issue alerts.

Crossing into another country implies complying with different legislation. To filter out safety locations from countries where reporting them is illegal, you can take the following steps:

  1. Configure HorizonOptions to also subscribe to country information in addition to safety locations:
    1private val horizonOptions = HorizonOptions(
    2 elementTypes = listOf(SafetyLocationElementType, CountryInformationElementType),
    3 mainPathSearchOptions = MainPathSearchOptions(
    4 searchDistancePolicy = RouteLengthPolicy
    5 )
    6)
  2. Implement a listener to get horizon updates and filter out safety locations:
    1private val horizonUpdatedListener = object : HorizonUpdatedListener {
    2 private var horizonSnapshot: HorizonSnapshot? = null
    3
    4 override fun onPositionUpdated(options: HorizonOptions, position: HorizonPosition) {
    5 horizonSnapshot?.let { horizonSnapshot ->
    6 horizonSnapshot.mainPath()?.let { mainPath ->
    7 val forbiddenCountryInformationElements = mainPath.retrieveForbiddenCountryInformationElements()
    8 val safetyLocationElements = mainPath.retrieveSafetyLocationElements()
    9
    10 val legalSafetyLocationElements =
    11 retrieveLegalSafetyLocationElements(safetyLocationElements, forbiddenCountryInformationElements)
    12
    13 Log.v(TAG, "Safety locations for which alerts are legal:")
    14 legalSafetyLocationElements.forEach { safetyLocation ->
    15 Log.v(TAG, "$safetyLocation")
    16 }
    17 }
    18 }
    19 }
    20
    21 override fun onHorizonReset(options: HorizonOptions) {
    22 Log.v(TAG, "Horizon reset")
    23 }
    24
    25 override fun onSnapshotUpdated(options: HorizonOptions, snapshot: HorizonSnapshot) {
    26 horizonSnapshot = snapshot
    27 }
  3. Extract the country information elements that correspond to countries where reporting safety locations is illegal.
    1private fun HorizonPath.retrieveForbiddenCountryInformationElements(): List<CountryInformationElement> =
    2 getElements(CountryInformationElementType)
    3 .map { it as CountryInformationElement }
    4 .filter {
    5 // Allows only country information elements from Germany or the US state of California
    6 (it.countryCodeIso3 == "DEU") ||
    7 (it.countryCodeIso3 == "USA" && it.regionCode == "CA")
    8 }
  4. Filter out the safety locations from those countries:
    1private fun retrieveLegalSafetyLocationElements(
    2 safetyLocationElements: List<SafetyLocationElement>,
    3 forbiddenCountryInformationElements: List<CountryInformationElement>
    4): List<SafetyLocationElement> =
    5 safetyLocationElements.filterNot { safetyLocationElement ->
    6 forbiddenCountryInformationElements.map { countryInformationElement ->
    7 safetyLocationElement.isInCountry(countryInformationElement)
    8 }.any { it }
    9 }
    10
    11private fun SafetyLocationElement.isInCountry(countryInformationElement: CountryInformationElement): Boolean =
    12 if (this.pathId == countryInformationElement.pathId) {
    13 this.startOffset >= countryInformationElement.startOffset &&
    14 this.startOffset < countryInformationElement.endOffset ||
    15 this.endOffset > countryInformationElement.startOffset &&
    16 this.endOffset <= countryInformationElement.endOffset
    17 } else {
    18 false
    19 }

You can also filter out specific safety location types:

1private fun HorizonPath.retrieveSafetyLocationElements(): List<SafetyLocationElement> =
2 getElements(SafetyLocationElementType)
3 .map { it as SafetyLocationElement }
4 .filter { element ->
5 // Filters out safety locations of type RestrictionCamera
6 element.safetyLocation.type != SafetyLocationType.RestrictionCamera
7 }