Skip to content

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.

1
2
3
4
5
6
7
8
// 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.

1
2
3
4
5
// remove listener
navigationService->removeNavigationListener(listener);

// stop navigation session
navigationService->stopNavigation();

Handle basic information updates

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 ...
}

Handle travel information updates

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