Horizon safety locations
The Safety Cameras SDK for iOS 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:
- Red light cameras
- Fixed speed cameras
- Mobile speed cameras
- Speed enforcement zones
- Restricted areas
- Danger zones
- Risk zones
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 HorizonElementType.safetyLocationType
in the list of element types of interest.
1let mainPathSearchOptions = try MainPathSearchOptions(2 searchDistancePolicy: RouteLengthPolicy()3)4return try HorizonOptions(5 elementTypes: [.safetyLocationType],6 mainPathSearchOptions: mainPathSearchOptions7)
Configuring navigation
The Safety Cameras feature requires configuring the safety locations data source before starting navigation. You need to create a SafetyLocationsConfiguration
object and inject it into the navigation OnlineTomTomNavigationFactory.Configuration
constructor. When you set up the SafetyLocationsConfiguration
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.
1let navigationConfiguration = OnlineTomTomNavigationFactory.Configuration(2 navigationTileStore: try NavigationTileStore(config: NavigationTileStoreConfiguration(apiKey: "YOUR_TOMTOM_API_KEY")),3 locationProvider: locationProvider,4 routePlanner: routePlanner,5 safetyLocationsConfiguration: SafetyLocationsConfiguration(apiKey: "YOUR_TOMTOM_API_KEY")6)78let tomTomNavigation = try OnlineTomTomNavigationFactory.create(configuration: navigationConfiguration)
Registering an observer for horizon updates
Before starting navigation, register a NavigationHorizonObserver
to listen to horizon updates for the horizon options you defined.
1let navigationHorizonObserver = SafetyLocationHorizonObserver()2navigationHorizonObserver.updateBlock = { snapshot, position in3 self.onHorizonUpdated(snapshot: snapshot, position: position)4}56try tomTomNavigation.addHorizonObserver(navigationHorizonObserver, options: horizonOption)
Starting navigation
Then, start navigation with a route as described in the Retrieving horizon data guide.
1let routePlan = RoutePlan(route: route, routePlanningOptions: routePlanningOptions)2let navigationOptions = NavigationOptions(activeRoutePlan: routePlan)34try tomTomNavigation.start()
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:
- Configure
HorizonOptions
to also subscribe to country information in addition to safety locations:1let mainPathSearchOptions = try MainPathSearchOptions(2 searchDistancePolicy: RouteLengthPolicy()3)4return try HorizonOptions(5 elementTypes: [.safetyLocationType, .countryInformationType],6 mainPathSearchOptions: mainPathSearchOptions7) - Implement a listener to get horizon updates and filter out safety locations:
1func onHorizonUpdated(snapshot: HorizonSnapshot, position: HorizonPosition) {2 guard let mainPath = snapshot.mainPath() else { return }34 let forbiddenCountryInformationElements = retrieveForbiddenCountryInformationElements(horizonPath: mainPath)5 let safetyLocationElements = retrieveSafetyLocationElements(horizonPath: mainPath)67 let legalSafetyLocationElements = retrieveLegalSafetyLocationElements(8 safetyLocationElements: safetyLocationElements,9 forbiddenCountryInformationElements: forbiddenCountryInformationElements10 )1112 legalSafetyLocationElements.forEach { safetyLocation in13 print("Safety location: \(safetyLocation)")14 }15}
- Extract the country information elements that correspond to countries where reporting safety locations is illegal.
1func retrieveForbiddenCountryInformationElements(horizonPath: HorizonPath) -> [CountryInformationElement] {2 return horizonPath.getElements(type: .countryInformationType)3 .compactMap { $0 as? CountryInformationElement }4 .filter {5 // Allows only country information elements from Germany or the US state of California6 ($0.countryCodeISO3 == "DEU") ||7 ($0.countryCodeISO3 == "USA" && $0.regionCode == "CA")8 }9}
- Filter out the safety locations from those countries:
1func retrieveLegalSafetyLocationElements(2 safetyLocationElements: [SafetyLocationElement],3 forbiddenCountryInformationElements: [CountryInformationElement]4) -> [SafetyLocationElement] {5 return safetyLocationElements.filter { safetyLocation in6 !forbiddenCountryInformationElements.contains { countryInformation in7 isInCountry(safetyLocationElement: safetyLocation, countryInformationElement: countryInformation)8 }9 }10}1112func isInCountry(13 safetyLocationElement: SafetyLocationElement,14 countryInformationElement: CountryInformationElement15)16 -> Bool {17 if safetyLocationElement.pathID == countryInformationElement.pathID {18 return safetyLocationElement.startOffset >= countryInformationElement.startOffset &&19 safetyLocationElement.startOffset < countryInformationElement.endOffset ||20 safetyLocationElement.endOffset > countryInformationElement.startOffset &&21 safetyLocationElement.endOffset <= countryInformationElement.endOffset22 } else {23 return false24 }25}
You can also filter out specific safety location types:
1func retrieveSafetyLocationElements(horizonPath: HorizonPath) -> [SafetyLocationElement] {2 return horizonPath.getElements(type: .safetyLocationType)3 .compactMap { $0 as? SafetyLocationElement }4 .filter {5 // Filters out safety locations of type RestrictionCamera6 $0.safetyLocation.type != .restrictionCamera7 }8}