Continuous replanning

VERSION 1.26.3
PUBLIC PREVIEW

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

Route update mode

This mode specifies whether the system should periodically update the active route and search for better route proposals. There are two possible values:

  • ENABLED - The system periodically updates the active route and searches for better route proposals, based on the replanRouteInterval. How these proposals are handled is defined by the BetterProposalAcceptanceMode.
  • DISABLED - The system neither updates the active route nor searches for better route proposals.

By default, RouteUpdateMode is set to ENABLED. It can be 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 road conditions can change constantly. As a result, route data may become outdated during the trip. Route refresh provides users with the most up-to-date route information, such as:

  • Travel time and ETA (Estimated Time of Arrival)
  • Traffic information (delays and traffic volume)
  • 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 is always applied automatically, and the reason is be set to Refresh.

Continuous Replanning - finding better alternatives

To receive alternative route proposals, RouteUpdateMode must be set to ENABLED. The frequency at which the system searches for better routes is defined by replanRouteInterval. Based on this interval, the Navigation module periodically attempts to find a better alternative to the current route. This process is referred to as continuous replanning. The BetterProposalAcceptanceMode determines how better route proposals are 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: It is the responsibility of the provider of the custom route proposal selector to make sure that a reachable proposal is chosen whenever the current route is blocked.

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 via RouteAddedListener for the following reasons:

  • BetterRouteProposed - A better alternative was found for the current route.
  • AvoidBlockage - A better alternative was found due to blockages on the current route.
  • WithinRange - Used only when an electric vehicle is navigating: a better alternative was 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 base route with the forking route and returns a Result containing either RouteDifferences or a RouteComparisonFailure if the comparison fails. The returned RouteDifferences object provides insights into the advantages and disadvantages of the proposed route compared to the current one.

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 first informed by the RouteAddedListener, which signals that a new route has been added to the navigation session. Then the new route is automatically set as the active route, and the ActiveRouteChangedListener notifies the user about the active route change.
  • Manual - The better route proposal is added to the navigation session, and the user is informed by the RouteAddedListener. The proposal is applied only when you call the TomTomNavigation.selectActiveRoute(RouteId) method, or when the driver steers into the proposed route. Once the new route is selected, the ActiveRouteChangedListener is triggered.
  • UnreachableOnly - The active route is automatically replaced with a better proposal only if the current route is blocked or its itinerary is not reachable due to insufficient battery charge. Otherwise, the better route proposal is handled in the same way as in the Manual mode.

By default, the mode is set to Automatic. You can change it by passing a BetterProposalAcceptanceMode to the navigation configuration (e.g., Configuration) during 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 updated at runtime, even if the navigation has started.

navigation.betterProposalAcceptanceMode
navigation.betterProposalAcceptanceMode = BetterProposalAcceptanceMode.Manual

Automatic handling

Replan proposals are applied automatically. The user is notified by the RouteAddedListener with one of the following reasons: RouteAddedReason.BetterRouteProposed, RouteAddedReason.AvoidBlockage, or RouteAddedReason.WithinRange. Then, the new route is automatically set as the active route, and the ActiveRouteChangedListener notifies the user of the active route change.

1navigation.addRouteAddedListener { route, options, reason ->
2 if (reason is RouteAddedReason.BetterRouteProposed ||
3 reason is RouteAddedReason.AvoidBlockage ||
4 reason is RouteAddedReason.WithinRange
5 ) {
6 // ActiveRouteChangedListener will follow
7 // Your code goes here (e.g. displaying RouteDifferences,...)
8 }
9}
10navigation.addActiveRouteChangedListener { newActiveRoute ->
11 // Your code goes here
12}

Unreachable only handling

Replan proposals are applied automatically only when the current route is blocked or unreachable due to insufficient battery charge (in electric vehicle use cases only). In such cases, the user is notified via the RouteAddedListener with one of the following reasons: RouteAddedReason.AvoidBlockage or RouteAddedReason.WithinRange. The notification is followed by the ActiveRouteChangedListener, which notifies the user of the active route change.

Other types of proposals are delivered using the RouteAddedListener with the reason RouteAddedReason.BetterRouteProposed. You can decide whether to select the proposed route as active using selectActiveRoute, or to ignore it.

1navigation.addRouteAddedListener { route, options, reason ->
2 if (reason is RouteAddedReason.AvoidBlockage || reason is RouteAddedReason.WithinRange) {
3 // ActiveRouteChangedListener will follow
4 // Your code goes here (e.g. displaying RouteDifferences,...)
5 } else if (reason is RouteAddedReason.BetterRouteProposed) {
6 // Your code goes here (e.g. displaying RouteDifferences, selecting the proposal,..)
7 navigation.selectActiveRoute(route.id)
8 }
9}
10navigation.addActiveRouteChangedListener { newActiveRoute ->
11 // Your code goes here
12}

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

Manual handling

A better route proposal is delivered via the RouteAddedListener with one of the following reasons: RouteAddedReason.BetterRouteProposed, RouteAddedReason.AvoidBlockage, or RouteAddedReason.WithinRange. You can decide whether to select the proposed route as active using selectActiveRoute, or to ignore it.

1navigation.addRouteAddedListener { route, _, reason ->
2 if (reason is RouteAddedReason.BetterRouteProposed ||
3 reason is RouteAddedReason.AvoidBlockage ||
4 reason is RouteAddedReason.WithinRange
5 ) {
6 // Your code goes here (e.g. displaying RouteDifferences, selecting the proposal,..)
7 navigation.selectActiveRoute(route.id)
8 }
9}

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 interval between route updates is set to three (3) minutes, and the minimum remaining travel time is set to ten (10) minutes. After this point, route refresh and continuous replanning are automatically disabled.

You can configure these parameters using 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 the user deviates from the current route, the navigation system informs them of the deviation. The navigation module may then do one of the following:

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

The new route is calculated using the route optimization type defined in the RoutePlan.

Stick to route behavior after deviation

The navigation system is designed to seamlessly handle cases where a user prepares a route in advance and then imports it. This ensures the imported route is preserved and the user can continue following it without interruption. You can find more information in the Route Planning - Route Import feature.

Basic deviation handling

If the driver deviates from a route imported using supporting points, the routing engine will attempt to keep them within a 1 km radius of the original route until they reach the rejoining point.

ss deviation handling 1

If the user is not yet on the imported route, the system routes them to its starting point—regardless of the distance.

ss deviation handling 2

Increase distance to rejoin point upon subsequent deviations

Upon subsequent deviations, a rejoining point’s distance is increased exponentially. This addresses a known limitation of the fixed cutoff distance, which can cause U-turns when the driver deviates. If the car continues to deviate from the replanned route, the initial 1 km cutoff radius is doubled after each deviation until the driver starts following the route.

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

ss highway example

If the user exits at junction #1, and the fixed cutoff is shorter than the distance to the next junction, the system continuously reroutes back to junction #1. This happens because the system considers it necessary to visit 4 km of the highway track.

In this iteration, the rejoining point is calculated using a fixed cutoff. A cutoff means removing supporting points from the route up to 1km ahead.

Automatic handling

By default, the navigation module automatically plans a new route when the user deviates. To disable this behavior, configure TomTomNavigation as follows:

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

With automatic replanning enabled, the user doesn’t need to take any action — the new route is applied automatically. The RouteAddedListener notifies the user when the new route is added to the navigation session. This notification includes the RouteAddedReason.Deviated as the reason for adding the new route, along with the Route itself. Once the new route is activated, the ActiveRouteChangedListener notifies the user of the active route change. The previous route is then removed from the navigation session, and the RouteRemovedListener notifies the user of its removal.

Manual handling

This is the default strategy for handling route deviations. Manual handling means that when a user is informed about a deviation from the current route, navigation is stopped. It resumes only after a new route has been planned and started.

To manually update the route, call TomTomNavigation.setActiveRoutePlan(RoutePlan). The RoutePlan is built with a new Route to follow and the RoutePlanningOptions used to plan 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. This instruction indicates the start of navigation.

Here is an 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 replanning is successful, the Navigation module uses the returned route to replace the current one. If not, the module attempts to replan the route again. The number of replan attempts and the delay between them 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.

You can override the default parameters as shown below:

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 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 a route is less than the route’s total length as specified 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: