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 TomTom Safety Cameras service provides clients with:
- Red light cameras
- Fixed or 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 configure and set up a Virtual Horizon.
Specifying horizon options
To indicate that you are interested in safety location horizon elements, specify 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)
Starting navigation
To enable the Safety Locations feature, you need to configure the safety locations data source when you start navigation. To do that, create a SafetyLocationsConfiguration
object and inject it into into the navigation Configuration constructor. When you create the SafetyLocationsConfiguration
object, you need to specify an API key that grants your application access to the TomTom Safety Cameras service.
1let navigationConfiguration = OnlineTomTomNavigationFactory.Configuration(2 locationProvider: locationProvider,3 routeReplanner: routeReplanner,4 apiKey: "YOUR_API_KEY",5 safetyLocationsConfiguration: SafetyLocationsConfiguration(apiKey: "YOUR_API_KEY")6)78let tomTomNavigation = try OnlineTomTomNavigationFactory.create(configuration: navigationConfiguration)
To retrieve safety locations from the horizon engine, declare a custom NavigationHorizonObserver
.
1class SafetyLocationHorizonObserver: NavigationHorizonObserver {2 private var snapshot: HorizonSnapshot?3 private var position: HorizonPosition?45 var updateBlock: ((HorizonSnapshot, HorizonPosition) -> ())?67 func didUpdateSnapshot(options: HorizonOptions, snapshot: HorizonSnapshot) {8 self.snapshot = snapshot9 }1011 func didUpdatePosition(options: HorizonOptions, position: HorizonPosition) {12 self.position = position1314 if let snapshot {15 updateBlock?(snapshot, position)16 }17 }1819 func didResetHorizon(options: HorizonOptions) {20 // do nothing21 }22}
Register the observer when starting navigation.
1let navigationHorizonObserver = SafetyLocationHorizonObserver()2navigationHorizonObserver.updateBlock = { snapshot, position in3 self.onHorizonUpdated(snapshot: snapshot, position: position)4}56try tomTomNavigation.addHorizonObserver(navigationHorizonObserver, options: horizonOption)78let routePlan = RoutePlan(route: route, routePlanningOptions: routePlanningOptions)9let navigationOptions = NavigationOptions(activeRoutePlan: routePlan)1011try tomTomNavigation.start()
Retrieving and 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. You would have to assess the situation and, if necessary, filter out these safety locations. Therefore, you must assess the risk and, if required, apply additional filtering to ignore those elements.
Crossing into another country’s jurisdiction implies complying with different legislation. The legislation of the new country may be ambiguous with respect to safety locations. To detect a border crossing, you can use the following approach:
Configure HorizonOptions
to also subscribe to country information in addition safety locations:
1let mainPathSearchOptions = try MainPathSearchOptions(2 searchDistancePolicy: RouteLengthPolicy()3)4return try HorizonOptions(5 elementTypes: [.safetyLocationType, .countryInformationType],6 mainPathSearchOptions: mainPathSearchOptions7)
With navigation started, you can listen to horizon updates to retrieve and filter 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}
You can filter the country information elements for a specific country:
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}
You can also filter the safety locations, by filtering against types that you do not want to display:
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}
Then process the retrieved horizon data to filter out safety locations from countries where reporting them is illegal:
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}