Currently route results are annotated with additional path information, such as geometries, turn-by-turn steps and other metadata. These annotations are generated if they are not requested or returned in the response. Datasets needed to generate these annotations are loaded and available to the OSRM process even when unused. This commit is a first step towards making the loading of these datasets optional. We refactor the code so that route annotations are only generated if explicitly requested and needed in the response. Specifically, we change the following annotations to be lazily generated: - Turn-by-turn steps - Route Overview geometry - Route segment metadata For example. a /route/v1 request with steps=false&overview=false&annotations=false would no longer call the following data facade methods: - GetOSMNodeIDOfNode - GetTurnInstructionForEdgeID - GetNameIndex - GetNameForID - GetRefForID - GetTurnInstructionForEdgeID - GetClassData - IsLeftHandDriving - GetTravelMode - IsSegregated - PreTurnBearing - PostTurnBearing - HasLaneData - GetLaneData - GetEntryClass Requests that include segment metadata and/or overview geometry but not turn-by-turn instructions will also benefit from this, although there is some interdependency with the step instructions - a call to GetTurnInstructionForEdgeID is still required. Requests for OSM annotations will understandably still need to call GetOSMNodeIDOfNode. Making these changes unlocks the optional loading of data contained in the following OSRM files: - osrm.names - osrm.icd - osrm.nbg_nodes (partial) - osrm.ebg_nodes (partial) - osrm.edges
1008 lines
45 KiB
C++
1008 lines
45 KiB
C++
#ifndef ENGINE_API_ROUTE_HPP
|
|
#define ENGINE_API_ROUTE_HPP
|
|
|
|
#include "extractor/maneuver_override.hpp"
|
|
#include "engine/api/base_api.hpp"
|
|
#include "engine/api/base_result.hpp"
|
|
#include "engine/api/json_factory.hpp"
|
|
#include "engine/api/route_parameters.hpp"
|
|
|
|
#include "engine/datafacade/datafacade_base.hpp"
|
|
|
|
#include "engine/guidance/assemble_geometry.hpp"
|
|
#include "engine/guidance/assemble_leg.hpp"
|
|
#include "engine/guidance/assemble_overview.hpp"
|
|
#include "engine/guidance/assemble_route.hpp"
|
|
#include "engine/guidance/assemble_steps.hpp"
|
|
#include "engine/guidance/collapse_turns.hpp"
|
|
#include "engine/guidance/lane_processing.hpp"
|
|
#include "engine/guidance/post_processing.hpp"
|
|
#include "engine/guidance/verbosity_reduction.hpp"
|
|
|
|
#include "engine/internal_route_result.hpp"
|
|
|
|
#include "guidance/turn_instruction.hpp"
|
|
|
|
#include "util/coordinate.hpp"
|
|
#include "util/integer_range.hpp"
|
|
#include "util/json_util.hpp"
|
|
|
|
#include <iterator>
|
|
#include <vector>
|
|
|
|
namespace osrm
|
|
{
|
|
namespace engine
|
|
{
|
|
namespace api
|
|
{
|
|
|
|
class RouteAPI : public BaseAPI
|
|
{
|
|
public:
|
|
RouteAPI(const datafacade::BaseDataFacade &facade_, const RouteParameters ¶meters_)
|
|
: BaseAPI(facade_, parameters_), parameters(parameters_)
|
|
{
|
|
}
|
|
|
|
void
|
|
MakeResponse(const InternalManyRoutesResult &raw_routes,
|
|
const std::vector<PhantomNodes>
|
|
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
|
|
osrm::engine::api::ResultT &response) const
|
|
{
|
|
BOOST_ASSERT(!raw_routes.routes.empty());
|
|
|
|
if (response.is<flatbuffers::FlatBufferBuilder>())
|
|
{
|
|
auto &fb_result = response.get<flatbuffers::FlatBufferBuilder>();
|
|
MakeResponse(raw_routes, all_start_end_points, fb_result);
|
|
}
|
|
else
|
|
{
|
|
auto &json_result = response.get<util::json::Object>();
|
|
MakeResponse(raw_routes, all_start_end_points, json_result);
|
|
}
|
|
}
|
|
|
|
void
|
|
MakeResponse(const InternalManyRoutesResult &raw_routes,
|
|
const std::vector<PhantomNodes>
|
|
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
|
|
flatbuffers::FlatBufferBuilder &fb_result) const
|
|
{
|
|
|
|
auto data_timestamp = facade.GetTimestamp();
|
|
flatbuffers::Offset<flatbuffers::String> data_version_string;
|
|
if (!data_timestamp.empty())
|
|
{
|
|
data_version_string = fb_result.CreateString(data_timestamp);
|
|
}
|
|
|
|
auto response =
|
|
MakeFBResponse(raw_routes, fb_result, [this, &all_start_end_points, &fb_result]() {
|
|
return BaseAPI::MakeWaypoints(&fb_result, all_start_end_points);
|
|
});
|
|
|
|
if (!data_timestamp.empty())
|
|
{
|
|
response->add_data_version(data_version_string);
|
|
}
|
|
fb_result.Finish(response->Finish());
|
|
}
|
|
|
|
void
|
|
MakeResponse(const InternalManyRoutesResult &raw_routes,
|
|
const std::vector<PhantomNodes>
|
|
&all_start_end_points, // all used coordinates, ignoring waypoints= parameter
|
|
util::json::Object &response) const
|
|
{
|
|
util::json::Array jsRoutes;
|
|
|
|
for (const auto &route : raw_routes.routes)
|
|
{
|
|
if (!route.is_valid())
|
|
continue;
|
|
|
|
jsRoutes.values.push_back(MakeRoute(route.segment_end_coordinates,
|
|
route.unpacked_path_segments,
|
|
route.source_traversed_in_reverse,
|
|
route.target_traversed_in_reverse));
|
|
}
|
|
|
|
if (!parameters.skip_waypoints)
|
|
{
|
|
response.values["waypoints"] = BaseAPI::MakeWaypoints(all_start_end_points);
|
|
}
|
|
response.values["routes"] = std::move(jsRoutes);
|
|
response.values["code"] = "Ok";
|
|
auto data_timestamp = facade.GetTimestamp();
|
|
if (!data_timestamp.empty())
|
|
{
|
|
response.values["data_version"] = data_timestamp;
|
|
}
|
|
}
|
|
|
|
protected:
|
|
template <typename GetWptsFn>
|
|
std::unique_ptr<fbresult::FBResultBuilder>
|
|
MakeFBResponse(const InternalManyRoutesResult &raw_routes,
|
|
flatbuffers::FlatBufferBuilder &fb_result,
|
|
GetWptsFn getWaypoints) const
|
|
{
|
|
|
|
std::vector<flatbuffers::Offset<fbresult::RouteObject>> routes;
|
|
for (const auto &raw_route : raw_routes.routes)
|
|
{
|
|
if (!raw_route.is_valid())
|
|
continue;
|
|
|
|
routes.push_back(MakeRoute(fb_result,
|
|
raw_route.segment_end_coordinates,
|
|
raw_route.unpacked_path_segments,
|
|
raw_route.source_traversed_in_reverse,
|
|
raw_route.target_traversed_in_reverse));
|
|
}
|
|
|
|
auto routes_vector = fb_result.CreateVector(routes);
|
|
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbresult::Waypoint>>>
|
|
waypoints_vector;
|
|
if (!parameters.skip_waypoints)
|
|
{
|
|
waypoints_vector = getWaypoints();
|
|
}
|
|
|
|
auto response = std::make_unique<fbresult::FBResultBuilder>(fb_result);
|
|
response->add_routes(routes_vector);
|
|
response->add_waypoints(waypoints_vector);
|
|
|
|
return response;
|
|
}
|
|
|
|
template <typename ForwardIter>
|
|
mapbox::util::variant<flatbuffers::Offset<flatbuffers::String>,
|
|
flatbuffers::Offset<flatbuffers::Vector<const fbresult::Position *>>>
|
|
MakeGeometry(flatbuffers::FlatBufferBuilder &builder, ForwardIter begin, ForwardIter end) const
|
|
{
|
|
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
|
|
{
|
|
return builder.CreateString(encodePolyline<100000>(begin, end));
|
|
}
|
|
else if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
|
|
{
|
|
return builder.CreateString(encodePolyline<1000000>(begin, end));
|
|
}
|
|
std::vector<fbresult::Position> coordinates;
|
|
coordinates.resize(std::distance(begin, end));
|
|
std::transform(begin, end, coordinates.begin(), [](const Coordinate &c) {
|
|
return fbresult::Position{static_cast<float>(util::toFloating(c.lon).__value),
|
|
static_cast<float>(util::toFloating(c.lat).__value)};
|
|
});
|
|
return builder.CreateVectorOfStructs(coordinates);
|
|
}
|
|
|
|
boost::optional<util::json::Value>
|
|
MakeGeometry(boost::optional<std::vector<Coordinate>> &&annotations) const
|
|
{
|
|
boost::optional<util::json::Value> json_geometry;
|
|
if (annotations)
|
|
{
|
|
auto begin = annotations->begin();
|
|
auto end = annotations->end();
|
|
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
|
|
{
|
|
json_geometry = json::makePolyline<100000>(begin, end);
|
|
}
|
|
else if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
|
|
{
|
|
json_geometry = json::makePolyline<1000000>(begin, end);
|
|
}
|
|
else
|
|
{
|
|
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
|
|
json_geometry = json::makeGeoJSONGeometry(begin, end);
|
|
}
|
|
}
|
|
return json_geometry;
|
|
}
|
|
|
|
template <typename ValueType, typename GetFn>
|
|
flatbuffers::Offset<flatbuffers::Vector<ValueType>> GetAnnotations(
|
|
flatbuffers::FlatBufferBuilder &fb_result, guidance::LegGeometry &leg, GetFn Get) const
|
|
{
|
|
std::vector<ValueType> annotations_store;
|
|
annotations_store.reserve(leg.annotations.size());
|
|
|
|
for (const auto &step : leg.annotations)
|
|
{
|
|
annotations_store.push_back(Get(step));
|
|
}
|
|
|
|
return fb_result.CreateVector(annotations_store);
|
|
}
|
|
|
|
template <typename GetFn>
|
|
util::json::Array GetAnnotations(const guidance::LegGeometry &leg, GetFn Get) const
|
|
{
|
|
util::json::Array annotations_store;
|
|
annotations_store.values.reserve(leg.annotations.size());
|
|
|
|
for (const auto &step : leg.annotations)
|
|
{
|
|
annotations_store.values.push_back(Get(step));
|
|
}
|
|
|
|
return annotations_store;
|
|
}
|
|
|
|
fbresult::ManeuverType WaypointTypeToFB(guidance::WaypointType type) const
|
|
{
|
|
switch (type)
|
|
{
|
|
case guidance::WaypointType::Arrive:
|
|
return fbresult::ManeuverType_Arrive;
|
|
case guidance::WaypointType::Depart:
|
|
return fbresult::ManeuverType_Depart;
|
|
default:
|
|
return fbresult::ManeuverType_Notification;
|
|
}
|
|
}
|
|
|
|
fbresult::ManeuverType TurnTypeToFB(osrm::guidance::TurnType::Enum turn) const
|
|
{
|
|
static std::map<osrm::guidance::TurnType::Enum, fbresult::ManeuverType> mappings = {
|
|
{osrm::guidance::TurnType::Invalid, fbresult::ManeuverType_Notification},
|
|
{osrm::guidance::TurnType::NewName, fbresult::ManeuverType_NewName},
|
|
{osrm::guidance::TurnType::Continue, fbresult::ManeuverType_Continue},
|
|
{osrm::guidance::TurnType::Turn, fbresult::ManeuverType_Turn},
|
|
{osrm::guidance::TurnType::Merge, fbresult::ManeuverType_Merge},
|
|
{osrm::guidance::TurnType::OnRamp, fbresult::ManeuverType_OnRamp},
|
|
{osrm::guidance::TurnType::OffRamp, fbresult::ManeuverType_OffRamp},
|
|
{osrm::guidance::TurnType::Fork, fbresult::ManeuverType_Fork},
|
|
{osrm::guidance::TurnType::EndOfRoad, fbresult::ManeuverType_EndOfRoad},
|
|
{osrm::guidance::TurnType::Notification, fbresult::ManeuverType_Notification},
|
|
{osrm::guidance::TurnType::EnterRoundabout, fbresult::ManeuverType_Roundabout},
|
|
{osrm::guidance::TurnType::EnterAndExitRoundabout,
|
|
fbresult::ManeuverType_ExitRoundabout},
|
|
{osrm::guidance::TurnType::EnterRotary, fbresult::ManeuverType_Rotary},
|
|
{osrm::guidance::TurnType::EnterAndExitRotary, fbresult::ManeuverType_ExitRotary},
|
|
{osrm::guidance::TurnType::EnterRoundaboutIntersection,
|
|
fbresult::ManeuverType_Roundabout},
|
|
{osrm::guidance::TurnType::EnterAndExitRoundaboutIntersection,
|
|
fbresult::ManeuverType_ExitRoundabout},
|
|
{osrm::guidance::TurnType::NoTurn, fbresult::ManeuverType_Notification},
|
|
{osrm::guidance::TurnType::Suppressed, fbresult::ManeuverType_Notification},
|
|
{osrm::guidance::TurnType::EnterRoundaboutAtExit, fbresult::ManeuverType_Roundabout},
|
|
{osrm::guidance::TurnType::ExitRoundabout, fbresult::ManeuverType_ExitRoundabout},
|
|
{osrm::guidance::TurnType::EnterRotaryAtExit, fbresult::ManeuverType_Rotary},
|
|
{osrm::guidance::TurnType::ExitRotary, fbresult::ManeuverType_ExitRotary},
|
|
{osrm::guidance::TurnType::EnterRoundaboutIntersectionAtExit,
|
|
fbresult::ManeuverType_Roundabout},
|
|
{osrm::guidance::TurnType::ExitRoundaboutIntersection,
|
|
fbresult::ManeuverType_ExitRoundabout},
|
|
{osrm::guidance::TurnType::StayOnRoundabout, fbresult::ManeuverType_RoundaboutTurn},
|
|
{osrm::guidance::TurnType::Sliproad, fbresult::ManeuverType_Notification},
|
|
{osrm::guidance::TurnType::MaxTurnType, fbresult::ManeuverType_Notification}};
|
|
return mappings[turn];
|
|
}
|
|
|
|
fbresult::Turn TurnModifierToFB(osrm::guidance::DirectionModifier::Enum modifier) const
|
|
{
|
|
static std::map<osrm::guidance::DirectionModifier::Enum, fbresult::Turn> mappings = {
|
|
{osrm::guidance::DirectionModifier::UTurn, fbresult::Turn_UTurn},
|
|
{osrm::guidance::DirectionModifier::SharpRight, fbresult::Turn_SharpRight},
|
|
{osrm::guidance::DirectionModifier::Right, fbresult::Turn_Right},
|
|
{osrm::guidance::DirectionModifier::SlightRight, fbresult::Turn_SlightRight},
|
|
{osrm::guidance::DirectionModifier::Straight, fbresult::Turn_Straight},
|
|
{osrm::guidance::DirectionModifier::SlightLeft, fbresult::Turn_SlightLeft},
|
|
{osrm::guidance::DirectionModifier::Left, fbresult::Turn_Left},
|
|
{osrm::guidance::DirectionModifier::SharpLeft, fbresult::Turn_SharpLeft},
|
|
};
|
|
return mappings[modifier];
|
|
}
|
|
|
|
std::vector<int8_t> TurnLaneTypeToFB(const extractor::TurnLaneType::Mask lane_type) const
|
|
{
|
|
const static fbresult::Turn mapping[] = {fbresult::Turn_None,
|
|
fbresult::Turn_Straight,
|
|
fbresult::Turn_SharpLeft,
|
|
fbresult::Turn_Left,
|
|
fbresult::Turn_SlightLeft,
|
|
fbresult::Turn_SlightRight,
|
|
fbresult::Turn_Right,
|
|
fbresult::Turn_SharpRight,
|
|
fbresult::Turn_UTurn,
|
|
fbresult::Turn_SlightLeft,
|
|
fbresult::Turn_SlightRight};
|
|
std::vector<int8_t> result;
|
|
std::bitset<8 * sizeof(extractor::TurnLaneType::Mask)> mask(lane_type);
|
|
for (auto index : util::irange<std::size_t>(0, extractor::TurnLaneType::NUM_TYPES))
|
|
{
|
|
if (mask[index])
|
|
{
|
|
result.push_back(mapping[index]);
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
flatbuffers::Offset<fbresult::RouteObject>
|
|
MakeRoute(flatbuffers::FlatBufferBuilder &fb_result,
|
|
const std::vector<PhantomNodes> &segment_end_coordinates,
|
|
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
|
const std::vector<bool> &source_traversed_in_reverse,
|
|
const std::vector<bool> &target_traversed_in_reverse) const
|
|
{
|
|
auto legs_info = MakeLegs(segment_end_coordinates,
|
|
unpacked_path_segments,
|
|
source_traversed_in_reverse,
|
|
target_traversed_in_reverse);
|
|
std::vector<guidance::RouteLeg> legs = legs_info.first;
|
|
std::vector<guidance::LegGeometry> leg_geometries = legs_info.second;
|
|
auto route = guidance::assembleRoute(legs);
|
|
|
|
// Fill legs
|
|
std::vector<flatbuffers::Offset<fbresult::Leg>> routeLegs;
|
|
routeLegs.reserve(legs.size());
|
|
for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
|
|
{
|
|
auto leg = legs[idx];
|
|
auto &leg_geometry = leg_geometries[idx];
|
|
|
|
// Fill steps
|
|
std::vector<flatbuffers::Offset<fbresult::Step>> legSteps;
|
|
if (!leg.steps.empty())
|
|
{
|
|
legSteps.resize(leg.steps.size());
|
|
std::transform(leg.steps.begin(),
|
|
leg.steps.end(),
|
|
legSteps.begin(),
|
|
[this, &fb_result, &leg_geometry](auto &step) {
|
|
return this->MakeFBStep(fb_result, leg_geometry, step);
|
|
});
|
|
}
|
|
auto steps_vector = fb_result.CreateVector(legSteps);
|
|
|
|
// Fill annotations
|
|
// To maintain support for uses of the old default constructors, we check
|
|
// if annotations property was set manually after default construction
|
|
auto requested_annotations = parameters.annotations_type;
|
|
if (parameters.annotations &&
|
|
(parameters.annotations_type == RouteParameters::AnnotationsType::None))
|
|
{
|
|
requested_annotations = RouteParameters::AnnotationsType::All;
|
|
}
|
|
|
|
flatbuffers::Offset<fbresult::Annotation> annotation_buffer;
|
|
if (requested_annotations != RouteParameters::AnnotationsType::None)
|
|
{
|
|
annotation_buffer =
|
|
MakeFBAnnotations(fb_result, leg_geometry, requested_annotations);
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::String> summary_string;
|
|
if (!leg.summary.empty())
|
|
{
|
|
summary_string = fb_result.CreateString(leg.summary);
|
|
}
|
|
|
|
fbresult::LegBuilder legBuilder(fb_result);
|
|
legBuilder.add_distance(leg.distance);
|
|
legBuilder.add_duration(leg.duration);
|
|
legBuilder.add_weight(leg.weight);
|
|
if (!leg.summary.empty())
|
|
{
|
|
legBuilder.add_summary(summary_string);
|
|
}
|
|
legBuilder.add_steps(steps_vector);
|
|
|
|
if (requested_annotations != RouteParameters::AnnotationsType::None)
|
|
{
|
|
legBuilder.add_annotations(annotation_buffer);
|
|
}
|
|
routeLegs.emplace_back(legBuilder.Finish());
|
|
}
|
|
auto legs_vector = fb_result.CreateVector(routeLegs);
|
|
|
|
// Fill geometry
|
|
auto overview = MakeOverview(leg_geometries);
|
|
mapbox::util::variant<flatbuffers::Offset<flatbuffers::String>,
|
|
flatbuffers::Offset<flatbuffers::Vector<const fbresult::Position *>>>
|
|
geometry;
|
|
if (overview)
|
|
{
|
|
geometry = MakeGeometry(fb_result, overview->begin(), overview->end());
|
|
}
|
|
|
|
auto weight_name_string = fb_result.CreateString(facade.GetWeightName());
|
|
|
|
fbresult::RouteObjectBuilder routeObject(fb_result);
|
|
routeObject.add_distance(route.distance);
|
|
routeObject.add_duration(route.duration);
|
|
routeObject.add_weight(route.weight);
|
|
routeObject.add_weight_name(weight_name_string);
|
|
routeObject.add_legs(legs_vector);
|
|
if (overview)
|
|
{
|
|
mapbox::util::apply_visitor(GeometryVisitor<fbresult::RouteObjectBuilder>(routeObject),
|
|
geometry);
|
|
}
|
|
|
|
return routeObject.Finish();
|
|
}
|
|
|
|
flatbuffers::Offset<fbresult::Annotation>
|
|
MakeFBAnnotations(flatbuffers::FlatBufferBuilder &fb_result,
|
|
guidance::LegGeometry &leg_geometry,
|
|
const RouteParameters::AnnotationsType &requested_annotations) const
|
|
{
|
|
// AnnotationsType uses bit flags, & operator checks if a property is set
|
|
flatbuffers::Offset<flatbuffers::Vector<float>> speed;
|
|
if (parameters.annotations_type & RouteParameters::AnnotationsType::Speed)
|
|
{
|
|
double prev_speed = 0;
|
|
speed =
|
|
GetAnnotations<float>(fb_result,
|
|
leg_geometry,
|
|
[&prev_speed](const guidance::LegGeometry::Annotation &anno) {
|
|
if (anno.duration < std::numeric_limits<float>::min())
|
|
{
|
|
return prev_speed;
|
|
}
|
|
else
|
|
{
|
|
auto speed =
|
|
round(anno.distance / anno.duration * 10.) / 10.;
|
|
prev_speed = speed;
|
|
return util::json::clamp_float(speed);
|
|
}
|
|
});
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::Vector<uint32_t>> duration;
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Duration)
|
|
{
|
|
duration = GetAnnotations<uint32_t>(
|
|
fb_result, leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.duration;
|
|
});
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::Vector<uint32_t>> distance;
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Distance)
|
|
{
|
|
distance = GetAnnotations<uint32_t>(
|
|
fb_result, leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.distance;
|
|
});
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::Vector<uint32_t>> weight;
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Weight)
|
|
{
|
|
weight = GetAnnotations<uint32_t>(
|
|
fb_result, leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.weight;
|
|
});
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::Vector<uint32_t>> datasources;
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Datasources)
|
|
{
|
|
datasources = GetAnnotations<uint32_t>(
|
|
fb_result, leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.datasource;
|
|
});
|
|
}
|
|
std::vector<uint32_t> nodes;
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Nodes)
|
|
{
|
|
nodes.reserve(leg_geometry.node_ids.size());
|
|
for (const auto node_id : leg_geometry.node_ids)
|
|
{
|
|
nodes.emplace_back(static_cast<uint64_t>(facade.GetOSMNodeIDOfNode(node_id)));
|
|
}
|
|
}
|
|
auto nodes_vector = fb_result.CreateVector(nodes);
|
|
// Add any supporting metadata, if needed
|
|
bool use_metadata = requested_annotations & RouteParameters::AnnotationsType::Datasources;
|
|
flatbuffers::Offset<fbresult::Metadata> metadata_buffer;
|
|
if (use_metadata)
|
|
{
|
|
const auto MAX_DATASOURCE_ID = 255u;
|
|
std::vector<flatbuffers::Offset<flatbuffers::String>> names;
|
|
for (auto i = 0u; i < MAX_DATASOURCE_ID; i++)
|
|
{
|
|
const auto name = facade.GetDatasourceName(i);
|
|
// Length of 0 indicates the first empty name, so we can stop here
|
|
if (name.empty())
|
|
break;
|
|
names.emplace_back(
|
|
fb_result.CreateString(std::string(facade.GetDatasourceName(i))));
|
|
}
|
|
metadata_buffer = fbresult::CreateMetadataDirect(fb_result, &names);
|
|
}
|
|
fbresult::AnnotationBuilder annotation(fb_result);
|
|
annotation.add_speed(speed);
|
|
annotation.add_duration(duration);
|
|
annotation.add_distance(distance);
|
|
annotation.add_weight(weight);
|
|
annotation.add_datasources(datasources);
|
|
annotation.add_nodes(nodes_vector);
|
|
if (use_metadata)
|
|
{
|
|
annotation.add_metadata(metadata_buffer);
|
|
}
|
|
|
|
return annotation.Finish();
|
|
}
|
|
|
|
template <typename Builder> class GeometryVisitor
|
|
{
|
|
public:
|
|
GeometryVisitor(Builder &builder) : builder(builder) {}
|
|
|
|
void operator()(const flatbuffers::Offset<flatbuffers::String> &value)
|
|
{
|
|
builder.add_polyline(value);
|
|
}
|
|
void operator()(
|
|
const flatbuffers::Offset<flatbuffers::Vector<const fbresult::Position *>> &value)
|
|
{
|
|
builder.add_coordinates(value);
|
|
}
|
|
|
|
private:
|
|
Builder &builder;
|
|
};
|
|
|
|
flatbuffers::Offset<fbresult::Step> MakeFBStep(flatbuffers::FlatBufferBuilder &builder,
|
|
const guidance::LegGeometry &leg_geometry,
|
|
const guidance::RouteStep &step) const
|
|
{
|
|
auto name_string = builder.CreateString(step.name);
|
|
|
|
flatbuffers::Offset<flatbuffers::String> ref_string;
|
|
if (!step.ref.empty())
|
|
{
|
|
ref_string = builder.CreateString(step.ref);
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::String> pronunciation_string;
|
|
if (!step.pronunciation.empty())
|
|
{
|
|
pronunciation_string = builder.CreateString(step.pronunciation);
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::String> destinations_string;
|
|
if (!step.destinations.empty())
|
|
{
|
|
destinations_string = builder.CreateString(step.destinations);
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::String> exists_string;
|
|
if (!step.exits.empty())
|
|
{
|
|
exists_string = builder.CreateString(step.exits);
|
|
}
|
|
|
|
flatbuffers::Offset<flatbuffers::String> rotary_name_string;
|
|
flatbuffers::Offset<flatbuffers::String> rotary_pronunciation_string;
|
|
if (!step.rotary_name.empty())
|
|
{
|
|
rotary_name_string = builder.CreateString(step.rotary_name);
|
|
if (!step.rotary_pronunciation.empty())
|
|
{
|
|
rotary_pronunciation_string = builder.CreateString(step.rotary_pronunciation);
|
|
}
|
|
}
|
|
auto mode_string = builder.CreateString(extractor::travelModeToString(step.mode));
|
|
|
|
// Geometry
|
|
auto geometry = MakeGeometry(builder,
|
|
leg_geometry.locations.begin() + step.geometry_begin,
|
|
leg_geometry.locations.begin() + step.geometry_end);
|
|
// Maneuver
|
|
fbresult::StepManeuverBuilder maneuver(builder);
|
|
fbresult::Position maneuverPosition{
|
|
static_cast<float>(util::toFloating(step.maneuver.location.lon).__value),
|
|
static_cast<float>(util::toFloating(step.maneuver.location.lat).__value)};
|
|
maneuver.add_location(&maneuverPosition);
|
|
maneuver.add_bearing_before(step.maneuver.bearing_before);
|
|
maneuver.add_bearing_after(step.maneuver.bearing_after);
|
|
if (step.maneuver.waypoint_type == guidance::WaypointType::None)
|
|
maneuver.add_type(TurnTypeToFB(step.maneuver.instruction.type));
|
|
else
|
|
maneuver.add_type(WaypointTypeToFB(step.maneuver.waypoint_type));
|
|
if (osrm::engine::api::json::detail::isValidModifier(step.maneuver))
|
|
{
|
|
maneuver.add_modifier(TurnModifierToFB(step.maneuver.instruction.direction_modifier));
|
|
}
|
|
if (step.maneuver.exit != 0)
|
|
{
|
|
maneuver.add_exit(step.maneuver.exit);
|
|
}
|
|
auto maneuver_buffer = maneuver.Finish();
|
|
|
|
// intersections
|
|
auto intersections_vector = MakeFBIntersections(builder, step);
|
|
|
|
fbresult::StepBuilder stepBuilder(builder);
|
|
stepBuilder.add_duration(step.duration);
|
|
stepBuilder.add_distance(step.distance);
|
|
stepBuilder.add_weight(step.weight);
|
|
stepBuilder.add_name(name_string);
|
|
stepBuilder.add_mode(mode_string);
|
|
stepBuilder.add_driving_side(step.is_left_hand_driving);
|
|
stepBuilder.add_ref(ref_string);
|
|
stepBuilder.add_pronunciation(pronunciation_string);
|
|
stepBuilder.add_destinations(destinations_string);
|
|
stepBuilder.add_exits(exists_string);
|
|
stepBuilder.add_rotary_name(rotary_name_string);
|
|
stepBuilder.add_rotary_pronunciation(rotary_pronunciation_string);
|
|
stepBuilder.add_intersections(intersections_vector);
|
|
stepBuilder.add_maneuver(maneuver_buffer);
|
|
mapbox::util::apply_visitor(GeometryVisitor<fbresult::StepBuilder>(stepBuilder), geometry);
|
|
return stepBuilder.Finish();
|
|
};
|
|
|
|
flatbuffers::Offset<flatbuffers::Vector<flatbuffers::Offset<fbresult::Intersection>>>
|
|
MakeFBIntersections(flatbuffers::FlatBufferBuilder &fb_result,
|
|
const guidance::RouteStep &step) const
|
|
{
|
|
std::vector<flatbuffers::Offset<fbresult::Intersection>> intersections;
|
|
intersections.resize(step.intersections.size());
|
|
std::transform(
|
|
step.intersections.begin(),
|
|
step.intersections.end(),
|
|
intersections.begin(),
|
|
[&fb_result, this](const guidance::IntermediateIntersection &intersection) {
|
|
std::vector<flatbuffers::Offset<fbresult::Lane>> lanes;
|
|
if (json::detail::hasValidLanes(intersection))
|
|
{
|
|
BOOST_ASSERT(intersection.lanes.lanes_in_turn >= 1);
|
|
lanes.reserve(intersection.lane_description.size());
|
|
LaneID lane_id = intersection.lane_description.size();
|
|
|
|
for (const auto &lane_desc : intersection.lane_description)
|
|
{
|
|
--lane_id;
|
|
auto indications = TurnLaneTypeToFB(lane_desc);
|
|
|
|
auto lane_valid = lane_id >= intersection.lanes.first_lane_from_the_right &&
|
|
lane_id < intersection.lanes.first_lane_from_the_right +
|
|
intersection.lanes.lanes_in_turn;
|
|
lanes.push_back(
|
|
fbresult::CreateLaneDirect(fb_result, &indications, lane_valid));
|
|
}
|
|
}
|
|
auto lanes_vector = fb_result.CreateVector(lanes);
|
|
|
|
fbresult::Position maneuverPosition{
|
|
static_cast<float>(util::toFloating(intersection.location.lon).__value),
|
|
static_cast<float>(util::toFloating(intersection.location.lat).__value)};
|
|
auto bearings_vector = fb_result.CreateVector(intersection.bearings);
|
|
std::vector<flatbuffers::Offset<flatbuffers::String>> classes;
|
|
classes.resize(intersection.classes.size());
|
|
std::transform(
|
|
intersection.classes.begin(),
|
|
intersection.classes.end(),
|
|
classes.begin(),
|
|
[&fb_result](const std::string &cls) { return fb_result.CreateString(cls); });
|
|
auto classes_vector = fb_result.CreateVector(classes);
|
|
auto entry_vector = fb_result.CreateVector(intersection.entry);
|
|
|
|
fbresult::IntersectionBuilder intersectionBuilder(fb_result);
|
|
intersectionBuilder.add_location(&maneuverPosition);
|
|
intersectionBuilder.add_bearings(bearings_vector);
|
|
intersectionBuilder.add_classes(classes_vector);
|
|
intersectionBuilder.add_entry(entry_vector);
|
|
intersectionBuilder.add_in_bearing(intersection.in);
|
|
intersectionBuilder.add_out_bearing(intersection.out);
|
|
intersectionBuilder.add_lanes(lanes_vector);
|
|
return intersectionBuilder.Finish();
|
|
});
|
|
return fb_result.CreateVector(intersections);
|
|
}
|
|
|
|
util::json::Object MakeRoute(const std::vector<PhantomNodes> &segment_end_coordinates,
|
|
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
|
const std::vector<bool> &source_traversed_in_reverse,
|
|
const std::vector<bool> &target_traversed_in_reverse) const
|
|
{
|
|
auto legs_info = MakeLegs(segment_end_coordinates,
|
|
unpacked_path_segments,
|
|
source_traversed_in_reverse,
|
|
target_traversed_in_reverse);
|
|
std::vector<guidance::RouteLeg> legs = legs_info.first;
|
|
std::vector<guidance::LegGeometry> leg_geometries = legs_info.second;
|
|
|
|
auto route = guidance::assembleRoute(legs);
|
|
boost::optional<util::json::Value> json_overview =
|
|
MakeGeometry(MakeOverview(leg_geometries));
|
|
|
|
std::vector<util::json::Value> step_geometries;
|
|
const auto total_step_count =
|
|
std::accumulate(legs.begin(), legs.end(), 0, [](const auto &v, const auto &leg) {
|
|
return v + leg.steps.size();
|
|
});
|
|
step_geometries.reserve(total_step_count);
|
|
|
|
for (const auto idx : util::irange<std::size_t>(0UL, legs.size()))
|
|
{
|
|
auto &leg_geometry = leg_geometries[idx];
|
|
|
|
std::transform(
|
|
legs[idx].steps.begin(),
|
|
legs[idx].steps.end(),
|
|
std::back_inserter(step_geometries),
|
|
[this, &leg_geometry](const guidance::RouteStep &step) {
|
|
if (parameters.geometries == RouteParameters::GeometriesType::Polyline)
|
|
{
|
|
return static_cast<util::json::Value>(json::makePolyline<100000>(
|
|
leg_geometry.locations.begin() + step.geometry_begin,
|
|
leg_geometry.locations.begin() + step.geometry_end));
|
|
}
|
|
|
|
if (parameters.geometries == RouteParameters::GeometriesType::Polyline6)
|
|
{
|
|
return static_cast<util::json::Value>(json::makePolyline<1000000>(
|
|
leg_geometry.locations.begin() + step.geometry_begin,
|
|
leg_geometry.locations.begin() + step.geometry_end));
|
|
}
|
|
|
|
BOOST_ASSERT(parameters.geometries == RouteParameters::GeometriesType::GeoJSON);
|
|
return static_cast<util::json::Value>(json::makeGeoJSONGeometry(
|
|
leg_geometry.locations.begin() + step.geometry_begin,
|
|
leg_geometry.locations.begin() + step.geometry_end));
|
|
});
|
|
}
|
|
|
|
std::vector<util::json::Object> annotations;
|
|
|
|
// To maintain support for uses of the old default constructors, we check
|
|
// if annotations property was set manually after default construction
|
|
auto requested_annotations = parameters.annotations_type;
|
|
if (parameters.annotations &&
|
|
(parameters.annotations_type == RouteParameters::AnnotationsType::None))
|
|
{
|
|
requested_annotations = RouteParameters::AnnotationsType::All;
|
|
}
|
|
|
|
if (requested_annotations != RouteParameters::AnnotationsType::None)
|
|
{
|
|
for (const auto idx : util::irange<std::size_t>(0UL, leg_geometries.size()))
|
|
{
|
|
auto &leg_geometry = leg_geometries[idx];
|
|
util::json::Object annotation;
|
|
|
|
// AnnotationsType uses bit flags, & operator checks if a property is set
|
|
if (parameters.annotations_type & RouteParameters::AnnotationsType::Speed)
|
|
{
|
|
double prev_speed = 0;
|
|
annotation.values["speed"] = GetAnnotations(
|
|
leg_geometry, [&prev_speed](const guidance::LegGeometry::Annotation &anno) {
|
|
if (anno.duration < std::numeric_limits<double>::min())
|
|
{
|
|
return prev_speed;
|
|
}
|
|
else
|
|
{
|
|
auto speed = std::round(anno.distance / anno.duration * 10.) / 10.;
|
|
prev_speed = speed;
|
|
return util::json::clamp_float(speed);
|
|
}
|
|
});
|
|
}
|
|
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Duration)
|
|
{
|
|
annotation.values["duration"] = GetAnnotations(
|
|
leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.duration;
|
|
});
|
|
}
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Distance)
|
|
{
|
|
annotation.values["distance"] = GetAnnotations(
|
|
leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.distance;
|
|
});
|
|
}
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Weight)
|
|
{
|
|
annotation.values["weight"] = GetAnnotations(
|
|
leg_geometry,
|
|
[](const guidance::LegGeometry::Annotation &anno) { return anno.weight; });
|
|
}
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Datasources)
|
|
{
|
|
annotation.values["datasources"] = GetAnnotations(
|
|
leg_geometry, [](const guidance::LegGeometry::Annotation &anno) {
|
|
return anno.datasource;
|
|
});
|
|
}
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Nodes)
|
|
{
|
|
util::json::Array nodes;
|
|
nodes.values.reserve(leg_geometry.node_ids.size());
|
|
for (const auto node_id : leg_geometry.node_ids)
|
|
{
|
|
nodes.values.push_back(
|
|
static_cast<std::uint64_t>(facade.GetOSMNodeIDOfNode(node_id)));
|
|
}
|
|
annotation.values["nodes"] = std::move(nodes);
|
|
}
|
|
// Add any supporting metadata, if needed
|
|
if (requested_annotations & RouteParameters::AnnotationsType::Datasources)
|
|
{
|
|
const auto MAX_DATASOURCE_ID = 255u;
|
|
util::json::Object metadata;
|
|
util::json::Array datasource_names;
|
|
for (auto i = 0u; i < MAX_DATASOURCE_ID; i++)
|
|
{
|
|
const auto name = facade.GetDatasourceName(i);
|
|
// Length of 0 indicates the first empty name, so we can stop here
|
|
if (name.empty())
|
|
break;
|
|
datasource_names.values.push_back(std::string(facade.GetDatasourceName(i)));
|
|
}
|
|
metadata.values["datasource_names"] = datasource_names;
|
|
annotation.values["metadata"] = metadata;
|
|
}
|
|
|
|
annotations.push_back(std::move(annotation));
|
|
}
|
|
}
|
|
|
|
auto result = json::makeRoute(route,
|
|
json::makeRouteLegs(std::move(legs),
|
|
std::move(step_geometries),
|
|
std::move(annotations)),
|
|
std::move(json_overview),
|
|
facade.GetWeightName());
|
|
|
|
return result;
|
|
}
|
|
|
|
const RouteParameters ¶meters;
|
|
|
|
std::pair<std::vector<guidance::RouteLeg>, std::vector<guidance::LegGeometry>>
|
|
MakeLegs(const std::vector<PhantomNodes> &segment_end_coordinates,
|
|
const std::vector<std::vector<PathData>> &unpacked_path_segments,
|
|
const std::vector<bool> &source_traversed_in_reverse,
|
|
const std::vector<bool> &target_traversed_in_reverse) const
|
|
{
|
|
auto result =
|
|
std::make_pair(std::vector<guidance::RouteLeg>(), std::vector<guidance::LegGeometry>());
|
|
auto &legs = result.first;
|
|
auto &leg_geometries = result.second;
|
|
auto number_of_legs = segment_end_coordinates.size();
|
|
legs.reserve(number_of_legs);
|
|
leg_geometries.reserve(number_of_legs);
|
|
|
|
for (auto idx : util::irange<std::size_t>(0UL, number_of_legs))
|
|
{
|
|
const auto &phantoms = segment_end_coordinates[idx];
|
|
const auto &path_data = unpacked_path_segments[idx];
|
|
|
|
const bool reversed_source = source_traversed_in_reverse[idx];
|
|
const bool reversed_target = target_traversed_in_reverse[idx];
|
|
|
|
auto leg = guidance::assembleLeg(facade,
|
|
path_data,
|
|
phantoms.source_phantom,
|
|
phantoms.target_phantom,
|
|
reversed_target);
|
|
|
|
guidance::LegGeometry leg_geometry;
|
|
|
|
// Generate additional geometry data if request includes turn-by-turn steps,
|
|
// overview geometry or route geometry annotations.
|
|
// Note that overview geometry and route geometry annotations can return different
|
|
// results depending on whether turn-by-turn steps are also requested.
|
|
if (parameters.steps || parameters.annotations ||
|
|
parameters.overview != RouteParameters::OverviewType::False)
|
|
{
|
|
|
|
leg_geometry = guidance::assembleGeometry(BaseAPI::facade,
|
|
path_data,
|
|
phantoms.source_phantom,
|
|
phantoms.target_phantom,
|
|
reversed_source,
|
|
reversed_target);
|
|
|
|
util::Log(logDEBUG) << "Assembling steps " << std::endl;
|
|
if (parameters.steps)
|
|
{
|
|
leg.summary = guidance::assembleSummary(
|
|
facade, path_data, phantoms.target_phantom, reversed_target);
|
|
|
|
auto steps = guidance::assembleSteps(BaseAPI::facade,
|
|
path_data,
|
|
leg_geometry,
|
|
phantoms.source_phantom,
|
|
phantoms.target_phantom,
|
|
reversed_source,
|
|
reversed_target);
|
|
|
|
// Apply maneuver overrides before any other post
|
|
// processing is performed
|
|
guidance::applyOverrides(BaseAPI::facade, steps, leg_geometry);
|
|
|
|
// Collapse segregated steps before others
|
|
steps = guidance::collapseSegregatedTurnInstructions(std::move(steps));
|
|
|
|
/* Perform step-based post-processing.
|
|
*
|
|
* Using post-processing on basis of route-steps for a single leg at a time
|
|
* comes at the cost that we cannot count the correct exit for roundabouts.
|
|
* We can only emit the exit nr/intersections up to/starting at a part of the
|
|
*leg. If a roundabout is not terminated in a leg, we will end up with a
|
|
*enter-roundabout
|
|
* and exit-roundabout-nr where the exit nr is out of sync with the previous
|
|
*enter.
|
|
*
|
|
* | S |
|
|
* * *
|
|
* ----* * ----
|
|
* T
|
|
* ----* * ----
|
|
* V * *
|
|
* | |
|
|
* | |
|
|
*
|
|
* Coming from S via V to T, we end up with the legs S->V and V->T. V-T will say
|
|
*to take the second exit, even though counting from S it would be the third.
|
|
* For S, we only emit `roundabout` without an exit number, showing that we
|
|
*enter a roundabout to find a via point. The same exit will be emitted, though,
|
|
*if we should start routing at S, making the overall response consistent.
|
|
*
|
|
* ⚠ CAUTION: order of post-processing steps is important
|
|
* - handleRoundabouts must be called before collapseTurnInstructions that
|
|
* expects post-processed roundabouts
|
|
*/
|
|
|
|
guidance::trimShortSegments(steps, leg_geometry);
|
|
leg.steps = guidance::handleRoundabouts(std::move(steps));
|
|
leg.steps = guidance::collapseTurnInstructions(std::move(leg.steps));
|
|
leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
|
|
leg.steps = guidance::buildIntersections(std::move(leg.steps));
|
|
leg.steps = guidance::suppressShortNameSegments(std::move(leg.steps));
|
|
leg.steps = guidance::assignRelativeLocations(std::move(leg.steps),
|
|
leg_geometry,
|
|
phantoms.source_phantom,
|
|
phantoms.target_phantom);
|
|
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);
|
|
}
|
|
}
|
|
|
|
leg_geometries.push_back(std::move(leg_geometry));
|
|
legs.push_back(std::move(leg));
|
|
}
|
|
return result;
|
|
}
|
|
|
|
boost::optional<std::vector<Coordinate>>
|
|
MakeOverview(const std::vector<guidance::LegGeometry> &leg_geometries) const
|
|
{
|
|
boost::optional<std::vector<Coordinate>> overview;
|
|
if (parameters.overview != RouteParameters::OverviewType::False)
|
|
{
|
|
const auto use_simplification =
|
|
parameters.overview == RouteParameters::OverviewType::Simplified;
|
|
BOOST_ASSERT(use_simplification ||
|
|
parameters.overview == RouteParameters::OverviewType::Full);
|
|
|
|
overview = guidance::assembleOverview(leg_geometries, use_simplification);
|
|
}
|
|
return overview;
|
|
}
|
|
};
|
|
|
|
} // namespace api
|
|
} // namespace engine
|
|
} // namespace osrm
|
|
|
|
#endif
|