Continuous replanning

VERSION 1.20.1
PUBLIC PREVIEW

Navigation SDK for Android is only available upon request. Contact us to get started.

Route update mode

This mode specifies whether or not the system should try to periodically update the active route and look for better route proposals. There are two possible values:

  • ENABLED - The system periodically updates the active route and looks for better route proposals, based on replanRouteInterval. The way that better route proposals are handled is defined by BetterProposalAcceptanceMode.
  • DISABLED - The system does not update the active route nor search for better route proposals.

By default, RouteUpdateMode is set to ENABLED. It is configured in the following way:

1val customRouteReplanningEngine =
2 RouteReplanningEngineFactory.create(
3 routeReplanner = OnlineRouteReplannerFactory.create(routePlanner),
4 options =
5 RouteReplanningEngineOptions(
6 routeUpdateMode = RouteUpdateMode.Enabled,
7 ),
8 )
9val onlineConfiguration =
10 Configuration(
11 context = context,
12 navigationTileStore = navigationTileStore,
13 locationProvider = locationProvider,
14 routePlanner = routePlanner,
15 routeReplanningEngine = customRouteReplanningEngine,
16 )

Route refresh

Every trip takes time and the road situation is constantly changing. As a result, route data may become outdated during a trip. Route refresh provides users the most up-to-date route information such as:

  • Travel time and ETA (Estimated Time of Arrival)
  • Traffic information (delay and amount of traffic)
  • Updated legs and sections

Route refresh might fail if the initial route planning was done with densely placed supporting points in comparison to route geometry. The replanning is made based on the geometry of the current route, where the density of coordinates may be lower.

Replanning response

The updated route will always be automatically applied and the reason will be set to RouteUpdatedReason.Refresh.

Continuous Replanning - finding better alternatives

If you would like to receive alternative route proposals, RouteUpdateMode must be set to ENABLED. The frequency of searches for better route proposals is defined with replanRouteInterval. The Navigation module will then, based on this interval, periodically attempt to find a better alternative to the current route. This is what continuous replanning refers to. BetterProposalAcceptanceMode defines how a better route proposal is handled.

If the default implementation of the RouteProposalSelector does not perform as you desire, you can provide your own object when creating the RouteReplanningEngine. Note that it’s up to the provider of the custom route proposal selector to make sure that a reachable proposal is chosen whenever the current route has a blockage.

1val customRouteReplanningEngine =
2 RouteReplanningEngineFactory.create(
3 routeReplanner = OnlineRouteReplannerFactory.create(routePlanner),
4 options = routeReplanningEngineOptions,
5 routeProposalSelector = customRouteProposalSelector,
6 )
7val onlineConfiguration =
8 Configuration(
9 context = context,
10 navigationTileStore = navigationTileStore,
11 locationProvider = locationProvider,
12 routePlanner = routePlanner,
13 routeReplanningEngine = customRouteReplanningEngine,
14 )

Replanning response

A better route proposal is added to the navigation session with the following reasons:

  • BetterRouteProposed - A better alternative was found for the current route.
  • AvoidBlockage - A better alternative has been found due to blockages on the current route.
  • WithinRange - Only used in cases where an electric vehicle is navigating: a better alternative has been found due to insufficient battery charge.

Comparing routes

After a new route is proposed, you can compare it to the current route with the compareToForkingRoute method. This method compares the current route with the proposed route and returns the Result containing RouteDifferences, or a RouteComparisonFailure if the comparison fails. The returned RouteDifferences object can provide insights into the advantages and disadvantages of the proposed route as compared to the current route.

1val reasoning = mutableListOf<String>()
2currentRoute.compareToForkingRoute(proposedRoute).ifSuccess { differences ->
3 val timeDifference = differences.timeDifference.travelTime.inWholeMinutes
4 if (timeDifference > 0) {
5 reasoning.add("The proposed route is $timeDifference minutes faster")
6 }
7 val chargingStopsDifference = differences.chargingDifference.numberOfChargingStops
8 if (chargingStopsDifference > 0) {
9 reasoning.add("The proposed route has $chargingStopsDifference less charging stops")
10 }
11 with(differences.sectionsDifferences.traffic) {
12 val currentTrafficDelay = firstOnly.fold(Duration.ZERO) { acc, section -> acc + section.delay }
13 val proposedTrafficDelay = secondOnly.fold(Duration.ZERO) { acc, section -> acc + section.delay }
14 val trafficDelayDifference = (currentTrafficDelay - proposedTrafficDelay).inWholeMinutes
15 if (trafficDelayDifference > 0) {
16 reasoning.add("The proposed route has $trafficDelayDifference less minutes of traffic delay")
17 }
18 }
19}
20// display reasoning

Better proposal acceptance mode

There are three ways to handle a better route proposal:

  • Automatic - Better route proposals are automatically applied as active routes. The user is informed first by the RouteAddedListener, that the new route was added to the navigation. Then the new route is set as the active route, the ActiveRouteChangedListener notifies the user about the active route change.
  • Manual - The better route proposal is added to the navigation session, the user is informed about it by the RouteAddedListener. The better alternative route that was found will be applied when you call the TomTomNavigation.selectActiveRoute(RouteId) method or when the driver steers into that road. After selecting the new active route, the ActiveRouteChangedListener is triggered.
  • UnreachableOnly - The active route is automatically replaced with a better proposal only if the current route contains a blockage or its itinerary is not reachable due to insufficient battery charge. Otherwise, a better route proposal is handled in the same way as in the Manual mode.

Automatic mode is set by default and can be changed by passing the BetterProposalAcceptanceMode to the navigation configuration (e.g., Configuration) during the TomTomNavigation initialization.

1val onlineConfiguration =
2 Configuration(
3 context = context,
4 navigationTileStore = navigationTileStore,
5 locationProvider = locationProvider,
6 routePlanner = routePlanner,
7 betterProposalAcceptanceMode = BetterProposalAcceptanceMode.Manual,
8 )

The current BetterProposalAcceptanceMode can also be checked and changed during runtime, even if the navigation is already started.

navigation.betterProposalAcceptanceMode
navigation.betterProposalAcceptanceMode = BetterProposalAcceptanceMode.Manual

Automatic handling

Replan proposals are applied automatically. The user is notified by the RouteAddedListener, which provides both a new Route and a RouteAddedReason as the reason the new route was added. Then the new route is set as the active route, the ActiveRouteChangedListener notifies the user about the active route change.

1navigation.addRouteAddedListener { route, options, reason ->
2 // Your code goes here
3}
4navigation.addActiveRouteChangedListener { newActiveRoute ->
5 // Your code goes here
6}

Unreachable only handling

Replan proposals are applied automatically only when there is a blockage on the current route or if the current route is unreachable due to an insufficient battery charge (only in an electric vehicle use case). In that case, the user is notified by the RouteAddedListener. The notification is followed by the ActiveRouteChangedListener notifying the user about the active route change.

Manual handling

The obtained route is proposed using the RouteAddedListener with the RouteAddedReason set to BetterRouteProposed. You decide whether the proposed route should be selected as active with selectActiveRoute or ignored. The navigation is then updated with the proposed route.

1navigation.addRouteAddedListener { route, _, reason ->
2 if (reason is RouteAddedReason.BetterRouteProposed) {
3 navigation.selectActiveRoute(route.id)
4 }
5}

The Navigation supports decide-by-steering, which means that the driver can also select a better route proposal by steering into it. The system will detect the driver’s location on the proposed route and set it as active.

Specifying replanning intervals

By default, the intervals between route updates are set to three (3) minutes. The minimum remaining travel time is set to ten (10) minutes. After this point, route refresh and continuous replanning are automatically disabled.

These parameters can be set in RouteReplanningEngineOptions.

1val customRouteReplanningEngine =
2 RouteReplanningEngineFactory.create(
3 routeReplanner = OnlineRouteReplannerFactory.create(routePlanner),
4 options =
5 RouteReplanningEngineOptions(
6 replanRouteInterval = replanRouteInterval,
7 validRemainingRouteDuration = validRemainingRouteDuration,
8 ),
9 )
10val onlineConfiguration =
11 Configuration(
12 context = context,
13 navigationTileStore = navigationTileStore,
14 locationProvider = locationProvider,
15 routePlanner = routePlanner,
16 routeReplanningEngine = customRouteReplanningEngine,
17 )

Replanning on deviation

If a user has deviated from the current route, the navigation informs them of it. The navigation module may do one of the following things:

  • Automatically plan a new route. If supporting points are provided in the initial planning stick to the route.
  • Wait for the user to manually provide a new route.

A new route is planned by using the route optimization type defined in the RoutePlan.

Stick to route behavior after deviation

The navigation system is designed to seamlessly handle the behavior when a user prepares a route in advance and then imports it. This ensures that the user does not lose the imported route and can continue to follow it without interruption. You can find more information in the Route Planning - Route Import feature.

Basic deviation handling

If the route is imported via supporting points, when the user deviates our routing engine will aim at keeping the driver within 1KM radius of the original route until the rejoining point.

deviation handling 1

If the user is not yet on the imported route, we route to the starting point of the imported route (no matter how far this is).

deviation handling 2

Increase distance to rejoin point upon subsequent deviations

Upon subsequent deviations, a rejoining point’s distance is increased exponentially. This overcomes a known limitation with the fixed cutoff distance that makes it possible to get U-turns when deviating. If the car continues deviating from the replanned route, a 1km cut-off radius is doubled after every deviation until the driver starts following the route.

For example: On some sparse road networks a fixed cutoff distance may result in U-turns.

highway example

If the user exits at junction #1, due to the fixed cutoff being less than the distance to the next junction, the resulting route always reroutes back to junction #1. This is because the system deems it necessary to visit 4km of the highway track.

In this iteration, the rejoining point is calculated as a fixed cutoff (cutoff means removing supporting points from a route for 1km ahead).

Automatic handling

The default is to automatically plan a new route. To turn off automatic route planning, configure TomTomNavigation:

1val onlineConfiguration =
2 Configuration(
3 context = context,
4 navigationTileStore = navigationTileStore,
5 locationProvider = locationProvider,
6 routePlanner = routePlanner,
7 deviationReplanningMode = DeviationReplanningMode.None,
8 )

This means the user doesn’t need to take any action, because the new route is automatically applied. The RouteAddedListener notifies the user when the new route is added to the navigation session. The notification contains the RouteAddedReason.Deviated as the reason for adding the new route and the Route itself. The ActiveRouteChangedListener then notifies the user about the active route change. When the new route becomes active the old route is removed from the navigation session. RouteRemovedListener notifies the user about the removed route.

Manual handling

This is the default route deviation handling strategy. Manual handling means that once a user is informed about a deviation from the route, navigation is stopped. Navigation only starts again once a new route has been planned and started.

A manual route update is made by calling TomTomNavigation.setActiveRoutePlan(RoutePlan). The RoutePlan is built with a Route for the user to follow and the RoutePlanningOptions used for planning that route.

val routePlan = RoutePlan(route = route, routePlanningOptions = routePlanningOptions)
tomTomNavigation.setActiveRoutePlan(routePlan)

To keep navigation guidance consistent, before calling TomTomNavigation.setActiveRoutePlan(RoutePlan), the DepartureInstruction should be filtered out from the proposed route plan. An instruction is just an indicator of starting navigation.

Example of how to remove the DepartureInstruction from the first leg of the route:

1fun Route.removeDepartInstruction(): Route {
2 val firstLeg = legs.first()
3 return firstLeg.instructions
4 .dropWhile { it is DepartureInstruction }
5 .let { newInstructions ->
6 val newFirstLeg =
7 RouteLeg(
8 points = firstLeg.points,
9 instructions = newInstructions,
10 summary = firstLeg.summary,
11 mapReferences = firstLeg.mapReferences,
12 )
13 val oldLegsWithoutFirst = legs.drop(1)
14 val newLegs = listOf(newFirstLeg) + oldLegsWithoutFirst
15 Route(
16 id,
17 summary,
18 newLegs,
19 routeStops,
20 sections,
21 modificationHistory,
22 forkPoints,
23 guidanceProgressOffset,
24 computedAs,
25 routePoints,
26 planningReason,
27 )
28 }
29}

Replan retry policy

If the replanning is successful, the Navigation module uses the returned route to replace the current one. Otherwise the Navigation module tries to replan the route again. The number of replan attempts and the delay between retries are defined by the ReplanningRetryPolicy interface.

Policy configuration

The default implementation of ReplanningRetryPolicy periodically retries the operation with increasing delays between calls. You can set this up using ReplanningRetryPolicyFactory.create() with default values.

The default maximum delay time is ten (10) seconds.

The default parameters can also be overridden as follows:

1val replanningRetryPolicy =
2 ReplanningRetryPolicyFactory.create(
3 maxRetryDelay = maxRetryDelay,
4 )

Once the policy is configured, it must be provided during initialization of the Navigation module:

1val onlineConfiguration =
2 Configuration(
3 context = context,
4 navigationTileStore = navigationTileStore,
5 locationProvider = locationProvider,
6 routePlanner = routePlanner,
7 replanningRetryPolicy = replanningRetryPolicy,
8 )

Providing a custom replan retry policy

You can also provide your own implementation instead of using the default policy. To do this, implement the ReplanningRetryPolicy interface and pass it during the Navigation module initialization.

Incremental guidance computation

If the guidanceProgressOffset of the route is less than the length in the route summary, the route needs to be updated. Instead of calling the planRoute method, the replanning engine can call the advanceGuidanceProgress method. This adds more instructions and corresponding lane guidance to the route and increases the guidanceProgressOffset of the route.

Next steps

Since you have learned about route replanning, here are recommendations for the next steps: