Turn By Turn Navigation
Turn-by-turn navigation
This tutorial shows how to handle navigation status updates for turn-by-turn navigation experience.
Get started
Based on the route in navigation and location information, the navigation session provides vehicle current status,
properties of the road where the vehicle is, dynamic predictions and statistics, and along route traffic information.
Following table shows the basic information for turn-by-turn navigation.
Information Type |
Content |
vehicle status |
- whether it's on route or not
- map matched location
- bearing
- speed
|
road properties |
- driving side
- speed limit
- time zone
- country code
- road types
- road names
- unique ID of road segment
|
on route information |
- indices to locate vehicle on route
- distance to forward turn
- whether next turn is a tight turn
|
travel information |
- forward waypoint
- estimated travel time to forward waypoints and destination
- traveled distance and time in current navigation session
|
POI related information |
- whether in a service area
- whether in a parking lot
- whether nearby a planned charging station (EV only)
|
navigation events |
- turn list update
- along route traffic information update
- arrival notification for waypoint and destination
- approaching and leaving a junction
- timed restrictions detected
- planned charging station not reachable (EV only)
|
Make a customized navigation listener to handle navigation status updates.
| class NavigationObserver : public tn::drive::api::NavigationEventListener
{
public:
void onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus) override
{
// ...
}
// omit other methods
};
|
Register the listener after creation of the navigation service, before starting navigation session.
| // make a listener
const auto listener = tn::make_shared<NavigationObserver>();
// register the listener
navigationService->addNavigationListener(listener);
// start navigation
auto* navigationSession = navigationService->startNavigation(route);
|
Stop navigation and remove the listener when it's not needed anymore.
| // remove listener
navigationService->removeNavigationListener(listener);
// stop navigation session
navigationService->stopNavigation();
|
Basic navigation information includes vehicle status, road properties, administrative information, POI information and on-route location.
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
// vehicle status is always updated
// vehicle location is always map matched location
const auto vehicleLocation = navStatus.vehicle_location;
// vehicle bearing is always the same as it is in position indicator update
const auto vehicleBearing = navStatus.vehicle_bearing;
// vehicle speed is always the same as it is in position indicator update
const auto vehicleSpeed = navStatus.vehicle_speed;
// get on-route information when vehicle is on route
const auto vehicleStatus = navStatus.vehicle_status;
if (vehicleStatus == tn::drive::models::v1::NavStatus::VehicleStatus::OnRoute)
{
// current route in navigation, may be different from initial route used to start navigation
// see better route detection tutorial for details
const auto route = navStatus.route;
// indices to locate vehicle along the route quickly
// could be used to get leg, step and edge from route
const auto legIndex = navStatus.current_leg_index;
const auto stepIndex = navStatus.current_step_index;
const auto edgeIndex = navStatus.current_edge_index;
const auto edgePointIndex = navStatus.current_edge_point_index;
const auto edgePointRange = navStatus.current_edge_point_range;
// distance along the route to forward maneuver
const auto distanceToTurn = navStatus.distance_to_turn;
}
// get road properties when vehicle is not off-road
if (vehicleStatus != tn::drive::models::v1::NavStatus::VehicleStatus::OffRoad)
{
// following road properties is always the same as they are in position indicator update
// see tn::drive::models::v1::Indicator::matched_road field
const auto roadType = navStatus.road_type;
const auto roadSubType = navStatus.road_subtype;
const auto drivingSide = navStatus.driving_side;
const auto speedLimit = navStatus.speed_limit;
const auto edgeId = navStatus.current_edge_id;
const auto roadName = navStatus.best_name;
const auto combinedRoadName = navStatus.combined_name; // "|" concatenated names
// following administrative information is always the same as they are in position indicator update
// see tn::drive::models::v1::Indicator::regional field
const auto timeZone = navStatus.time_zone_info;
const auto countryCode = navStatus.country_code;
// following POI related information is always the same as they are in position indicator update
// see tn::drive::models::v1::Indicator::feedback field
const auto isInServiceArea = navStatus.in_service_area;
const auto isInParkingLot = navStatus.in_parking_lot;
}
// omit other fields handling ...
}
|
Travel information in navigation status updates include estimated travel and arrival time for remaining waypoints and destination.
Actual traveled time and distance in the navigation session is also part of the travel information.
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
// Forward waypoint is the first one in the remaining waypoints
// that a corresponding arrival notification has not been triggered yet.
// For destination, index can always be got from this value
// regardless of the arrival notification has been sent or not.
const auto index = navStatus.forward_waypoint_index;
// get travel estimation for all remaining waypoints and destination
const auto& estimations = navStatus.travel_estimations;
// get estimated remaining distance and time to forward waypoint / destination
const auto& estimation = estimations.back();
const auto distance = estimation.distance;
// remaining travel time takes traffic impact into account
const auto time = estimation.time;
// remaining travel time takes no traffic impact into account
const auto time_no_traffic = estimation.time_no_traffic;
// traffic impact could be negative if live or historical traffic speed is greater than speed limit
const auto traffic_impact = estimation.time - estimation.time_no_traffic;
// show estimated arrival time in local time zone
// this the time zone of the forward waypoint or destination,
// may be different from the time zone of the location where vehicle is located
const auto timeZone = estimation.time_zone_info;
// local time of arrival
const auto arrivalTime = estimate.arrival_time;
// if the travel estimation level is at least at the step level
const auto& stepEstimations = navStatus.step_travel_estimations;
// if travel estimation level is at the edge level
for (const auto& est : stepEstimations)
{
// get edge travel estimations for each step
const auto& edgeEstimations = est.edge_travel_estimations;
}
// the total traveled distance and time are tracked for the lifetime of the navigation session, not just for a specific route
// the traveled distance is updated only if the vehicle status is on route
const auto traveledDistance = navStatus.traveled_distance;
// traveled time always gets updated
const auto traveledTime = navStatus.traveled_time;
}
|
Handle navigation events
Basic and travel information in navigation status updates are provided continuously during the whole life of the navigation session.
While navigation events are all one-shot signals triggered by corresponding conditions on the road.
Handle turn list updates
During navigation, navigation service provides next road names in turn order to guide user in the right direction.
The conditions to trigger a turn list update are:
* the route in navigation changed
* guidance information for the route in navigation updated
* the TA SDK foundation system locale changed
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
const auto& turnListSignal = navStatus.update_turn_by_turn_list_signal;
if (turnListSignal)
{
const auto& turnList = turnListSignal->turn_by_turn_list;
for (const auto& turn : turnList)
{
// turn index started from zero
// turns are sorted by index in the ascending order
// NOTICE: the number of turns may change in the case of guidance information update
const auto index = turn.step_index;
// best name for next road
const auto& nextRoadName = turn.next_road_name;
// turn actions to determine the action icon used for the turn
const auto turnAction = turn.turn_action;
const auto turnAssistantAction = turn.assistant_action;
// for a tight turn, i.e. a turn on a step that is too short
// a special turn action icon is preferred to indicate the turn needs preparation in advance
const auto isTightTurn = turn.is_tight_turn;
// a road may have multiple shield icons
for (const auto& shield : turn.shield_info)
{
// render shield icon (bitmap in ARGB32 format)
const auto image = navigationService->renderIcon(60, 48, shield.shield_icon_context);
}
// if there's a signpost on the turn, branch name may be available
std::string branchName;
if (turnListSignal.branch_name.has_value())
{
branchName = parseRoadName(turnListSignal.branch_name.value());
}
// if there's a signpost on the turn, towards name may be available
std::string towardsName;
if (turnListSignal.towards_name.has_value())
{
towardsName = parseRoadName(turnListSignal.towards_name.value());
}
// check whether the best name for next road comes from branch or towards names
bool isBestNameFromBranch = (branchName.empty() == false && (nextRoadName == branchName));
bool isBestNameFromTowards = (towardsName.empty() == false && (nextRoadName == towardsName));
// add one turn to show in turn list on HMI
}
}
}
|
Refer to parse name string for road names
for how to handle the road name strings.
Handle arrival notifications
Arrival notifications are given in advance before the vehicle actually hits the arrival point on the route.
This is intentionally done to allow some time for audio guidance prompts to be played.
There are two kinds of arrival notifications for waypoints and destinations:
* regular arrival
* off-road arrival (e.g., parking lot area)
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
const auto& arrivalSignal = navStatus.reach_waypoint_signal;
if (arrivalSignal)
{
const auto index = arrivalSignal->waypoint_index;
const auto position = arrivalSignal->waypoint_position;
// show notification on arrival of the (index)th waypoint and the position along route
}
}
|
For regular arrival mode, the default distance before arrival to give the notification is:
* 100 meters if arrival point is on a highway road
* 60 meters otherwise
The distances to notify arrival can be configured in the creation time of a navigation service.
| // arrival signals are generated before vehicle actually hits the waypoint location for the reason of leaving some time for
// audio guidance to play the sentence.
// the distance can be configured at navigation service creation time
auto settings = tn::foundation::Settings::Builder()
.setString(tn::drive::api::SettingConstants::SETTING_WAYPOINT_LOCAL_ARRIVAL_DISTANCE, "40")
.setString(tn::drive::api::SettingConstants::SETTING_WAYPOINT_HIGHWAY_ARRIVAL_DISTANCE, "120")
.build();
// create a navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;
// customized arrival signal trigger distance 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);
|
Handle real-time along route traffic updates
Along route traffic updates are triggered when:
* route in navigation changed and the route object used for navigation has traffic flow information
* traffic flow or incident changes along the route during periodically querying
Traffic condition changes may trigger better route detection during navigation. This is enabled by default.
If you want to handle traffic conditions for better route searching by yourself, you can turn it off.
It's strongly recommended that if you decide to do it on your own, blocking traffic flows and incidents should trigger better route detection.
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
const auto& trafficSignal = navStatus.along_route_traffic_signal;
if (trafficSignal)
{
const auto& flows = trafficSignal->flows;
for (const auto& flow : flows)
{
// along route locations for traffic flow
const auto& beginLocation = flow.begin_location;
const auto& endLocation = flow.end_location;
// the flow is shared for the part of the route between beginLocation and endLocation
const auto flowLevel = flowInfo.lowest_level;
const auto averageSpeed = flowInfo.average_speed;
if ((flowLevel == tn::traffic::models::v2::TrafficFlow::Level::Closed) ||
(std::fabs(averageSpeed - 0.0f) < std::numeric_limits<float>::epsilon()>))
{
// this is a blocking traffic flow
// should rerouting if default better route detection for traffic condition changes is disabled
}
// update flow along route
}
const auto& incidents = trafficSignal->incidents;
for (const auto& incident : incidents)
{
// along route location for traffic incident
const auto& incidentLocation = incident.location;
// estimated traffic delay time caused by incident
const auto delayTime = incident.delay_time;
// traffic incident information
const auto eventCode = incident.incident.event_code;
const auto incidentType = incident.incident.type;
const auto isBlockingIncident = incident.incident.is_blocking;
if (isBlockingIncident)
{
// this is a blocking traffic incident
// should rerouting if default better route detection for traffic condition changes is disabled
}
// update incident along route
}
}
}
|
Periodically querying of traffic along route has a range configured at the creation of a navigation service.
By default, traffic along route that may affect travel estimation within one hour will be queried.
You can adjust it using the following parameter.
| // customize traffic fetch range
const auto settings = tn::foundation::Settings::Builder()
.setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_TRAFFIC_FETCH_RANGE, "1800")
.build();
// create a navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;
// customized traffic query range 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);
|
Handle junction updates
Junction updates are triggered when:
* vehicle is approaching the junction
* vehicle is the progress of passing the junction
* vehicle has left the junction
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
const auto& junctionSignal = navStatus.update_junction_view_signal;
if (junctionSignal)
{
const auto status = junctionSignal->status;
if (status == tn::drive::models::v1::NavigationSignalUpdateJunctionView::JunctionViewStatus::Entered ||
status == tn::drive::models::v1::NavigationSignalUpdateJunctionView::JunctionViewStatus::InTransition)
{
const auto& route = navStatus->route();
const auto legIndex = junctionSignal->getLegIndex();
const auto stepIndex = junctionSignal->getStepIndex();
// render junction view and signboard images in ARGB32 format
// signboard image is same as it is in the junction view image
// you could choose to show either image on HMI or both
const auto signboard = navigationService->renderSignboard(route, legIndex, stepIndex, 260, 100);
const auto junctionView = navigationService->renderJunctionView(route, legIndex, stepIndex, 400, 480);
}
else
{
// dismiss the previously shown junction view image
}
}
}
|
The approaching junction notification is sent before the vehicle actually hits the junction location to leave some time for the user to take action.
The default distance before hitting the junction to give the notification is:
* 700 meters if the junction point is on a highway road
* 400 meters otherwise
The distances to notify approaching junctions can be configured in the creation time of a navigation service.
| // junction signals are generated before vehicle actually hits the junction location for the reason of leaving some time for
// drive to take actions.
// the distance can be configured at navigation service creation time
auto settings = tn::foundation::Settings::Builder()
.setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_HIGHWAY_DISPLAY_DISTANCE, "1000")
.setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_JUNCTION_LOCAL_STREET_DISPLAY_DISTANCE, "250")
.build();
// create a navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;
// customized junction approaching notification distances 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);
|
Handle timed restriction updates
Timed restriction updates are triggered when navigation session detects that there's a timed restriction zone ahead of current vehicle location
along the route and it's estimated that vehicle will be in the zone during restriction effective period.
Timed restriction detected will trigger better route detection during navigation. This is enabled by default.
If you want to handle timed restriction for better route searching by yourself, you can turn it off.
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
const auto& restrictionSignal = navStatus.timed_restriction_signal;
if (restrictionSignal)
{
// timed restriction affected edged on the route
// NOTICE: only the first restriction along the route will be notified in this signal
const auto& edges = restrictionSignal->timed_restriction_edges;
// show timed restriction affected edges along the route
}
}
|
The detection of timed restriction zone is done periodically within a detection distance.
The default detection distance is 50 kilometers.
It can be configured at the creation of a navigation service.
| // timed restriction signals are generated if it is estimated that vehicle may be on a road that will have timed restrictions
// effective in the future.
// the distance can be configured at service creation time
auto settings = tn::foundation::Settings::Builder()
.setString(tn::drive::api::SettingConstants::SETTING_NAVIGATION_TIMED_RESTRICTION_DETECT_RANGE, "10000")
.build();
// create a navigation service options with navigation enabled
tn::drive::api::NavigationServiceOptions options;
options.enable_navigation = true;
// customized timed restriction detection distance 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);
|
Handle planned charging station not reachable warnings (EV only)
Planned charging station unreachable warnings are triggered when it's estimated that the remaining battery is insufficient to make the
vehicle arrive at the incoming planned charging station.
Refer to the tutorial of navigation for electric vehicles for more information.
Handle departure notifications
Departure notifications are given when the vehicle actually leave the waypoint on the route.
There are three kinds of departure notifications for waypoints:
- regular departute
- off-route departure (e.g., service area)
- off-road departute (e.g., parking lot area)
| void NavigationObserver::onNavigationStatusUpdated(const tn::drive::models::v1::NavStatus& navStatus)
{
const auto& departSignal = navStatus.depart_waypoint_signal;
if (departSignal)
{
const auto index = departSignal->waypoint_index;
// show notification on departure of the (index)th waypoint along route
}
}
|
Next steps