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.
| // 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.
| // 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);
|