Skip to content

Better Route Detection

Better route detection

This tutorial shows how to handle better route detection.

Get started

Better routes will be detected during navigation, and the detection result can be categorized into two kinds: * A better route must be detected and used for navigation since the current route is no longer valid. * A better route will save the user some time to reach the forward waypoint or destination.

During better route searching, the routing preferences used for the current route in navigation will be preserved. Besides, the route source (calculated on the cloud or on board) will be preserved if possible.

Handle better route update progress

Meeting any of the following conditions will definitely trigger better route detection:

If a better route is found, it will be updated automatically in the navigation session. No interaction with user is required in this case.

Make a customized navigation listener to handle better route updating progress.

class NavigationObserver : public tn::drive::api::NavigationEventListener
{
public:
    void onNavigationRouteUpdating(const tn::drive::models::v1::BetterRouteUpdateProgress& progress) override
    {
        // check better route updating status and reason
        const auto status = progress.status;
        const auto reason = progress.reason;

        // for internal update reason, a route may updated with following changes:
        // 1) guidance information update
        // 2) map data version used for routing changed
        if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::UpdateInternal)
        {
            if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Succeeded)
            {
                updateRoute(progress);
            }
        }
        else if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::AvoidBlockingTraffic)
        {
            if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Succeeded)
            {
                // get avoided blocking traffic incident
                // NOTICE: if it's the blocking traffic flows that trigger the route update, 
                // there's no such information available from route update progress context
                const auto& context = progress.route_update_context;
                const auto& incidents = context.blocking_incidents;
                if (!incidents.empty())
                {
                    // get the previous route that incidents affect
                    const auto& previousRoute = context.route;
                    for (const auto& incident : incidents)
                    {
                        // along route location of the incident
                        // it's based on the previous route, not current route after avoiding
                        const auto& location = incident.location;
                    }
                }

                updateRoute(progress);
            }
        }
        else if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::AvoidTimedRestriction)
        {
            if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Succeeded)
            {
                // get avoided timed restriction affected edges
                const auto& context = progress.route_update_context;
                const auto& edges = context.time_restriction_edges;

                // get the previous route that incidents affect
                const auto& previousRoute = context.route;
                for (const auto& edge : edges)
                {
                    // along route indices of the edge affected by the timed restriction
                    // it's based on the previous route, not current route after avoiding
                    const auto legIndex = edge.leg_index;
                    const auto stepIndex = edge.step_index;
                    const auto edgeIndex = edge.edge_index;
                }

                updateRoute(progress);
            }
        }
        // deviation from current route will trigger better route detection in most cases
        // there's rare cases that a new route is not necessary to be found
        // the case is when vehicle deviate from current route to a service area or a parking lot
        // and the distance from current vehicle location to the route is not large enough
        // in this case it may be annoying to make a better route detection and updating
        else if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::Deviation) 
        {
            if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Started)
            {
                requestAudio(tn::drive::models::v1::AudioPromptType::Deviation);
                requestAudio(tn::drive::models::v1::AudioPromptType::Rerouting);
            }
            else if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Canceled)
            {
                // vehicle is back on route before a new route is retrieved
            }
            else if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Succeeded)
            {
                updateRoute(progress);
            }
        }
        else if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::InsufficientBatteryLevel)
        {
            // refer to tutorial of navigation for electric vehicles
            // ...
        }
    }

    // omit other methods

private:
    void requestAudio(tn::drive::models::v1::AudioPromptType type)
    {
        tn::drive::models::v1::AudioRequest request;
        request.request_type = type;

        tn::drive::models::v1::AudioInstruction instruction;
        driveSession->requestAudio(request, instruction);

        playAudioInstruction(instruction);
    }

    void updateRoute(const tn::drive::models::v1::BetterRouteUpdateProgress& progress)
    {
        // get route in navigation now
        const auto& currentRoute = progress.route;

        // update current route to necessary components in your application
        // it's already in navigation state inside the drive session components
    }
};

Handle better route proposals

Meeting any of the following conditions will optionally trigger better route detection:

  • traffic delay on current route is long enough
  • timer to try searching for better route triggered
  • next planned charging station is not available (EV only)
// make a customized better route observer to handle better route candidate
class NavigationObserver : public tn::drive::api::NavigationEventListener
{
public:
    void onBetterRouteDetected(const tn::drive::models::v1::BetterRouteProposal& proposal) override
    {
        const auto status = proposal.status;
        if (status == tn::drive::models::v1::BetterRouteProposal::BetterRouteStatus::BetterRouteDetected)
        {
            const auto reason = proposal.reason;
            if (reason == tn::drive::models::v1::BetterRouteProposal::BetterRouteReason::SaveTime)
            {
                const auto& betterRoute = proposal.better_route;
                const auto savedTime = proposal.saved_time;

                if (savedTime.has_value() && savedTime.value() > 60) // saved more than one minute
                {
                    navigationSession->acceptRouteProposal(proposal);
                }
            }
            else if (reason == tn::drive::models::v1::BetterRouteProposal::BetterRouteReason::ChargingStationUnavailable)
            {
                // refer to tutorial of navigation for electric vehicles
                // ...
            }
        }
    }
};

Customize better route trigger and update settings

Better route detector is a flexible module that can be configured on every trigger condition parameter. You can even make a customized better route controller to fully control the way to say if a route is better regarding how much time it saves.

All better route detection triggers are enabled by default, most of them can be turned off if you decide to handle rerouting for these cases on your own. However, it's strongly recommended to not turn off these triggers due to the internal arrangement on routing tasks. Once turned off, navigation session will lose the track of routing tasks for better route detection, it may affect user experience on finding better routes.

Customize better route detection for deviation

Rerouting for deviation is enable by default. Turn it off if you decided to handle deviation rerouting by yourself which is not recommended.

// Turn off routing for deviation
navigationSession->enableDeviationHandling(false);

Check vehicle status to see if it's deviated from the route in navigation when receiving navigation status updates.

void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
    // check if vehicle is deviated from current route in navigation
    const auto vehicleStatus = navStatus.vehicle_status;
    if (vehicleStatus == tn::drive::models::v1::NavStatus::VehicleStatus::Deviated)
    {
        // deviation status is given when vehicle has been deviated from current route in navigation
        // for same distance and time, to avoid to sensitive rerouting
        // there's no need to add extra delay processing for rerouting
        reroute(navStatus);
    }
}

void NavigationObserver::reroute(const tn::drive::models::v1::NavStatus& navStatus)
{
    // get current vehicle location
    const auto vehicleLocation = navStatus.vehicle_location;
    // get current vehicle bearing
    const auto vehicleBearing = navStatus.vehicle_bearing;
    // get current vehicle speed
    const auto vehicleSpeed = navStatus.vehicle_speed;

    // get forward waypoint index, started from zero
    const auto index = navStatus.forward_waypoint_index;

    // get current route
    const auto& route = navStatus.route;
    // get travel points from route
    const auto& travelPoints = route->travelPoints();

    // keep travel points not passed in the new routing request
    // travelPoints.front() is origin
    tn::direction::models::v2::GeoLocation origin(vehicleLocation.lat, vehicleLocation.lon);
    // travelPoints.back() is destination
    tn::direction::models::v2::GeoLocation destination(
        travelPoints.back()->location.display_point.lat,
        travelPoints.back()->location.display_point.lon);
    // all those in the middle are waypoints
    std::vector<tn::direction::models::v2::Waypoint> waypoints;
    if (index != tn::drive::models::v1::NavStatus::kIndexDestination)
    {
        // there are waypoints remaining
        auto iterBegin = travelPoints.begin();
        std::advance(iterBegin, index + 1);

        auto iterEnd = travelPoints.end();
        std::advance(iterEnd, -1);

        std::transform(iterBegin, iterEnd, std::back_inserter(waypoints), 
            [](const tn::shared_ptr<tn::direction::models::v2::TravelPoint>& point){
                const auto geoLocation = point->location();
                return tn::direction::models::v2::Waypoint(geoLocation.display_point.lat, geoLocation.display_point.lon);
            });
    }

    // get a request builder
    const auto builder = directionService->createRouteRequestBuilder();

    // get a request
    const auto request = builder->setOrigin(origin)
        .setDestination(destination)
        .setWaypoints(waypoints)
        .setHeading(vehicle_bearing)
        .setSpeed(vehicle_speed)
        .setRouteCount(1)
        .build();

    // calculate a route
    // NOTICE: route calculation mode should be aligned with current map mode
    // i.e. use onboard mode if current map mode is base mode, otherwise use offboard mode
    const auto task = directionService->createRouteCalculationTask(request, tn::direction::api::CalculationMode::Offboard);
    task->runAsync([](tn::foundation::ErrorCode errCode, tn::shared_ptr<tn::direction::api::RouteResponse> response){
        if (response && !response->routes.empty())
        {
            // stop current navigation session first
            driveSession->stopNavigation();

            // start a new session with new route
            tn::drive::api::NavigationSessionOptions options;
            options.enable_audio = true;
            options.enable_alert = true;
            options.enable_ADAS = true;
            driveSession->startNavigation(options, response->routes[0]);
        }
    });
}
Customize better route detection for traffic condition changes

Rerouting for traffic conditions changes is enabled by default. Turn it off if you decide to handle traffic conditions changes by yourself which is not recommended.

navigationSession->enableTrafficHandling(false);

Check along route traffic signal when receiving navigation status updates. Refer to tutorial of handling real-time along route traffic updates for more information.

If default handler for traffic conditions changes is enabled, it will be triggered in two cases: * there's blocking traffic incident or flow along the route * traffic delay is large enough

For the second case, there're two configuration items that can be adjusted.

// adjust traffic degradation percentage and remaining travel time to next travel point at the creation of drive session
auto settings = tn::foundation::Settings::Builder()
    // default value is 10% of estimated travel time to the next travel point, change it to 5% for example
    // if traffic delay to next travel point is no less than 5% of estimated travel time to next travel point
    // it may trigger a better route searching, depends on if following condition is satisfied or not
    .setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_BETTER_ROUTE_TRAFFIC_DEGRADATION_MIN_VALUE, "5")
    // default value is estimated travel time to the next travel point is at least 60 seconds, change it to 120 seconds for example
    // if estimated travel time to next travel when traffic condition changes is no less than 120 seconds
    // it will trigger a better route searching
    .setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_BETTER_ROUTE_TRIGGER_MIN_REMAINING_TIME, "120")
    .build();

// create a drive session options with navigation enabled
tn::drive::api::DriveSessionOptions options;
options.enable_navigation = true;

// customized better route detection settings will take effect in all navigation sessions created from this drive session
const auto driveSession = tn::drive::api::DriveSessionFactory::createDriveSession(
    options, system, settings, mapContent, directionService);

In above setting items, SETTING_NAVIGATION_BETTER_ROUTE_TRAFFIC_DEGRADATION_MIN_VALUE can be overwritten at runtime.

1
2
3
4
5
6
// overwrite traffic degradation percentage at runtime
// smaller percentage triggers better route searching on traffic condition change more frequently
navigationSession->setTrafficMinDegradation(3);

// if it's annoying, you can change the percentage to a large one to make it less happen
navigationSession->setTrafficMinDegradation(100);
Customize better route detection for timed restriction

Rerouting for timed restriction is enabled by default. Turn it off if you decide to handle rerouting in this case by yourself which is not recommended.

// turn off rerouting for timed restriction
navigationSession->enableTimedRestrictionHandling(false);

Check timed restriction detected signal when receiving navigation status updates. Refer to tutorial of handling timed restriction updates for more information.

Customize better route detection timer

Better route searching can be triggered by a timer with fixed interval for each route. Previously mentioned triggers for better route detection are all along route events, this one is different. Since navigation session provides information along the route, the changes around the route is not the focus of it. However, there may be cases that condition on roads nearby is better than those on current route. Timer is used for trying to get a better route.

Timer trigger is enabled by default as well. It's ok to turn it off if it's not needed.

// turn off timer trigger for better route detection
navigationSession->enableBetterRouteCheckTimer(false);

If it's enabled, the timer will count down for the interval set either via the setting item tn::drive::api::SettingConstants::SETTING_NAVIGATION_BETTER_ROUTE_CHECK_INTERVAL or via the API NavigationSession::setBetterRouteCheckInterval(). The interval set via the API will overwrite the one set by setting item.

// adjust better route detection timer interval at the creation of drive session
auto settings = tn::foundation::Settings::Builder()
    // default value is 20% of the initial estimated travel time to destination, it does not change during navigation
    // change it to a fixed interval 900 seconds for example
    .setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_BETTER_ROUTE_CHECK_INTERVAL, "900")
    .build();

// create a drive session options with navigation enabled
tn::drive::api::DriveSessionOptions options;
options.enable_navigation = true;

// customized better route detection settings will take effect in all navigation sessions created from this drive session
const auto driveSession = tn::drive::api::DriveSessionFactory::createDriveSession(
    options, system, settings, mapContent, directionService);

Overwrite the interval at runtime.

// overwrite the better route detection timer interval
navigationSession->setBetterRouteCheckInterval(1800);
Customize better route controller

Whether a route is better enough to be proposed is controlled by two things by default: * the estimated travel time to the point where the new route deviates from the current route in navigation is large enough for safety reasons * the estimated saved travel time to the next trave point is large enough

Above items can be adjusted at the creation of a drive session.

// adjust default better route controller parameters at the creation of drive session
auto settings = tn::foundation::Settings::Builder()
    // default value is estimated travel time the point new route deviates from current route is at least 60 seconds
    // change it to 120 seconds for example
    .setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_BETTER_ROUTE_LOGIC_MIN_TRAVEL_TIME, "120")
    // default value is estimated saved travel time is 10% of estimated travel time to the next travel point
    // change it to 5% for example
    .setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_BETTER_ROUTE_LOGIC_PERC_TIME_SAVED, "5")
    .build();

// create a drive session options with navigation enabled
tn::drive::api::DriveSessionOptions options;
options.enable_navigation = true;

// customized better route detection settings will take effect in all navigation sessions created from this drive session
const auto driveSession = tn::drive::api::DriveSessionFactory::createDriveSession(
    options, system, settings, mapContent, directionService);

In above setting items, SETTING_NAVIGATION_BETTER_ROUTE_LOGIC_PERC_TIME_SAVED can be overwritten at runtime.

1
2
3
4
5
6
7
// overwrite saved travel time percentage at runtime
// smaller percentage triggers better route proposal more frequently
// NOTICE: 0 is a special value which means any route is better than current one
navigationSession->setMinTimeSavedPercent(0);

// if it's annoying, you can change the percentage to a large one to make it less happen
navigationSession->setMinTimeSavedPercent(70);