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::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 time restriction 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.index.leg_index;
                    const auto stepIndex = edge.index.step_index;
                    const auto edgeIndex = edge.index.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
            // ...
        }
        else if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::AvoidBlockingTraffic)
        {
            // get avoided blocking traffic condition
            const auto& betterRoute = proposal.better_route;
            const auto& incidents = proposal.blocking_incidents;
            const auto& flows = proposal.blocking_flows;

            // get the current route that incidents affect
            const auto& currentRoute = proposal.current_route;

            if (!incidents.empty())
            {  
                for (const auto& incident : incidents)
                {
                    // along route location of the incident
                    // it's based on the current route, not better route after avoiding
                    const auto& location = incident.location;
                }
            }

            if (!flows.empty())
            {
                for (const auto& flow : flows)
                {
                    // along route location of the flow
                    // it's based on the current route, not better route after avoiding
                    const auto& beginLocation = flow.begin_location;
                    const auto& endLocation = flow.end_location;
                }
            }
        }
        else if (reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::ResumeEvTripPlan ||
                 reason == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateReason::UpdateEvTripPlan)
        {
            if (status == tn::drive::models::v1::BetterRouteUpdateProgress::RouteUpdateStatus::Succeeded)
            {
                // 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;
        navigationService->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 navigation service 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), refer to tutorial of navigation for electric vehicles for more information
  • there're blocking traffic incidents or flows ahead
  • works for passager vehicles when client starting navigation on a route with avoid traffic closure option set to FALSE
  • works for commercial vehicles
  • the arrival battery to the destination has dropped to a certain low level (EV only), refer to tutorial of navigation for electric vehicles for more information
  • timed restriction detected ahead for commercial vehicles, refer to tutorial of timed restriction detection for more information
// 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, [this](
                        const tn::drive::models::v1::BetterRouteProposalFeedback& feedback){
                        this->onProposalFeedbackProgressed(feedback);
                    });
                }
            }
            else if (reason == tn::drive::models::v1::BetterRouteProposal::BetterRouteReason::AvoidBlockingTraffic)
            {
                // get avoided blocking traffic condition
                const auto& betterRoute = proposal.better_route;
                const auto& incidents = proposal.blocking_incidents;
                const auto& flows = proposal.blocking_flows;

                // get the current route that incidents affect
                const auto& currentRoute = proposal.current_route;

                if (!incidents.empty())
                {  
                    for (const auto& incident : incidents)
                    {
                        // along route location of the incident
                        // it's based on the current route, not better route after avoiding
                        const auto& location = incident.location;
                    }
                }

                if (!flows.empty())
                {
                    for (const auto& flow : flows)
                    {
                        // along route location of the flow
                        // it's based on the current route, not better route after avoiding
                        const auto& beginLocation = flow.begin_location;
                        const auto& endLocation = flow.end_location;
                    }
                }
            }
            else if (reason == tn::drive::models::v1::BetterRouteProposal::BetterRouteReason::ChargingStationUnavailable)
            {
                // refer to tutorial of navigation for electric vehicles
                // ...
            }
            else if (reason == tn::drive::models::v1::BetterRouteProposal::BetterRouteReason::LowArrivalBatteryLevel)
            {
                // refer to tutorial of navigation for electric vehicles
                // ...
            }
            else if (reason == tn::drive::models::v1::BetterRouteProposal::BetterRouteReason::AvoidTimedRestriction)
            {
                const auto& betterRoute = proposal.better_route;
                const auto& timed_restriction_edges = proposal.time_restriction_edges;

                // get the current route that time restriction affect
                const auto& currentRoute = proposal.current_route;

                // get the current route that time restriction affect
                for (const auto& edge : timed_restriction_edges)
                {
                    // along route indices of the edge affected by the timed restriction
                    // it's based on the current route
                    const auto legIndex = edge.index.leg_index;
                    const auto stepIndex = edge.index.step_index;
                    const auto edgeIndex = edge.index.edge_index;

                    // check restrictions on current route for commercial vehicles
                    for (const auto& restriction: edge.truck_restrictions)
                    {
                        const auto reason = restriction.reason;
                        const auto value = restriction.reference_value;

                        // If restriction reason is height, width or length, the reference value unit is centimeter.
                        // If restriction reason is weight, weight per axle or gross vehicle mass, the reference value unit is kilogram.
                        // If restriction reason is hazard material or tunnel, the reference value is hazard type @see tn::mapcontent::models::v1::HazardType
                        // If restriction reason is amount of axles, the reference value is the amount condition @see tn::mapcontent::models::v1::AmountOfAxles
                        // If restriction reason is amount of trailer count, the reference value is the amount condition @see tn::mapcontent::models::v1::AmountOfTrailers
                        // If the reference value is -1, it means there's no such value for the restriction reason
                    }
                }
            }
        }
    }

private:
    // make a customized better route proposal feedback function to handle feedback result
    void onProposalFeedbackProgressed(const tn::drive::models::v1::BetterRouteProposalFeedback& feedback)
    {
        if (feedback.status == tn::drive::models::v1::BetterRouteProposalFeedback::FeedbackStatus::Succeeded &&
            feedback.proposal.better_route)
        {
            // do something to the route
        }
    }
};

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
            navigationService->stopNavigation();

            // start a new session with new route
            navigationService->startNavigation(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 navigation service
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 navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;

// customized better route detection settings will take effect in all navigation sessions created from this navigation service
const auto navigationService = tn::drive::api::NavigationServiceFactory::createNavigationService(
    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 navigation service
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 navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;

// customized better route detection settings will take effect in all navigation sessions created from this navigation service
const auto navigationService = tn::drive::api::NavigationServiceFactory::createNavigationService(
    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 navigation service.

// adjust default better route controller parameters at the creation of navigation service
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 navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;

// customized better route detection settings will take effect in all navigation sessions created from this navigation service
const auto navigationService = tn::drive::api::NavigationServiceFactory::createNavigationService(
    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);