# Traffic

With a dedicated 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 TRAFFIC_INCIDENTS with one line of code. The HERE SDK supports also a separate layer to see the current traffic situation by adding a TRAFFIC_FLOW layer.

In addition, you can also indicate the traffic along a Route instance as shown in the Directions section.

## Show Real-Time Traffic Flow and Incidents on the Map

You can easily visualize traffic incidents on the map by enabling the map layer state TRAFFIC_INCIDENTS. The HERE SDK also supports a separate layer to see the current traffic situation. See the example below for how to show or hide a layer on the map.

After a layer is set, the visible area of the map is automatically updated. So you can freely pan the map in all directions to see the latest traffic incidents.

In many situations, drivers are interested in finding the fastest route based on the current traffic jams in a city - or outside a city. The HERE SDK allows you to show a layer holding all the current traffic jams, visualized by lines in different colors to indicate the severity of the jam - always updated in real-time. This feature requires an online connection and consumes slightly more data. However, the traffic lines are shown as part of the map tiles and are therefore highly performant.

Together - or independently - you can visualize such traffic information on the map with just a few lines of code:

private void enableTrafficVisualization() {
Map<String, String> mapFeatures = new HashMap<>();
// Once these traffic layers are added to the map, they will be automatically updated while panning the map.
mapFeatures.put(MapFeatures.TRAFFIC_FLOW, MapFeatureModes.TRAFFIC_FLOW_WITH_FREE_FLOW);
// MapFeatures.TRAFFIC_INCIDENTS renders traffic icons and lines to indicate the location of incidents.
mapFeatures.put(MapFeatures.TRAFFIC_INCIDENTS, MapFeatureModes.DEFAULT);
mapView.getMapScene().enableFeatures(mapFeatures);
}


Setting a new layer state is performed synchronously, but it requires a valid map scene that must have been loaded before. Also, setting a new feature state while a new map scene is being loaded, may result in an exception. For hiding feature layers, you can call:

private void disableTrafficVisualization() {
List<String> mapFeatures = new ArrayList<>();
mapView.getMapScene().disableFeatures(mapFeatures);
}


The traffic flow lines are color coded as follows:

• Green: Normal traffic
• Amber/Yellow: High traffic
• Red: Very high traffic
• Black: Blocking traffic

### Pick Traffic Incidents from the Map

When the TRAFFIC_INCIDENTS is shown on the MapView, you can set up a tap handler and pick the traffic incidents to get more information.

private void setTapGestureHandler() {
mapView.getGestures().setTapListener(touchPoint -> {
GeoCoordinates touchGeoCoords = mapView.viewToGeoCoordinates(touchPoint);
// Can be null when the map was tilted and the sky was tapped.
if (touchGeoCoords != null) {
// Pick incidents that are shown in TRAFFIC_INCIDENTS.
pickTrafficIncident(touchPoint);
}
});
}

// Traffic incidents can only be picked, when TRAFFIC_INCIDENTS is visible.
private void pickTrafficIncident(Point2D touchPointInPixels) {
Point2D originInPixels = new Point2D(touchPointInPixels.x, touchPointInPixels.y);
Size2D sizeInPixels = new Size2D(1, 1);
Rectangle2D rectangle = new Rectangle2D(originInPixels, sizeInPixels);

mapView.pickMapContent(rectangle, new MapViewBase.PickMapContentCallback() {
@Override
public void onPickMapContent(@Nullable PickMapContentResult pickMapContentResult) {
if (pickMapContentResult == null) {
// An error occurred while performing the pick operation.
return;
}

List<PickMapContentResult.TrafficIncidentResult> trafficIncidents =
pickMapContentResult.getTrafficIncidents();
if (trafficIncidents.size() == 0) {
Log.d(TAG, "No traffic incident found at picked location");
} else {
Log.d(TAG, "Picked at least one incident.");
PickMapContentResult.TrafficIncidentResult firstIncident = trafficIncidents.get(0);
showDialog("Traffic incident picked:", "Type: " +
firstIncident.getType().name());

// Find more details by looking up the ID via TrafficEngine.
findIncidentByID(firstIncident.getOriginalId());
}

// Optionally, look for more map content like embedded POIs.
}
});
}


With the tap handler, we get the touched location in view coordinates that can be passed to mapView.pickMapContent(). Here, we just use a point-sized rectangle, but you can also enlarge the pick area to include more content at once.

The callback provides a PickMapContentResult which can contain a TrafficIncidentResult, but also other embedded types like default POI markers, that are always visible on the map. The TrafficIncidentResult type already provides most information about the incident, but to get all available information, we can use the TrafficEngine (see also below) to search for the picked incident by ID:

private void findIncidentByID(String originalId) {
TrafficIncidentLookupOptions trafficIncidentsQueryOptions = new TrafficIncidentLookupOptions();
// Optionally, specify a language:
// the language of the country where the incident occurs is used.
// trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;
trafficEngine.lookupIncident(originalId, trafficIncidentsQueryOptions, new TrafficIncidentLookupCallback() {
@Override
public void onTrafficIncidentFetched(@Nullable TrafficQueryError trafficQueryError, @Nullable TrafficIncident trafficIncident) {
if (trafficQueryError == null) {
Log.d(TAG, "Fetched TrafficIncident from lookup request." +
" Description: " + trafficIncident.getDescription().text);
} else {
showDialog("TrafficLookupError:", trafficQueryError.toString());
}
}
});
}


A usage example is available on GitHub as part of the Traffic example app.

## Query Incidents with 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 = new TrafficEngine();
} catch (InstantiationErrorException e) {
throw new RuntimeException("Initialization of TrafficEngine failed: " + e.error.name());
}


Below we search inside a GeoCircle for possible TrafficIncidents:

private void queryForIncidents(GeoCoordinates centerCoords) {
GeoCircle geoCircle = new GeoCircle(centerCoords, radiusInMeters);
TrafficIncidentsQueryOptions trafficIncidentsQueryOptions = new TrafficIncidentsQueryOptions();
// Optionally, specify a language:
// the language of the country where the incident occurs is used.
// trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;
trafficEngine.queryForIncidents(geoCircle, trafficIncidentsQueryOptions, new TrafficIncidentsQueryCallback() {
@Override
public void onTrafficIncidentsFetched(@Nullable TrafficQueryError trafficQueryError,
@Nullable List<TrafficIncident> trafficIncidentsList) {
if (trafficQueryError == null) {
// If error is null, it is guaranteed that the list will not be null.
String trafficMessage = "Found " + trafficIncidentsList.size() + " result(s). See log for details.";
TrafficIncident nearestIncident =
getNearestTrafficIncident(centerCoords, trafficIncidentsList);
if (nearestIncident != null) {
trafficMessage += " Nearest incident: " + nearestIncident.getDescription().text;
}
showDialog("Nearby traffic incidents", trafficMessage);

for (TrafficIncident trafficIncident : trafficIncidentsList) {
Log.d(TAG, "" + trafficIncident.getDescription().text);
}
} else {
showDialog("TrafficQueryError:", trafficQueryError.toString());
}
}
});
}


For each found TrafficIncident, we log its description. If we search in Germany, then by default the results will be in German. This may sound like this:

Berlin, Sachsendamm zwischen Gotenstraße und Priesterweg Fahrbahn auf einen Fahrstreifen verengt, Staugefahr, Bauarbeiten, bis voraussichtlich 20.02.2028

You can also specify another language, for example, by setting this:

trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US;


Note that TRAFFIC_INCIDENTS 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:

@Nullable
private TrafficIncident getNearestTrafficIncident(GeoCoordinates currentGeoCoords,
List<TrafficIncident> trafficIncidentsList) {
if (trafficIncidentsList.size() == 0) {
return null;
}

// By default, traffic incidents results are not sorted by distance.
double nearestDistance = Double.MAX_VALUE;
TrafficIncident nearestTrafficIncident = null;
for (TrafficIncident trafficIncident : trafficIncidentsList) {
// In case lengthInMeters == 0 then the polyline consistes of two equal coordinates.
// It is guaranteed that each incident has a valid polyline.
for (GeoCoordinates geoCoords : trafficIncident.getLocation().polyline.vertices) {
double currentDistance = currentGeoCoords.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.

With the TrafficDataProvider interface the HERE SDK allows to integrate radio station signals providing traffic broadcasts.

A TrafficBroadcast is expecting the RDS-TMC format and it can be used when there is no internet connection, so that the OfflineRoutingEngine can utilize traffic data coming over the radio channel.

The trafficBroadcast.activate() method needs to be called to receive traffic data events.

A TrafficBroadcast continuously reacts to new locations provided from a location source and acts as a LocationListener. The location must be updated regardless of calling activate().

### Note

In order to adopt the interface special hardware is required. Talk to your HERE representative for more details. This is released as a beta feature.