Skip to content

Update User Location

Update user location

This tutorial shows how to update user location via drive session and get feedback from it.

Get started

Before updating user location to drive session, make sure GPS location is available, and it's recommended to use a dead reckoning module to get a high-precision location for some scenarios like tunnels.

Update location

Make a customized location observer.

1
2
3
4
5
6
7
8
class LocationObserver : public tn::drive::api::PositionEventListener
{
public:
    void onPositionIndicatorUpdated(const tn::drive::models::v1::Indicator& indicator) override
    {
        // ...
    }
};

Register the location observer to drive session.

const auto locationObserver = tn::make_shared<LocationObserver>();
driveSession->addPositionEventListener(locationObserver);

Unregister the location observer before shutdown drive session.

driveSession->removePositionEventListener(locationObserver);
driveSession->shutdown();

Construct a location input and feed it to the drive session. Location inputs are recommended to be provided at a frequency no greater than 15 Hz. Too frequent inputs will cause some of them to be ignored by the drive session. For some important location updates, you are recommended to set epoch_flag to true to ensure it will get handled in the drive session.

tn::drive::models::v1::VehicleLocation inputLocation;
// when epoch_flag is set to true, the location input will be handled in drive session
// for example a location input with high confidence
inputLocation.epoch_flag = true;

// elapsed time could be used as a key to match input and output locations
inputLocation.elapsed_time = std::chrono::duration<uint32_t, std::milli>(std::chrono::steady_clock::now() - getSystemStartTimePoint());

// bearing is strongly recommended to be set to increase map matching quality
inputLocation.bearing = getVehicleBearing();

// speed is strongly recommended to be set to increase map matching quality
inputLocation.speed = getVehicleSpeed();

// GPS location is must to have for an input location
inputLocation.coordinate = getVehicleGpsLocation();

driveSession->updateLocation(inputLocation);

Handle map matching feedback

Handle map matching feedback.

void LocationObserver::onPositionIndicatorUpdated(const tn::drive::models::v1::Indicator& indicator)
{
    // location information is always available
    const auto location = indicator.location.matched_location;
    showVehicleLocationOnMap(location);

    if (indicator.matched_road.has_value())
    {
        const auto& roadInfo = indicator.matched_road.value();
        showSpeedLimit(roadInfo.speed_limit);

        // show combined road name if there are multiple names for the road
        showRoadNames(roadInfo.combined_name);

        // show the best name for the road even if there are multiple names for the road
        const auto roadName = parseRoadName(roadInfo.road_name);
        showRoadName(roadName);
    }

    if (indicator.regional.has_value())
    {
        const auto& regionalInfo = indicator.regional.value();
        showLocalTime(regionalInfo.time_zone);
    }

    // use map matching feedback to adjust dead reckoning locations
    if (indicator.feedback.has_value())
    {
        const auto& feedback = indicator.feedback.value();
        if (feedback.on_road)
        {
            // handle fields valid only on road, including:
            // oneway, right_hand, ferry, in_service_area, in_parking_lot, in_tunnel, 
            // lane_count, dist_from_bifurcation_behind, dist_to_bifurcation_ahead
            // NOTICE: these fields may not be related to matched road

            // omit code here ...
        }

        // feedback location may be different from map matched location
        const auto location = feedback.location;

        // use timestamp to match location input and output
        const auto elapsed_time = feedback.timestamp;
    }
}

Parse name string for road names

Name string is an encoded format to represent various names in one unified format. To decode the string, check the string format and use corresponding utility functions to get the real name.

std::string parseRoadName(const tn::mapcontent::models::v1::NameString& name)
{
    std::string parsedName;

    switch (name.format)
    {
        case tn::mapcontent::models::v1::NameString::Format::Name:
        case tn::mapcontent::models::v1::NameString::Format::ExitName:
        case tn::mapcontent::models::v1::NameString::Format::ExitNumber:
        {
            const auto ordinaryName = tn::mapcontent::models::v1::NameString::Utility::asOrdinaryName(name);
            parsedName = (ordinaryName.has_value() ? ordinaryName->orthography.content : "");
            break;
        }
        case tn::mapcontent::models::v1::NameString::Format::RoadNumber:
        {
            const auto roadNumber = tn::mapcontent::models::v1::NameString::Utility::asRoadNumberName(name);
            parsedName = (roadNumber.has_value() ? roadNumber->ordinary.orthography.content : "");
            break;
        }
        default:
            break;
    }

    return parsedName;
}

Next steps