# Get started with Routing
The HERE SDK provides the `RoutingEngine` to calculate the best route directions from A to B, including multiple waypoints and localizable maneuver instructions for each turn.
Specify your preferences by setting the desired route type (fastest or shortest) and various route options (such as speed profiles, route restrictions, vignette options, and more) to find the perfect route that saves the most energy: with our advanced routing technology and our dedicated EV route planning support HERE helps you to [make the planet cleaner and safer](https://www.here.com/about/sustainability) .
Feature overview:
* **Calculate routes**: Calculate routes with multiple waypoints for various transport modes.
* **Isoline routing**: Calculate isoline polygons to represent the area of reach from a given point based on time, distance or fuel consumption.
* **Search along a route**: Search for places along an entire route (this feature is described in the [Search](https://docs.here.com/here-sdk/docs/search) section).
* **Import routes / route matching**: You can import routes from other APIs.
* **Offline routing** (only for **HERE SDK for Android Navigate**): When no internet connection is available, you can switch to a dedicated offline routing engine to calculate routes on already cached map data or preloaded offline maps data.
Routing is based on [HERE Routing API v8](/routing/docs/routing-intro). An example of a transaction based on Routing is: Calculate a route with the `RoutingEngine` using `RoutingOptions`. You can set the desired transport mode via `RoutingOptions.transportSpecification.transportMode` to `CAR`, `TRUCK`, `PEDESTRIAN`, `BICYCLE`, `BUS`, `TAXI` or `SCOOTER` and do not use `TrafficOptimizationMode.TIME_DEPENDENT`. Note that `TIME_DEPENDENT` is set, by default, as time-aware routing provides better results including the current traffic flow.
An example of Time-Aware Routing: Calculate a route with the `RoutingEngine` with `TrafficOptimizationMode.TIME_DEPENDENT`. Note that this is enabled, by default. If `RouteOptions.arrivalTime` and `RouteOptions.arrivalTime` are kept as `null`, the HERE SDK will use the current device time.
An example of Routing with toll-costs: Calculate a route with the `RoutingEngine` with `RouteOptions.enableTolls` set to `true`.
Routing also uses the [HERE Public Transit API](/transit/docs/readme-public-transit-api-v8). An example using the HERE Public Transit API: Calculate a route with the `TransitRoutingEngine`.
> #### 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).
## Initialize the RoutingEngine
Start your trip by creating the route engine in this manner:
```java Java
try {
routingEngine = new RoutingEngine();
} catch (InstantiationErrorException e) {
throw new RuntimeException("Initialization of RoutingEngine failed: " + e.error.name());
}
```
```kotlin Kotlin
try {
routingEngine = RoutingEngine()
} catch (e: InstantiationErrorException) {
throw RuntimeException("Initialization of RoutingEngine failed: " + e.error.name)
}
```
Creating a new `RoutingEngine` instance can throw an error that we have to handle as shown above. For example, such an error can happen when the HERE SDK initialization failed beforehand.
When you no longer need `RoutingEngine` instance, it is recommended to call its `dispose()` method to cancel any pending requests and shut it down for proper resource cleanup.
> #### Note
>
> It is not possible to initialize this engine during the `Application`'s `onCreate()` method. Any other point in time is fine. For example, a good place to initialize this engine may be in an `Activity`'s `onCreate()`-method.
## Calculate routes
As a next step, you can calculate the route based on two waypoints - a starting location and a destination (both of type `Waypoint` that holds a `GeoCoordinates` instance). Below we use `RoutingOptions` to calculate a route that is optimized for cars. By default, `RoutingOptions` uses `TransportMode.CAR`:
```java Java
Waypoint startWaypoint = new Waypoint(startGeoCoordinates);
Waypoint destinationWaypoint = new Waypoint(destinationGeoCoordinates);
List waypoints =
new ArrayList<>(Arrays.asList(startWaypoint, destinationWaypoint));
routingEngine.calculateRoute(
waypoints,
new RoutingOptions(),
new CalculateRouteCallback() {
@Override
public void onRouteCalculated(@Nullable RoutingError routingError, @Nullable List routes) {
if (routingError == null) {
Route route = routes.get(0);
showRouteDetails(route);
showRouteOnMap(route);
logRouteViolations(route);
} else {
showDialog("Error while calculating a route:", routingError.toString());
}
}
});
```
```kotlin Kotlin
val startWaypoint = Waypoint(startGeoCoordinates)
val destinationWaypoint = Waypoint(destinationGeoCoordinates)
val waypoints = arrayListOf(startWaypoint, destinationWaypoint)
private fun calculateRoute(waypoints: List) {
routingEngine?.calculateRoute(
waypoints,
RoutingOptions(),
object : CalculateRouteCallback {
override fun onRouteCalculated(routingError: RoutingError?, routes: List?) {
if (routingError == null) {
val route: Route = routes!![0]
showRouteDetails(route)
showRouteOnMap(route)
logRouteViolations(route)
} else {
showDialog("Error while calculating a route:", routingError.toString())
}
}
})
}
```
You can call `calculateRoute()` multiple times. For example, you can call it to calculate routes with different routing options in parallel.
> #### Note
>
> When setting a `Waypoint` you can influence on which side of the road a pedestrian or a driver should reach the stop by setting a `sideOfStreetHint`.
>
> If a pedestrian or a driver is moving, a bearing value can help to determine the initial direction by setting `startWaypoint.headingInDegrees = location.bearingInDegrees;`. This can help to avoid unnecessary u-turns if the next destination lies in the back of a driver. For pedestrians this can lead to better routes as unnecessary street crossings can be avoided.
Each route calculation will be performed asynchronously. You will get a `Route` list or a `RoutingError` that holds a possible error when completed. If all goes well, `RoutingError` is null. In case of an error, the route list is null. For example, the engine cannot calculate routes if a route is not feasible for the specified mode of transportation.
> #### Note
>
> If there is no error, the route list will contain only one result. By specifying the number of route alternatives via the route options, you can request additional route variants. By default, a route will be calculated with no route alternatives.
The `showRouteDetails()`-method from the code snippet above is used to show more route details including maneuver instructions. You can find the full source code in the accompanying example app. Maneuver instructions are also explained in greater detail below. The `showRouteOnMap()`-method contains an example, how to render a route on the map. We will explain this shortly in the section below.
## Get ETA and traffic information
Additional information on the route - such as the estimated time it takes to travel to the destination (ETA) and the total length of the route in meters - can be retrieved from the `Route` object as shown below:
```java Java
// estimatedTravelTimeInSeconds includes traffic delay.
long estimatedTravelTimeInSeconds = route.getDuration().getSeconds();
// Get predictive traffic delay data.
long estimatedTrafficDelayInSeconds = route.getTrafficDelay().getSeconds();
int lengthInMeters = route.getLengthInMeters();
```
```kotlin Kotlin
// estimatedTravelTimeInSeconds includes traffic delay.
val estimatedTravelTimeInSeconds: Long = route.duration.seconds
// Get predictive traffic delay data.
val estimatedTrafficDelayInSeconds: Long = route.trafficDelay.seconds
val lengthInMeters: Int = route.lengthInMeters
```
You can find more information on traffic in the [Real-time versus predictive traffic data sources](https://docs.here.com/here-sdk/docs/traffic#real---time-versus-predictive-traffic-data-sources) section.
The estimated time of arrival (ETA) can be displayed in different time zones, which may vary depending on the device's geographic location. For instance, when calculating a route, the device's current time zone may differ from that of the destination. Consider a scenario where a user calculates a route from Berlin to London - each city operates in a different time zone. To address this, you can display the ETA in multiple time zones: the device's current time zone (Berlin), the destination's time zone (London), and UTC (Coordinated Universal Time), which serves as a global reference.
Here's an example of how the same ETA can vary across different time zones:
| ETA based on the device's current time zone in Berlin |
ETA based on the destination's time zone in London |
ETA in UTC |
| 8:30 AM |
7:30 AM |
6:30 AM |
To calculate the ETA in the device's time zone, we use the following method. This method involves retrieving the estimated travel time from a `Route` object, initializing the current date and time, adding the travel time, and formatting the result.
```java Java
/**
* Returns the ETA (as a string in ‘HH:mm’ format) in the current device’s timezone, derived from the estimatedTravelTimeInSeconds, which is sourced from the Route object.
*
* @param route Original route object from RoutingEngine.
* @return A string representing the ETA in "HH:mm" format.
*/
public String getETAinDeviceTimeZone(Route route) {
long estimatedTravelTimeInSeconds = route.getDuration().toSeconds();
// Get an instance of the Calendar class initialized with the current date and time
Calendar calendar = Calendar.getInstance();
// Set the calendar's time to the current date and time
calendar.setTime(new Date());
// Add the estimated travel time (in seconds) to the current time
calendar.add(Calendar.SECOND, (int) estimatedTravelTimeInSeconds);
return getFormattedDate(calendar.getTime());
}
```
```kotlin Kotlin
/**
* Returns the ETA (as a string in ‘HH:mm’ format) in the current device’s timezone, derived from the estimatedTravelTimeInSeconds, which is sourced from the Route object.
*
* @param route Original route object from RoutingEngine.
* @return A string representing the ETA in "HH:mm" format.
*/
fun getETAinDeviceTimeZone(route: Route): String {
val estimatedTravelTimeInSeconds = route.duration.toSeconds()
// Get an instance of the Calendar class initialized with the current date and time
val calendar = Calendar.getInstance()
// Set the calendar's time to the current date and time
calendar.time = Date()
// Add the estimated travel time (in seconds) to the current time
calendar.add(Calendar.SECOND, estimatedTravelTimeInSeconds.toInt())
return getFormattedDate(calendar.time)
}
```
To calculate the ETA in the destination's time zone, we use the following method. This involves obtaining the arrival location's local time and the difference between the local time at the destination and Coordinated Universal Time (UTC).
```java Java
/**
* Calculates the estimated time of arrival (ETA) in the destination timezone for a given route.
* It is possible that the destination can be in a different timezone compared to the source.
* Therefore, we are also calculating the ETA in the destination timezone. For example, the source can be in Berlin and the destination can be in Dubai.
*
* @param route Original route object from RoutingEngine.
* @return A string representing the estimated time of arrival in the destination timezone, formatted as "hh:mm".
*/
public String getETAinDestinationTimeZone(Route route) {
LocationTime arrivalLocationTime = getArrivalLocationTime(route);
Date destinationDate = arrivalLocationTime.localTime;
// The timeOffset represents the difference between the local time at destination and Coordinated Universal Time (UTC) in minutes.
int timeOffset = (int) arrivalLocationTime.utcOffset.toMinutes();
return getFormattedDate(destinationDate, timeOffset);
}
```
```kotlin Kotlin
/**
* Calculates the estimated time of arrival (ETA) in the destination timezone for a given route.
* It is possible that the destination can be in a different timezone compared to the source.
* Therefore, we are also calculating the ETA in the destination timezone. For example, the source can be in Berlin and the destination can be in Dubai.
*
* @param route Original route object from RoutingEngine.
* @return A string representing the estimated time of arrival in the destination timezone, formatted as "hh:mm".
*/
fun getETAinDestinationTimeZone(route: Route): String {
val arrivalLocationTime = getArrivalLocationTime(route)
val destinationDate = arrivalLocationTime!!.localTime
// The timeOffset represents the difference between the local time at destination and Coordinated Universal Time (UTC) in minutes.
val timeOffset = arrivalLocationTime.utcOffset.toMinutes().toInt()
return getFormattedDate(destinationDate, timeOffset)
}
```
To calculate the ETA in Coordinated Universal Time (UTC) for a given route, we use the following method. This method ensures that the ETA is represented in the primary time standard by which the world regulates clocks and time.
```java Java
/**
* Calculates the estimated time of arrival (ETA) in Coordinated Universal Time (UTC) for a given route.
* UTC (Coordinated Universal Time) is the primary time standard by which the world regulates clocks and time.
*
* @param route Original route object from RoutingEngine.
* @return A string representing the estimated time of arrival in UTC, formatted as "HH:mm".
*/
public String getEstimatedTimeOfArrivalInUTC(Route route) {
Date utcDate = getArrivalLocationTime(route).utcTime;
// The UTC offset represents the difference in hours and minutes between a specific time zone and Coordinated Universal Time (UTC).
// It indicates whether the local time is ahead (+) or behind (-) UTC.
// By using an offset of 0, we ensure that the time being represented is in Coordinated Universal Time (UTC).
int utcTimeOffset = 0;
return getFormattedDate(utcDate, utcTimeOffset);
}
```
```kotlin Kotlin
/**
* Calculates the estimated time of arrival (ETA) in Coordinated Universal Time (UTC) for a given route.
* UTC (Coordinated Universal Time) is the primary time standard by which the world regulates clocks and time.
*
* @param route Original route object from RoutingEngine.
* @return A string representing the estimated time of arrival in UTC, formatted as "HH:mm".
*/
fun getEstimatedTimeOfArrivalInUTC(route: Route): String {
val utcDate = getArrivalLocationTime(route)!!.utcTime
// The UTC offset represents the difference in hours and minutes between a specific time zone and Coordinated Universal Time (UTC).
// It indicates whether the local time is ahead (+) or behind (-) UTC.
// By using an offset of 0, we ensure that the time being represented is in Coordinated Universal Time (UTC).
val utcTimeOffset = 0
return getFormattedDate(utcDate, utcTimeOffset)
}
```
The `getArrivalLocationTime(Route route)` method retrieves the arrival time for a given route from the last section of the route. Each section within the route accumulates its duration sequentially. For example, if there are two sections, each five minutes long, the first section’s duration is five minutes, while the second section’s total duration is ten minutes (five minutes for each section).
```java Java
// Returns the arrival time at the final location of the route.
private LocationTime getArrivalLocationTime(Route route) {
int lastSectionIndex = route.getSections().size() - 1;
// The lastSection contains cumulative duration values that increase sequentially.
// For instance, if there are two sections, each with a duration of 5 minutes, the first section will reflect a total duration of 5 minutes,
// while the second section will show a total duration of 10 minutes.
// This is because the total time includes the initial 5 minutes for the first section, followed by an additional 5 minutes to complete the second section, resulting in a cumulative travel time.
Section lastSection = route.getSections().get(lastSectionIndex);
return lastSection.getArrivalLocationTime();
}
```
```kotlin Kotlin
// Returns the arrival time at the final location of the route.
private fun getArrivalLocationTime(route: Route): LocationTime? {
val lastSectionIndex = route.sections.size - 1
// The lastSection contains cumulative duration values that increase sequentially.
// For instance, if there are two sections, each with a duration of 5 minutes, the first section will reflect a total duration of 5 minutes,
// while the second section will show a total duration of 10 minutes.
// This is because the total time includes the initial 5 minutes for the first section, followed by an additional 5 minutes to complete the second section, resulting in a cumulative travel time.
val lastSection = route.sections[lastSectionIndex]
return lastSection.arrivalLocationTime
}
```
Here’s how to format the above ETA with a specific timezone:
```java Java
/**
* Formats the given date to a string representation based on the specified timezone offset.
*
* @param date The Date object to be formatted.
* @param offset The UTC offset in minutes for the desired timezone.
* @return A string representing the formatted time in the specified timezone.
*/
private String getFormattedDate(Date date, int offset) {
// Create a DateFormat instance that formats the time in a short format (e.g., "HH:mm a").
java.text.DateFormat dateFormat = SimpleDateFormat.getTimeInstance(java.text.DateFormat.SHORT);
// Retrieve the TimeZone object corresponding to the given UTC offset.
TimeZone timeZone = getTimeZone(offset);
// Set the DateFormat's timezone to the retrieved TimeZone.
dateFormat.setTimeZone(timeZone);
// Format the date using the configured DateFormat and return the result as a string.
return dateFormat.format(date);
}
/**
* Formats the given date to a string representation using the device's default timezone.
*
* @param date The Date object to be formatted.
* @return A string representing the formatted time in the default timezone.
*/
private String getFormattedDate(Date date) {
// Create a DateFormat instance that formats the time in a short format (e.g., "HH:mm a").
java.text.DateFormat dateFormat = SimpleDateFormat.getTimeInstance(java.text.DateFormat.SHORT);
// Format the date using the configured DateFormat and return the result as a string.
return dateFormat.format(date);
}
/**
* Retrieves a TimeZone object based on the given UTC offset in minutes.
*
* @param utcOffsetInMinutes The UTC offset in minutes (can be positive or negative).
* @return A TimeZone object representing the specified offset from GMT.
*/
private TimeZone getTimeZone(int utcOffsetInMinutes) {
// Calculate the offset in hours.
int hours = utcOffsetInMinutes / 60;
// Calculate the remaining offset in minutes after converting to hours.
int minutes = utcOffsetInMinutes % 60;
// Create a string representing the time zone in GMT format (e.g., "GMT+05:30").
String timeZoneId = String.format("GMT%+03d:%02d", hours, minutes);
// Retrieve and return the TimeZone object corresponding to the constructed GMT time zone ID.
return TimeZone.getTimeZone(timeZoneId);
}
```
```kotlin Kotlin
/**
* Formats the given date to a string representation based on the specified timezone offset.
*
* @param date The Date object to be formatted.
* @param offset The UTC offset in minutes for the desired timezone.
* @return A string representing the formatted time in the specified timezone.
*/
private fun getFormattedDate(date: Date, offset: Int): String {
// Create a DateFormat instance that formats the time in a short format (e.g., "HH:mm a").
val dateFormat = SimpleDateFormat.getTimeInstance(DateFormat.SHORT)
// Retrieve the TimeZone object corresponding to the given UTC offset.
val timeZone = getTimeZone(offset)
// Set the DateFormat's timezone to the retrieved TimeZone.
dateFormat.timeZone = timeZone
// Format the date using the configured DateFormat and return the result as a string.
return dateFormat.format(date)
}
/**
* Formats the given date to a string representation using the device's default timezone.
*
* @param date The Date object to be formatted.
* @return A string representing the formatted time in the default timezone.
*/
private fun getFormattedDate(date: Date): String {
// Create a DateFormat instance that formats the time in a short format (e.g., "HH:mm a").
val dateFormat = SimpleDateFormat.getTimeInstance(DateFormat.SHORT)
// Format the date using the configured DateFormat and return the result as a string.
return dateFormat.format(date)
}
/**
* Retrieves a TimeZone object based on the given UTC offset in minutes.
*
* @param utcOffsetInMinutes The UTC offset in minutes (can be positive or negative).
* @return A TimeZone object representing the specified offset from GMT.
*/
private fun getTimeZone(utcOffsetInMinutes: Int): TimeZone {
// Calculate the offset in hours.
val hours = utcOffsetInMinutes / 60
// Calculate the remaining offset in minutes after converting to hours.
val minutes = utcOffsetInMinutes % 60
// Format the time zone as a GMT offset string, such as "GMT+05:30" or "GMT-04:00".
val timeZoneId = "GMT%+03d:%02d".format(hours, minutes)
// Retrieve and return the TimeZone object corresponding to the constructed GMT time zone ID.
return TimeZone.getTimeZone(timeZoneId)
}
```
To update traffic information on an existing route, see [Update traffic information](https://docs.here.com/here-sdk/docs/traffic-update). This allows you to refresh traffic data and ETAs.
### Traffic-optimized route calculation
When calculating routes, the HERE SDK automatically integrates the traffic data to provide the fastest and most accurate paths.
By default, routes are optimized considering both current and predictive traffic conditions. The `RoutingEngine` uses predictive location services to anticipate upcoming traffic while traversing the route, ensuring that calculated routes account for traffic conditions you're likely to encounter.
* Routes automatically consider current traffic when calculating the fastest path.
* The `RoutingEngine` anticipates future traffic conditions based on historical patterns and real-time data.
* Estimated arrival times (ETAs) include delays from traffic congestion.
* Each `Span` of a route contains detailed traffic speed information with jam factors.
* Optionally, [query traffic data](https://docs.here.com/here-sdk/docs/traffic-engine) along the route with the `TrafficEngine`.
Traffic optimization happens automatically - you don't need to enable it explicitly, but you can turn it also off.
### Disable traffic optimization
The `RoutingEngine` uses *predictive location services* to anticipate upcoming traffic conditions. This means that the routes generated can change dynamically based on real-time traffic data, leading to different route shapes.
Usually, traffic optimization is desired, but if you are, for example, a bus driver, you cannot choose better routes as you have to follow a pre-defined bus route.
The HERE SDK provides a feature called `TrafficOptimizationMode` to manage this. If you prefer stable routes and are not concerned about traffic conditions, you can completely disable traffic optimization by choosing `TrafficOptimizationMode.DISABLED`:
```java Java
routeOptions.trafficOptimizationMode = TrafficOptimizationMode.DISABLED;
```
```kotlin Kotlin
routeOptions.trafficOptimizationMode = TrafficOptimizationMode.DISABLED
```
Note that disabling `TrafficOptimizationMode` will also disable any traffic information from the `Route` and the ETA will not include potential delays.
### Get incident summary from a route
If you are interested in basic traffic incident information along the route, you can get a summary of the traffic situation directly from the `Route` instance - per `Section`, by calling `section.getTrafficIncidents()`. The resulting `TrafficIncidentOnRoute` object contains a small subset of the data that is available also from the `TrafficEngine`, but it can be useful to give a first overview.
Examples of incident categories that are delivered as part of the `Route` object:
* `ACCIDENT`: Traffic accident.
* `CONGESTION`: Traffic jam.
* `CONSTRUCTION`: Construction work.
* `DISABLED_VEHICLE`: Overturned or broken down vehicle(s) on the road.
* `LANE_RESTRICTION`: A lane restriction.
* `MASS_TRANSIT`: Incident involving mass transit such as rail or subway.
* `PLANNED_EVENT`: Incident involving activities such as sport events or festivals.
* `ROAD_CLOSURE`: Road closure.
* `ROAD_HAZARD`: Dangerous obstruction on the road such as downed tree or traffic light out.
* `WEATHER`: Adverse weather conditions.
* `OTHER`: The incident is known but it doesn't fit into any of the other categories.
* `UNKNOWN`: No specific incident type is provided.
Note that the HERE SDK does not expose icon assets for these incident types, but you can enable the `TRAFFIC_INCIDENTS` map feature layer to show all incidents with an icon on the map. Similarly, you can also enable a `TRAFFIC_FLOW` layer to show the general traffic flow on the streets. Take a look at the [Get started with Traffic](https://docs.here.com/here-sdk/docs/traffic) section for more details.
## Find upcoming speed limits and more
The `Route` object exposes detailed information along a `Route` to know upcoming speed limits, street attributes and road names, railway crossings, dynamic traffic info and much more.
Take a look at the following attributes: `Span.sectionPolylineOffset`, `Span.dynamicSpeedInfo`, `Span.streetAttributes`, `Span.carAttributes`, `Span.truckAttributes`, `Span.scooterAttributes`, `Span.walkAttributes`, `Span.durationInSeconds`, `Span.streetNames`, `Span.routeNumbers`, `Span.speedLimitInMetersPerSecond`, `Span.consumptionInKilowattHours`, `Span.functionalRoadClass`, `Span.duration`, `Span.baseDuration`. Consult the API Reference for a full list of attributes.
Each attribute is given per span and it is valid for the entire length of a span. Sometimes you will also find segment IDs that indicate a portion of the road network between two intersections. A `Span` is a route-related concept - it is a portion of the route which has the same attributes. There can be multiple spans on the same segment and each segment usually has a start offset and an end offset.
A `Span` defines the smallest part of a route segment and its curvature is exposed as a list of `GeoCoordinates`.
## Get railway crossings information
A list of `RouteRailwayCrossing` can be retrieved from `Route` object that contains infomation which indicates the `GeoCoordinates` of the railway crossing, a `RouteOffset` and the `RouteRailwayCrossingType` of a railway for a train or a tram line that is crossing the route.
A `RouteOffset` is a location on the route defined by the section index and the distance in meters from the start of that section to the specified location on the route.
For more information about `RouteOffset`, look at the [API Reference](/sdk-for-android-navigate-api-reference-latestrouting/RouteOffset).
A usage example for `RouteRailwayCrossing` is shown below:
```java Java
for (RouteRailwayCrossing routeRailwayCrossing : route.getRailwayCrossings()) {
// Coordinates of the route offset
GeoCoordinates routeOffsetCoordinates = routeRailwayCrossing.coordinates;
// Index of the corresponding route section. The start of the section indicates the start of the offset.
int routeOffsetSectionIndex = routeRailwayCrossing.routeOffset.sectionIndex;
// Offset from the start of the specified section to the specified location along the route.
double routeOffsetInMeters = routeRailwayCrossing.routeOffset.offsetInMeters;
Log.d(TAG, "A railway crossing of type " + routeRailwayCrossing.type.name() +
"is situated " +
routeOffsetInMeters + " m away from start of section: " +
routeOffsetSectionIndex);
}
```
```kotlin Kotlin
for (routeRailwayCrossing in route.railwayCrossings) {
// Coordinates of the route offset
val routeOffsetCoordinates: GeoCoordinates = routeRailwayCrossing.coordinates
// Index of the corresponding route section. The start of the section indicates the start of the offset.
val routeOffsetSectionIndex: Int = routeRailwayCrossing.routeOffset.sectionIndex
// Offset from the start of the specified section to the specified location along the route.
val routeOffsetInMeters: Double = routeRailwayCrossing.routeOffset.offsetInMeters
Log.d(TAG, "A railway crossing of type " + routeRailwayCrossing.type.name +
"is situated " +
routeOffsetInMeters + " m away from start of section: " +
routeOffsetSectionIndex)
}
```
## Detect route violations
A route may contain a list of `NoticeCode` values that describe potential issues after a route was calculated. For example, when a route should avoid tunnels and the only possible route needs to pass a tunnel, the `Route` contains a notice that the requested avoidance of tunnels was violated.
* It is recommended to always check a calculated `Route` for possible violations.
* The `NoticeCode` is part of a `Notice` object. A list of possible `Notice` objects can be accessed per `Section` of a `Route`.
* The list will be empty, when no violation occurred.
* If any possible violation is not desired, it is recommended to skip routes that contain at least one violation.
However, an implementation may judge case by case depending on the requested route options and the actual list of `NoticeCode` values. More about route options can be found in the next section. Important: for the sake of simplicity, the code snippets in this guide do not evaluate the possible enum values of a notice.
You can detect possible route notices with the following method:
```java Java
// A route may contain several warnings, for example, when a certain route option could not be fulfilled.
// An implementation may decide to reject a route if one or more violations are detected.
private void logRouteViolations(Route route) {
for (Section section : route.getSections()) {
for (Span span : section.getSpans()) {
List spanGeometryVertices = span.getGeometry().vertices;
// This route violation spreads across the whole span geometry.
GeoCoordinates violationStartPoint = spanGeometryVertices.get(0);
GeoCoordinates violationEndPoint = spanGeometryVertices.get(spanGeometryVertices.size() - 1);
for (int index : span.getNoticeIndexes()) {
SectionNotice spanSectionNotice = section.getSectionNotices().get(index);
// The violation code such as "VIOLATED_VEHICLE_RESTRICTION".
String violationCode = spanSectionNotice.code.toString();
Log.d(TAG, "The violation " + violationCode + " starts at " + toString(violationStartPoint) + " and ends at " + toString(violationEndPoint) + " .");
}
}
}
}
```
```kotlin Kotlin
// A route may contain several warnings, for example, when a certain route option could not be fulfilled.
// An implementation may decide to reject a route if one or more violations are detected.
private fun logRouteViolations(route: Route) {
for (section in route.sections) {
for (span in section.spans) {
val spanGeometryVertices: List = span.geometry.vertices
// This route violation spreads across the whole span geometry.
val violationStartPoint: GeoCoordinates = spanGeometryVertices[0]
val violationEndPoint: GeoCoordinates =
spanGeometryVertices[spanGeometryVertices.size - 1]
for (index in span.noticeIndexes) {
val spanSectionNotice: SectionNotice = section.sectionNotices[index]
// The violation code such as "VIOLATED_VEHICLE_RESTRICTION".
val violationCode: String = spanSectionNotice.code.toString()
Log.d(TAG, "The violation " + violationCode + " starts at "
+ toString(violationStartPoint) + " and ends at " + toString(violationEndPoint) + " .")
}
}
}
}
```
> #### Note
>
> All routes contain altitude values along the route. For example, to create an elevation profile for a planned bicycle trip.
## Try the Routing example apps
On [GitHub](https://github.com/heremaps/here-sdk-examples) you can find the "Routing" example app, provided in both [Java](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Java/Routing) and [Kotlin](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Kotlin/RoutingKotlin), that contains all the code snippets from above, plus more.
Additionally, you can find on [GitHub](https://github.com/heremaps/here-sdk-examples) the "PublicTransit" example app, Provided in both [Java](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Java/PublicTransit) and [Kotlin](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Kotlin/PublicTransitKotlin). It shows how to use the `TransitRoutingEngine` to calculate public transit routes from A to B with a number of waypoints in between.
Take also a look at the [Rerouting](https://github.com/heremaps/here-sdk-examples/tree/master/examples/latest/navigate/android/Java/Rerouting) example app on [GitHub](https://github.com/heremaps/here-sdk-examples). Note that it requires the HERE SDK (Navigate), but the UI building blocks and the code for the `IconProvider` can be also used, for example, to show road shield icons as part of a route preview panel.