Traffic Engine
With the TrafficEngine, you can query detailed and localizable information on current traffic incidents such as accidents, construction works or road closures.
You can visualize traffic incidents on the map by enabling the map layer state trafficIncidents with one line of code. The HERE SDK supports also a separate layer to see the current traffic situation by adding a trafficFlow layer.
In addition, you can also indicate the traffic along a Route instance as shown in the Visualize traffic on routes section.
Data queried with the TrafficEngine is based on the HERE Traffic API v7.
NoteFor information about the pricing of this feature, see the HERE Base Plan Pricing. If you are using the Navigate license or have other questions about pricing, contact us.
Initialize the TrafficEngine
With the TrafficEngine, you can retrieve detailed information about ongoing traffic incidents. You can search for incidents along a route via a GeoCorridor, in a GeoBox or in a GeoCircle.
You can also set multiple options, for example, to search only for specific types of incidents. As a result you get results on the affected vehicle types, date information, location information, localizable descriptions, summaries and much more.
Start by creating a new instance of the TrafficEngine:
do {
try trafficEngine = TrafficEngine()
} catch let engineInstantiationError {
fatalError("Failed to initialize TrafficEngine. Cause: \(engineInstantiationError)")
}Initiate traffic requests
Below we search inside a GeoCircle for possible TrafficIncidents:
private func queryForIncidents(centerCoords: GeoCoordinates) {
let geoCircle = GeoCircle(center: centerCoords, radiusInMeters: 1000)
let trafficIncidentsQueryOptions = TrafficIncidentsQueryOptions()
// Optionally, specify a language:
// If the language is not supported, then the default behavior is applied and
// the language of the country where the incident occurs is used.
// trafficIncidentsQueryOptions.languageCode = LanguageCode.enUs
trafficEngine.queryForIncidents(inside: geoCircle,
queryOptions: trafficIncidentsQueryOptions,
completion: onTrafficIncidentsFound);
}
// TrafficIncidentQueryCompletionHandler to receive traffic items.
func onTrafficIncidentsFound(error: TrafficQueryError?,
trafficIncidentsList: [TrafficIncident]?) {
if let trafficQueryError = error {
print("TrafficQueryError: \(trafficQueryError)")
return
}
// If error is nil, it is guaranteed that the list will not be nil.
var trafficMessage = "Found \(trafficIncidentsList!.count) result(s). See log for details."
let nearestIncident = getNearestTrafficIncident(currentGeoCoords: tappedGeoCoordinates,
trafficIncidentsList: trafficIncidentsList!)
trafficMessage.append(contentsOf: " Nearest incident: \(nearestIncident?.description.text ?? \"nil\")")
showDialog(title: "Nearby traffic incidents",
message: trafficMessage)
for trafficIncident in trafficIncidentsList! {
print(trafficIncident.description.text)
}
}Handle traffic responses
For each found TrafficIncident, the app logs its description. If you search in Germany, by default the language of the results is German.
You can also specify another language, for example, by setting this:
trafficIncidentsQueryOptions.languageCode = LanguageCode.enUsNote that trafficIncidents is a layer that renders traffic icons and lines to indicate the location of incidents.
Each TrafficIncident contains a GeoPolyline that indicates its location. By comparing a tapped location on the map with the geographic coordinates contained in the polyline, you can find the closest incident to a tapped location on the map:
private func getNearestTrafficIncident(currentGeoCoords: GeoCoordinates,
trafficIncidentsList: [TrafficIncident]) -> TrafficIncident? {
if trafficIncidentsList.count == 0 {
return nil
}
// By default, traffic incidents results are not sorted by distance.
var nearestDistance: Double = Double.infinity
var nearestTrafficIncident: TrafficIncident!
for trafficIncident in trafficIncidentsList {
var trafficIncidentPolylines: [GeoPolyline] = []
// It is guaranteed that each incident has a valid polyline.
trafficIncidentPolylines.append(trafficIncident.location.polyline)
// If the polyline contains any gaps, they are available as additionalPolylines in TrafficLocation.
trafficIncidentPolylines.append(contentsOf: trafficIncident.location.additionalPolylines)
for polyline in trafficIncidentPolylines {
for geoCoords in polyline.vertices {
let currentDistance = currentGeoCoords.distance(to: geoCoords)
if currentDistance < nearestDistance {
nearestDistance = currentDistance
nearestTrafficIncident = trafficIncident
}
}
}
}
return nearestTrafficIncident
}Of course, it is also possible to render the polyline of an incident by yourself on the map, by adding a MapPolyline. You can also add your own icons as MapMarker - depending on the type of the incident you may decide to choose a different image.
Query traffic flow information
With the TrafficEngine you can also query information on realtime traffic jams by calling trafficEngine.queryForFlow(...) to make an asynchronous request. Note that the data might differ from the data shown in the trafficFlow layer as the time of the request may happen at a different time and may be visualized differently on app-side.
private func requestRealtimeTrafficOnRoute(route: Route) {
// We are interested to see traffic also for side paths.
let halfWidthInMeters = 500
let geoCorridor = GeoCorridor(polyline: route.geometry.vertices,
halfWidthInMeters: Int32(halfWidthInMeters))
let trafficFlowQueryOptions = TrafficFlowQueryOptions()
trafficEngine.queryForFlow(inside: geoCorridor,
queryOptions: trafficFlowQueryOptions) { (trafficQueryError, trafficFlowList) in
if let error = trafficQueryError {
self.showDialog(title: "Error while fetching traffic flow", message: "\(error)")
return
}
if let trafficFlows = trafficFlowList {
for trafficFlow in trafficFlows {
guard let confidence = trafficFlow.confidence, confidence > 0.5 else {
// Exclude speed-limit data and include only real-time and historical
// flow information.
continue
}
// Visualize all polylines unfiltered as we get them from the TrafficEngine.
let trafficGeoPolyline = trafficFlow.location.polyline
self.addTrafficPolylines(jamFactor: trafficFlow.jamFactor,
geoPolyline: trafficGeoPolyline)
}
}
}
}As a next step, you can render the traffic flow polylines using your own color encoding:
private func addTrafficPolylines(jamFactor: Double, geoPolyline: GeoPolyline) {
guard let lineColor = getTrafficColor(jamFactor: jamFactor) else {
// We skip rendering low traffic.
return
}
let widthInPixels: Float = 10
do {
let trafficSpanMapPolyline = try MapPolyline(geometry: geoPolyline,
representation: MapPolyline.SolidRepresentation(
lineWidth: MapMeasureDependentRenderSize(
sizeUnit: RenderSize.Unit.pixels,
size: Double(widthInPixels)),
color: lineColor,
capShape: LineCap.round))
mapView.mapScene.addMapPolyline(trafficSpanMapPolyline)
mapPolylines.append(trafficSpanMapPolyline)
} catch let error {
print("Failed to create MapPolyline: \(error.localizedDescription)")
}
}
// Define a traffic color scheme based on the traffic jam factor.
// 0 <= jamFactor < 4: No or light traffic.
// 4 <= jamFactor < 8: Moderate or slow traffic.
// 8 <= jamFactor < 10: Severe traffic.
// jamFactor = 10: No traffic, ie. the road is blocked.
// Returns null in case of no or light traffic.
private func getTrafficColor(jamFactor: Double) -> UIColor? {
if jamFactor < 4 {
return nil
} else if jamFactor < 8 {
return UIColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 0.63) // Yellow
} else if jamFactor < 10 {
return UIColor(red: 1.0, green: 0.0, blue: 0.0, alpha: 0.63) // Red
}
return UIColor(red: 0.0, green: 0.0, blue: 0.0, alpha: 0.63) // Black
}Try the Traffic example app
A usage example is available on GitHub as part of the "Traffic" example app.
Updated 10 hours ago