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:
try {
_trafficEngine = TrafficEngine();
} on InstantiationException {
throw ("Initialization of TrafficEngine failed.");
}Initiate traffic requests
Below we search inside a GeoCircle for possible TrafficIncidents:
void _queryForIncidents(GeoCoordinates centerCoords) {
double radiusInMeters = 1000;
GeoCircle geoCircle = GeoCircle(centerCoords, radiusInMeters);
TrafficIncidentsQueryOptions trafficIncidentsQueryOptions = TrafficIncidentsQueryOptions();
// Optionally, specify a language:
// the language of the country where the incident occurs is used.
// trafficIncidentsQueryOptions.languageCode = LanguageCode.enUs;
_trafficEngine.queryForIncidentsInCircle(geoCircle, trafficIncidentsQueryOptions,
(TrafficQueryError? trafficQueryError, List<TrafficIncident>? trafficIncidentsList) {
if (trafficQueryError != null) {
_showDialog("TrafficQueryError", "Error: " + trafficQueryError.toString());
return;
}
// If error is null, list is guaranteed to be not empty.
String trafficMessage = "Found ${trafficIncidentsList!.length} result(s). See log for details.";
TrafficIncident? nearestIncident = _getNearestTrafficIncident(centerCoords, trafficIncidentsList);
if (nearestIncident != null) {
trafficMessage += " Nearest incident: " + nearestIncident.description.text;
}
_showDialog("Nearby traffic incidents", trafficMessage);
for (TrafficIncident 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.enUs;Note that MapSceneLayers.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:
TrafficIncident? _getNearestTrafficIncident(
GeoCoordinates currentGeoCoordinates, List<TrafficIncident> trafficIncidentsList) {
if (trafficIncidentsList.length == 0) {
return null;
}
// By default, traffic incident results are not sorted by distance.
double nearestDistance = double.maxFinite;
TrafficIncident? nearestTrafficIncident;
for (final trafficIncident in trafficIncidentsList) {
final trafficIncidentPolylines = <GeoPolyline>[];
// It is guaranteed that each incident has a valid polyline.
trafficIncidentPolylines.add(trafficIncident.location.polyline);
// If the polyline contains any gaps, they are available as additionalPolylines in TrafficLocation.
trafficIncidentPolylines.addAll(trafficIncident.location.additionalPolylines);
for (final polyline in trafficIncidentPolylines) {
for (final geoCoords in polyline.vertices) {
final currentDistance = currentGeoCoordinates.distanceTo(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.
void _requestRealtimeTrafficOnRoute(here.Route route) {
// We are interested to see traffic also for side paths.
int halfWidthInMeters = 500;
GeoCorridor geoCorridor =
GeoCorridor(route.geometry.vertices, halfWidthInMeters);
TrafficFlowQueryOptions trafficFlowQueryOptions = TrafficFlowQueryOptions();
_trafficEngine.queryForFlowInCorridor(geoCorridor, trafficFlowQueryOptions,
(trafficQueryError, trafficFlowList) {
if (trafficQueryError == null && trafficFlowList != null) {
for (TrafficFlow trafficFlow in trafficFlowList) {
double? confidence = trafficFlow.confidence;
if (confidence != null && confidence <= 0.5) {
// 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.
GeoPolyline trafficGeoPolyline = trafficFlow.location.polyline;
_addTrafficPolylines(trafficFlow.jamFactor, trafficGeoPolyline);
}
} else {
_showDialog(
"Error while fetching traffic flow:", trafficQueryError.toString());
}
});
}As a next step, you can render the traffic flow polylines using your own color encoding:
void _addTrafficPolylines(double jamFactor, GeoPolyline geoPolyline) {
Color? lineColor = _getTrafficColor(jamFactor);
// We skip rendering low traffic.
if (lineColor == null) return;
double widthInPixels = 10;
MapPolyline trafficSpanMapPolyline;
try {
trafficSpanMapPolyline = MapPolyline.withRepresentation(
geoPolyline,
MapPolylineSolidRepresentation(
MapMeasureDependentRenderSize.withSingleSize(
RenderSizeUnit.pixels, widthInPixels),
lineColor,
LineCap.round));
_hereMapController.mapScene.addMapPolyline(trafficSpanMapPolyline);
_mapPolylines.add(trafficSpanMapPolyline);
} on MapPolylineRepresentationInstantiationException catch (e) {
print("MapPolylineRepresentationInstantiationException:" + e.error.name);
return;
} on MapMeasureDependentRenderSizeInstantiationException catch (e) {
print("MapMeasureDependentRenderSizeInstantiationException:" + e.error.name);
return;
}
_hereMapController.mapScene.addMapPolyline(trafficSpanMapPolyline);
_mapPolylines.add(trafficSpanMapPolyline);
}
// 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.
Color? _getTrafficColor(double? jamFactor) {
if (jamFactor == null || jamFactor < 4) {
return null;
} else if (jamFactor >= 4 && jamFactor < 8) {
return Color.fromARGB(160, 255, 255, 0); // Yellow
} else if (jamFactor >= 8 && jamFactor < 10) {
return Color.fromARGB(160, 255, 0, 0); // Red
}
return Color.fromARGB(160, 0, 0, 0); // Black
}Try the Traffic example app
A usage example is available on GitHub as part of the "traffic_app" example app.
Updated yesterday