# Advanced routing features Features such as isoline routing and real-time traffic information, which can be refreshed for already calculated routes, play a pivotal role in route planning. This section also delves into features such as returning to a route that has been left during navigation and importing routes from other services. **Isoline routing** features use the [HERE Isoline Routing API v8](https://www.here.com/docs/bundle/isoline-routing-api-developer-guide-v8/page/README.html). **Route import** features use the [HERE Routing API v8](https://www.here.com/docs/bundle/routing-api-developer-guide-v8/page/README.html). Example of a transaction based on HERE Isoline Routing API v8: Calculate a route with the `RoutingEngine` with `IsolineOptions`. Example of importing routes online based on HERE Routing API v8: `RoutingEngine.importRoutes(...)`. > #### Note > > For information about the pricing of these features, 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). ## Isoline routing With isoline routing you can generate polygons to represent the area of reach from a given point based on time, distance or energy consumption: the polygon will encompass all destinations that can be reached in a specific amount of time, a maximum travel distance, or even the charge level available at an electric vehicle. Please refer to the [Isoline Routing API Reference](/here-sdk/page/sdk-for-android-navigate-api-reference-latestisolineoptions) for details. The HERE SDK provides `IsolineRoutingEngine` which can be used to calculate a reachable area from a center point. ```java try { isolineRoutingEngine = new IsolineRoutingEngine(); } catch (InstantiationErrorException e) { throw new RuntimeException("Initialization of IsolineRoutingEngine failed: " + e.error.name()); } ``` > #### Note > > Isoline routing considers real-time and historical traffic in its calculations. Some examples of how this could be useful: * Users can find restaurants within a 2 km walking distance from their current location. * Users can search for hotels based on the distance to sights, for example, to find hotels within a 20 minute drive to major attractions - such as Disney World and Universal Studios in Orlando, Florida USA. Below we show a consumption based `Isoline` example for an electric vehicle scenario, where the driver wants to know which points are reachable within the provided limit of 400 Wh: so, the goal is to consume 400 Wh or less - and the question is: what can the driver reach within this energy limit? > #### Note > > Electric vehicles have a limited reachable range based on their current battery charge and factors affecting the rate of energy consumed, such as road slope or auxiliary power usage. Therefore, it is useful to visualize the appropriate range to avoid running out of energy before reaching a charging point. Since vehicles have unique consumption parameters, they are to be specified in the request for an accurate range to be calculated. For more details, see the electric vehicle section below. The result will be a `GeoPolygon` shape that you can show on the map to provide the driver a visual orientation: ```java List rangeValues = Collections.singletonList(400); IsolineOptions.Calculation calculationOptions = new IsolineOptions.Calculation(IsolineRangeType.CONSUMPTION_IN_WATT_HOURS, rangeValues, IsolineCalculationMode.BALANCED); IsolineOptions isolineOptions = new IsolineOptions(calculationOptions, getEVRoutingOptions()); isolineRoutingEngine.calculateIsoline(new Waypoint(startGeoCoordinates), isolineOptions, new CalculateIsolineCallback() { @Override public void onIsolineCalculated(RoutingError routingError, List list) { if (routingError != null) { showDialog("Error while calculating reachable area:", routingError.toString()); return; } // When routingError is nil, the isolines list is guaranteed to contain at least one isoline. // The number of isolines matches the number of requested range values. Here we have used one range value, // so only one isoline object is expected. Isoline isoline = list.get(0); // If there is more than one polygon, the other polygons indicate separate areas, for example, islands, that // can only be reached by a ferry. for (GeoPolygon geoPolygon : isoline.getPolygons()) { // Show polygon on map. Color fillColor = Color.valueOf(0, 0.56f, 0.54f, 0.5f); // RGBA MapPolygon mapPolygon = new MapPolygon(geoPolygon, fillColor); mapView.getMapScene().addMapPolygon(mapPolygon); mapPolygons.add(mapPolygon); } } }); ``` This finds the area that an electric vehicle can reach by consuming 400 Wh or less, while trying to take the fastest possible route into any possible straight direction from start. Why fastest? This depends on the route's optimization mode (we have shown in earlier sections above) - of course, you can specify any mode. Note that each isoline needs exactly one center point from where a journey may start. Since we are interested in the energy consumption, we also provided `RoutingOptions` with `ElectricVehicleOptions`. These options must include battery specifications as shown in the electric vehicle routing section below. If you provide a time or distance limit instead as range type, you can provide the usual route options as shown earlier. The `IsolineRangeType` defines the type of the provide range value. Here we use 400. You can provide multiple values - and as a result you would then get multiple `Isoline` objects - one for each provided range value. On the other hand, each `Isoline` object can contain multiple polygons for special cases, such as when the area of reach includes an island. Such areas are provided as separate polygons. The shape of the resulting polygon can be defined by the `maxPoints` parameter. It determines the number of vertices of the `GeoPolygon`. Note that this parameter does not influence the actual calculation of the polygon, but just its appearance. The screenshot shows the center location of the polygon, indicated by a big green circle. It also shows a possible route beyond this area with two additional found charging stations along the route. In the next section you can find how to calculate routes for electric vehicles. > #### Note > > You can find the above code snippet as part of the "EVRouting" example app on GitHub, available for both [Java](https://github.com/heremaps/here-sdk-examples/blob/master/examples/latest/navigate/android/Java/EVRouting) and [Kotlin](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Kotlin/EVRoutingKotlin). ## Refresh routes The traffic flow information contained in a `Route` object is valid for the time when the route was calculated, see above. If you want to update this information at any later point in time, you can refresh the route. It is also possible to refresh the route options for a route: ```java RefreshRouteOptions refreshRouteOptions = new RefreshRouteOptions(taxiOptions); // Update the route options and set a new start point on the route. // The new starting point must be on or very close to the original route, preferrably, use a map-matched waypoint if possible. // Note: A routeHandle is only available, when RouteOptions.enableRouteHandle() was set to true when the original route was calculated. routingEngine.refreshRoute(route.getRouteHandle(), mapMatchedWaypoint, refreshRouteOptions, new CalculateRouteCallback() { @Override public void onRouteCalculated(@Nullable RoutingError routingError, @Nullable List list) { if (routingError == null) { Route newRoute = list.get(0); // ... } else { // Handle error. } } }); ``` For this you need to know the `RouteHandle`, which must be requested via `RouteOptions.enableRouteHandle()` before the route was calculated. In addition, refreshing a route can be useful to convert the route options from one transport type to another or to update specific options. If the conversion is not possible - for example, when a pedestrian route is converted to a truck route, then a `routingError` indicates this. You can also shorten the route, by specifying a new starting point. The new starting point must be very close to the original route as no new route is calculated. If the new starting point is too far away, then a `routingError` occurs. Note that refreshing a route is not enough when a driver deviates from a route during an ongoing turn-by-turn navigation - as the detour part needs a new route calculation. In such a case, it may be more useful to recalculate the whole route by setting a new starting point - or to use the `returnToRoute()` method that allows to keep the originally chosen route alternative. ### Get the latest traffic delays ahead during turn-by-turn navigation (only available for Navigate) For the HERE SDK for Android Navigate you can refresh a route in order to get the latest traffic delay updates ahead during turn-by-turn navigation. Before the refreshed route can be set to the `Navigator`, the last location on the route needs to be set as new starting point. However, this can lead to unexpected results when the driver has already advanced while the refresh operation is ongoing. Either way, if you just want to update the ETA - and the new starting point lies on the existing route, you do not necessarily need to set the new route: instead, you can just use the updated ETA including traffic delay time - as the rest of the route ahead remains unchanged. Setting a new route during guidance effectively starts a new navigation session. As an alternative, consider to use the `DynamicRoutingEngine` that requires to set the last location and the current section index: this information will be used when the next `dynamicRoutingEngineOptions.pollInterval` is reached. As a trade-off, this does not lead to an immediate update and the route shape may change if a better route is found to bypass current traffic obstacles. Again, as with `refreshRoute()` you need to decide if you want to set the new route or not. > #### Note (only for the HERE SDK for Android Navigate) > > Refreshing routes is not supported for the `OfflineRoutingEngine` - as querying the current traffic situation requires an online connection. > Similarly, the `DynamicRoutingEngine` requires an online connection. ## Return to a route The `RoutingEngine` allows to refresh an existing route (see above) based on the current traffic situation. The result may be a new route that is faster than the previous one. However, the new `startingPoint` must be very close to the original route. If you need to handle a case where the `startingPoint` is farther away from the route, consider to use the `returnToRoute()` feature of the `RoutingEngine`. For example, a driver may decide to take a detour due to the local traffic situation. Calling `returnToRoute()` will also result in a new route, but it will try to resemble as much as possible from the original route without a costly route recalculation - if possible. * Stopover waypoints are guaranteed to be passed by. * Pass-through waypoints are only meant to shape the route, so there is no guarantee that the new route will honor or discard them. * The current traffic situation is taken into account and may reshape the route. Note that turn-by-turn navigation is only available for Navigate. > #### Note (only for the Navigate) > > The `returnToRoute()` method is also available for the `OfflineRoutingEngine` that works offline on cached or downloaded map data. However, it does not take the current traffic situation into account. > > Take a look at the navigation chapter for a [coding example](navigation-deviation.md#return-to-a-route-after-deviation) using `returnToRoute()`. ## Persist routes You can persist and reuse routes across sessions. Use the `serialize(...)` and `deserialize(...)` methods provided by the `Route` class to allow storing and restoring of `route` objects. Alternatively, you can use the import feature via `routingEngine.importRoute(...)`. ## Import routes You can import routes from other APIs and/or vendors via one of the various overloaded `routingEngine.importRoute()` methods. Note that this is not a 1:1 import feature, but rather a reconstruction. A new `Route` object can be created from: * **Option 1:** A list of `GeoCoordinates` and `RouteOptions`. This can be useful, when the route should be imported from a different vendor or when the route should be persisted for a longer time. The route shape will be kept as close as possible to the one provided. Requires an online connection. * **Option 2:** A `RouteHandle`. This can be useful to import a route from other HERE services. A possible use case can be to create a route on the [HERE WeGo](https://wego.here.com) website or another web page that uses the HERE REST APIs and then transfer it to a mobile device to start a trip. Note that a map update on backend side or other changes in the real world can lead to an invalid handle. Therefore, it is recommended to use the handle only for a few hours. Although the `RouteHandle` encodes certain information, it requires an online connection to get the full route data from backend. Any `AvoidanceOptions` that are applied may be discarded and reported as violations via the route's `Section.getSectionNotices()`. For example, if you request to avoid highways, but the provided coordinates match to a highway road, then the resulting route will still match the highway, but a notice is added to indicate that the desired avoidance option is violated. In general, be aware that importing a route may not always reproduce exactly the same route as the original one - even if you use the exact same `RouteOptions`. There can be always changes in the map, for example, due to a new construction site or related traffic events. Also, the map data itself can change, for example, when speed limits change along a road or new roads are built or existing ones closed. Also, the time of the departure can influence if certain roads can be taken or not. Below we look more into the details. **Option 1: import Routes from a List of Geographic Coordinates** To import a route from a list of `GeoCoordinates` with `RouteOptions`, a list of `GeoCoordinates` is needed that define the route shape. Such coordinates need to be very close to each other, or the calculation will fail. Such a list of coordinates can be extracted from a route calculated by a different vendor or it can be extracted from a GPX trace, for example. ```java List locations = Arrays.asList (new Location(new GeoCoordinates(52.518032,13.420632)), new Location(new GeoCoordinates(52.51772,13.42038)), new Location(new GeoCoordinates(52.51764,13.42062)), new Location(new GeoCoordinates(52.51754,13.42093)), new Location(new GeoCoordinates(52.51735,13.42155)), new Location(new GeoCoordinates(52.51719,13.42209)), new Location(new GeoCoordinates(52.51707,13.42248)), new Location(new GeoCoordinates(52.51695,13.42285)), new Location(new GeoCoordinates(52.5168, 13.42331)), new Location(new GeoCoordinates(52.51661,13.42387)), new Location(new GeoCoordinates(52.51648,13.42429)), new Location(new GeoCoordinates(52.51618,13.42513)), new Location(new GeoCoordinates(52.5161,13.42537)), new Location(new GeoCoordinates(52.51543,13.42475)), new Location(new GeoCoordinates(52.51514,13.42449)), new Location(new GeoCoordinates(52.515001,13.424374))); routingEngine.importRoute(locations, new RoutingOptions(), new CalculateRouteCallback() { @Override public void onRouteCalculated(@Nullable RoutingError routingError, @Nullable List list) { if (routingError == null) { Route newRoute = list.get(0); // ... } else { // Handle error. } } }); ``` For this option, you can select the overload of the `importRoute(..)` method that matches your desired transport type. When importing a route from a list of `Location` objects and `RouteOptions` then the `RoutingEngine` will create the route shape as closely as possible from the provided geographic coordinates. For best results use 1Hz GPS data, or points that have a spacing of a few meters. Very sparse data may result in an error. The list of locations is not unlimited: please refer to the API Reference for the maximum number of supported items. Similarly, the list of waypoints is not unlimited when calling `calculateRoute(..)`. > #### Note > > Additionally, you can use an overload of the `importRoute(..)` method to specify a list of `RouteStop` elements. When importing a route just from a list of coordinates, the information on stopover waypoints is usually lost. Use the `RouteStop` class to specify such stops. It allows to specify a `locationIndex` where the stop should be done and a `stopDuration`. This will influence the overall ETA and during navigation the `RouteStop` will be treated as a stopover waypoint so that it will be reported as a `Milestone` when passing-by (note that navigation is only supported for `Navigate`). For the HERE SDK for Android Navigate, note that when using the `OfflineRoutingEngine` there is no limitation when setting waypoints via `calculateRoute(..)`. > #### Note > > This feature requires to map-match all provides points to a street. Therefore, it may be considerably slower than importing a (longer) route from a `RouteHandle`. **Option 2: import Routes from a RouteHandle** Below we take a look into the `RouteHandle` option. With the `RouteHandle​(String handle)` constructor you can create a `RouteHandle` from a given string handle. Such a string can be provided from other backend sources such as one of the available HERE REST APIs. Note that the string is only valid for a couple of hours. Below you can find an example REST API call to create a route with a route handle string (replace `YOUR_API_KEY` with the actual key you have in use): [https://router.hereapi.com/v8/routes?apikey=-YOUR\_API\_KEY\&origin=52.524465%2C13.382334\&destination=52.525301%2C13.399844\&return=polyline%2Csummary%2Cactions%2Cinstructions%2CrouteHandle\&transportMode=car](https://router.hereapi.com/v8/routes?apikey=-YOUR_API_KEY\&origin=52.524465%2C13.382334\&destination=52.525301%2C13.399844\&return=polyline%2Csummary%2Cactions%2Cinstructions%2CrouteHandle\&transportMode=car) You can copy the route handle string from the JSON response and use it with the HERE SDK like so: ```java routingEngine.importRoute(new RouteHandle("routeHandleStringFromBackend"), new RefreshRouteOptions(TransportMode.CAR), new CalculateRouteCallback() { @Override public void onRouteCalculated(@Nullable RoutingError routingError, @Nullable List list) { if (routingError == null) { Route newRoute = list.get(0); // ... } else { // Handle error. } } }); ``` The `RouteHandle` string identifies an already calculated route. Once imported successfully, a new `Route` object is created and provided as part of the `CalculateRouteCallback` for further use with the HERE SDK. > #### Note (HERE SDK for Android Navigate) > > The `OfflineRoutingEngine` supports importing routes offline, too, via a `RouteHandle`. However, passing in a list of `GeoCoordinates` is not supported. This is only possible with the `RoutingEngine` that requires an online connection. > > However, there is a workaround possible: by inserting an unlimited number of `Waypoint` items into `OfflineRoutingEngine.calculateRoute(..)` a route can be calculated offline with the specified stopover waypoints to shape the route as close as possible as the original route. Unlike the online `RoutingEngine` which supports only up to 200 waypoints, the `OfflineRoutingEngine` doesn’t have such a limitation, but there may be performance trade-offs for longer routes. Therefore, it is recommended to pass only a subset of all route coordinates as waypoints - for example, filtered by a distance threshold. > > Additional waypoints can be set in the same way as shown earlier for the online `RoutingEngine`.