# 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 `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 [Visualize traffic on routes](traffic-render.md#render-a-polyline-adjacent-to-traffic-flow) section. Data queried with the `TrafficEngine` is based on the [HERE Traffic API v7](https://www.here.com/docs/bundle/traffic-api-developer-guide-v7/page/README.html). > #### Note > > For information about the pricing of this feature, see the [HERE Base Plan Pricing](https://www.here.com/get-started/pricing). If you are using the Navigate license or have other questions about pricing, [contact us](https://www.here.com/contact). ## 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`: ```java Java try { trafficEngine = new TrafficEngine(); } catch (InstantiationErrorException e) { throw new RuntimeException("Initialization of TrafficEngine failed: " + e.error.name()); } ``` ```kotlin Kotlin try { trafficEngine = TrafficEngine() } catch (e: InstantiationErrorException) { throw RuntimeException("Initialization of TrafficEngine failed: " + e.error.name) } ``` ## Initiate traffic requests Below we search inside a `GeoCircle` for possible `TrafficIncidents`: ```java Java private void queryForIncidents(GeoCoordinates centerCoords) { int radiusInMeters = 1000; 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 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()); } } }); } ``` ```kotlin Kotlin private fun queryForIncidents(centerCoords: GeoCoordinates) { val radiusInMeters = 1000 val geoCircle = GeoCircle(centerCoords, radiusInMeters.toDouble()) val trafficIncidentsQueryOptions = 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, TrafficIncidentsQueryCallback { trafficQueryError, trafficIncidentsList -> if (trafficQueryError == null) { // If error is null, it is guaranteed that the list will not be null. var trafficMessage = "Found " + trafficIncidentsList!!.size + " result(s). See log for details." val nearestIncident: TrafficIncident = getNearestTrafficIncident(centerCoords, trafficIncidentsList) if (nearestIncident != null) { trafficMessage += " Nearest incident: " + nearestIncident.description.text } showDialog("Nearby traffic incidents", trafficMessage!!) for (trafficIncident in trafficIncidentsList) { Log.d(TAG, "" + trafficIncident.description.text) } } else { showDialog("TrafficQueryError:", trafficQueryError.toString()) } }) } ``` ## 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: ```java Java trafficIncidentsQueryOptions.languageCode = LanguageCode.EN_US; ``` ```kotlin Kotlin 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: ```java Java @Nullable private TrafficIncident getNearestTrafficIncident(GeoCoordinates currentGeoCoords, List 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) { List trafficIncidentPolyLines = new ArrayList<>(); // It is guaranteed that each incident has a valid polyline. trafficIncidentPolyLines.add(trafficIncident.getLocation().polyline); // If the polyline contains any gaps, they are available as additionalPolylines in TrafficLocation. trafficIncidentPolyLines.addAll(trafficIncident.getLocation().additionalPolylines); for (GeoPolyline trafficIncidentPolyline : trafficIncidentPolyLines) { for (GeoCoordinates geoCoords : trafficIncidentPolyline.vertices) { double currentDistance = currentGeoCoords.distanceTo(geoCoords); if (currentDistance < nearestDistance) { nearestDistance = currentDistance; nearestTrafficIncident = trafficIncident; } } } } return nearestTrafficIncident; } ``` ```kotlin Kotlin private fun getNearestTrafficIncident( currentGeoCoords: GeoCoordinates, trafficIncidentsList: List? ): TrafficIncident? { if (trafficIncidentsList!!.size == 0) { return null } // By default, traffic incidents results are not sorted by distance. var nearestDistance = Double.MAX_VALUE var nearestTrafficIncident: TrafficIncident? = null for (trafficIncident in trafficIncidentsList) { val trafficIncidentPolyLines: MutableList = java.util.ArrayList() // 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 (trafficIncidentPolyline in trafficIncidentPolyLines) { for (geoCoords in trafficIncidentPolyline.vertices) { val 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. ## 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 `TRAFFIC_FLOW` layer as the time of the request may happen at a different time and may be visualized differently on app-side. ```java Java private void requestRealtimeTrafficOnRoute(Route route) { // We are interested to see traffic also for side paths. int halfWidthInMeters = 500; GeoCorridor geoCorridor = new GeoCorridor(route.getGeometry().vertices, halfWidthInMeters); TrafficFlowQueryOptions trafficFlowQueryOptions = new TrafficFlowQueryOptions(); trafficEngine.queryForFlow(geoCorridor, trafficFlowQueryOptions, new TrafficFlowQueryCallback() { @Override public void onTrafficFlowFetched(@Nullable TrafficQueryError trafficQueryError, @Nullable List list) { if (trafficQueryError == null) { for (TrafficFlow trafficFlow : list) { Double confidence = trafficFlow.getConfidence(); 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.getLocation().polyline; addTrafficPolylines(trafficFlow.getJamFactor(), trafficGeoPolyline); } } else { showDialog("Error while fetching traffic flow:", trafficQueryError.toString()); } } }); } ``` ```kotlin Kotlin private fun requestRealtimeTrafficOnRoute(route: Route) { // We are interested to see traffic also for side paths. val halfWidthInMeters = 500 val geoCorridor = GeoCorridor(route.geometry.vertices, halfWidthInMeters) val trafficFlowQueryOptions = TrafficFlowQueryOptions() trafficEngine.queryForFlow(geoCorridor, trafficFlowQueryOptions, TrafficFlowQueryCallback { trafficQueryError, list -> if (trafficQueryError == null) { for (trafficFlow in list!!) { val 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. val 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: ```java Java private void addTrafficPolylines(double jamFactor, GeoPolyline geoPolyline) { Color lineColor = getTrafficColor(jamFactor); if (lineColor == null) { // We skip rendering low traffic. return; } float widthInPixels = 10; MapPolyline trafficSpanMapPolyline = null; try { trafficSpanMapPolyline = new MapPolyline(geoPolyline, new MapPolyline.SolidRepresentation( new MapMeasureDependentRenderSize(RenderSize.Unit.PIXELS, widthInPixels), lineColor, LineCap.ROUND)); } catch (MapPolyline.Representation.InstantiationException e) { Log.e("MapPolyline Representation Exception:", e.error.name()); } catch (MapMeasureDependentRenderSize.InstantiationException e) { Log.e("MapMeasureDependentRenderSize Exception:", e.error.name()); } mapView.getMapScene().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. @Nullable private Color getTrafficColor(Double jamFactor) { if (jamFactor == null || jamFactor < 4) { return null; } else if (jamFactor >= 4 && jamFactor < 8) { return Color.valueOf(1, 1, 0, 0.63f); // Yellow } else if (jamFactor >= 8 && jamFactor < 10) { return Color.valueOf(1, 0, 0, 0.63f); // Red } return Color.valueOf(0, 0, 0, 0.63f); // Black } ``` ```kotlin Kotlin private fun addTrafficPolylines(jamFactor: Double, geoPolyline: GeoPolyline) { val lineColor = getTrafficColor(jamFactor) ?: // We skip rendering low traffic. return val widthInPixels = 10f var trafficSpanMapPolyline: MapPolyline? = null try { trafficSpanMapPolyline = MapPolyline( geoPolyline, SolidRepresentation( MapMeasureDependentRenderSize(RenderSize.Unit.PIXELS, widthInPixels.toDouble()), lineColor, LineCap.ROUND ) ) } catch (e: MapPolyline.Representation.InstantiationException) { Log.e("MapPolyline Representation Exception:", e.error.name) } catch (e: MapMeasureDependentRenderSize.InstantiationException) { Log.e("MapMeasureDependentRenderSize Exception:", e.error.name) } mapView.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. @Nullable private fun getTrafficColor(jamFactor: Double?): Color? { if (jamFactor == null || jamFactor < 4) { return null } else if (jamFactor >= 4 && jamFactor < 8) { return Color.valueOf(1f, 1f, 0f, 0.63f) // Yellow } else if (jamFactor >= 8 && jamFactor < 10) { return Color.valueOf(1f, 0f, 0f, 0.63f) // Red } return Color.valueOf(0f, 0f, 0f, 0.63f) // Black } ``` ## Try the Traffic example app A usage example is available on [GitHub](https://github.com/heremaps/here-sdk-examples) as part of the "Traffic" example app, provided in both [Java](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Java/Traffic) and [Kotlin](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Kotlin/TrafficKotlin).