This change takes the existing typedefs for weight, duration and distance, and makes them proper types, using the existing Alias functionality. Primarily this is to prevent bugs where the metrics are switched, but it also adds additional documentation. For example, it now makes it clear (despite the naming of variables) that most of the trip algorithm is running on the duration metric. I've not made any changes to the casts performed between metrics and numeric types, they now just more explicit.
373 lines
19 KiB
C++
373 lines
19 KiB
C++
#ifndef ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
|
|
#define ENGINE_GUIDANCE_ASSEMBLE_STEPS_HPP_
|
|
|
|
#include "extractor/travel_mode.hpp"
|
|
#include "extractor/turn_lane_types.hpp"
|
|
#include "guidance/turn_instruction.hpp"
|
|
#include "engine/datafacade/datafacade_base.hpp"
|
|
#include "engine/guidance/leg_geometry.hpp"
|
|
#include "engine/guidance/route_step.hpp"
|
|
#include "engine/guidance/step_maneuver.hpp"
|
|
#include "engine/internal_route_result.hpp"
|
|
#include "engine/phantom_node.hpp"
|
|
#include "util/bearing.hpp"
|
|
#include "util/coordinate.hpp"
|
|
#include "util/coordinate_calculation.hpp"
|
|
#include "util/guidance/entry_class.hpp"
|
|
#include "util/guidance/turn_lanes.hpp"
|
|
#include "util/typedefs.hpp"
|
|
|
|
#include <boost/optional.hpp>
|
|
#include <cstddef>
|
|
#include <guidance/turn_bearing.hpp>
|
|
#include <vector>
|
|
|
|
namespace osrm
|
|
{
|
|
namespace engine
|
|
{
|
|
namespace guidance
|
|
{
|
|
namespace detail
|
|
{
|
|
std::pair<short, short> getDepartBearings(const LegGeometry &leg_geometry,
|
|
const PhantomNode &source_node,
|
|
const bool traversed_in_reverse);
|
|
std::pair<short, short> getArriveBearings(const LegGeometry &leg_geometry,
|
|
const PhantomNode &target_node,
|
|
const bool traversed_in_reverse);
|
|
} // namespace detail
|
|
|
|
inline std::vector<RouteStep> assembleSteps(const datafacade::BaseDataFacade &facade,
|
|
const std::vector<PathData> &leg_data,
|
|
const LegGeometry &leg_geometry,
|
|
const PhantomNode &source_node,
|
|
const PhantomNode &target_node,
|
|
const bool source_traversed_in_reverse,
|
|
const bool target_traversed_in_reverse)
|
|
{
|
|
const double weight_multiplier = facade.GetWeightMultiplier();
|
|
|
|
const double constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0., ZERO_WEIGHT = 0;
|
|
const constexpr char *NO_ROTARY_NAME = "";
|
|
const EdgeWeight source_weight =
|
|
source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight;
|
|
const EdgeDuration source_duration =
|
|
source_traversed_in_reverse ? source_node.reverse_duration : source_node.forward_duration;
|
|
const auto source_node_id = source_traversed_in_reverse ? source_node.reverse_segment_id.id
|
|
: source_node.forward_segment_id.id;
|
|
const auto source_name_id = facade.GetNameIndex(source_node_id);
|
|
bool is_segregated = facade.IsSegregated(source_node_id);
|
|
const auto source_mode = facade.GetTravelMode(source_node_id);
|
|
auto source_classes = facade.GetClasses(facade.GetClassData(source_node_id));
|
|
|
|
const EdgeDuration target_duration =
|
|
target_traversed_in_reverse ? target_node.reverse_duration : target_node.forward_duration;
|
|
const EdgeWeight target_weight =
|
|
target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight;
|
|
const auto target_node_id = target_traversed_in_reverse ? target_node.reverse_segment_id.id
|
|
: target_node.forward_segment_id.id;
|
|
const auto target_name_id = facade.GetNameIndex(target_node_id);
|
|
const auto target_mode = facade.GetTravelMode(target_node_id);
|
|
|
|
const auto number_of_segments = leg_geometry.GetNumberOfSegments();
|
|
|
|
std::vector<RouteStep> steps;
|
|
steps.reserve(number_of_segments);
|
|
|
|
std::size_t segment_index = 0;
|
|
BOOST_ASSERT(leg_geometry.locations.size() >= 2);
|
|
|
|
auto bearings =
|
|
detail::getDepartBearings(leg_geometry, source_node, source_traversed_in_reverse);
|
|
|
|
StepManeuver maneuver{source_node.location,
|
|
bearings.first,
|
|
bearings.second,
|
|
osrm::guidance::TurnInstruction::NO_TURN(),
|
|
WaypointType::Depart,
|
|
0};
|
|
|
|
IntermediateIntersection intersection{source_node.location,
|
|
std::vector<short>({bearings.second}),
|
|
std::vector<bool>({true}),
|
|
IntermediateIntersection::NO_INDEX,
|
|
0,
|
|
util::guidance::LaneTuple(),
|
|
{},
|
|
source_classes};
|
|
|
|
if (!leg_data.empty())
|
|
{
|
|
// PathData saves the information we need of the segment _before_ the turn,
|
|
// but a RouteStep is with regard to the segment after the turn.
|
|
// We need to skip the first segment because it is already covered by the
|
|
// initial start of a route
|
|
EdgeDuration segment_duration = {0};
|
|
EdgeWeight segment_weight = {0};
|
|
|
|
// some name changes are not announced in our processing. For these, we have to keep the
|
|
// first name on the segment
|
|
auto step_name_id = source_name_id;
|
|
for (std::size_t leg_data_index = 0; leg_data_index < leg_data.size(); ++leg_data_index)
|
|
{
|
|
const auto &path_point = leg_data[leg_data_index];
|
|
segment_duration += path_point.duration_until_turn;
|
|
segment_weight += path_point.weight_until_turn;
|
|
|
|
// all changes to this check have to be matched with assemble_geometry
|
|
const auto turn_instruction =
|
|
path_point.turn_edge ? facade.GetTurnInstructionForEdgeID(*path_point.turn_edge)
|
|
: osrm::guidance::TurnInstruction::NO_TURN();
|
|
if (turn_instruction.type != osrm::guidance::TurnType::NoTurn)
|
|
{
|
|
BOOST_ASSERT(segment_weight >= EdgeWeight{0});
|
|
const auto name = facade.GetNameForID(step_name_id);
|
|
const auto ref = facade.GetRefForID(step_name_id);
|
|
const auto pronunciation = facade.GetPronunciationForID(step_name_id);
|
|
const auto destinations = facade.GetDestinationsForID(step_name_id);
|
|
const auto exits = facade.GetExitsForID(step_name_id);
|
|
const auto distance = leg_geometry.segment_distances[segment_index];
|
|
// intersections contain the classes of exiting road
|
|
intersection.classes =
|
|
facade.GetClasses(facade.GetClassData(path_point.from_edge_based_node));
|
|
|
|
const auto is_left_hand_driving =
|
|
facade.IsLeftHandDriving(path_point.from_edge_based_node);
|
|
const auto travel_mode = facade.GetTravelMode(path_point.from_edge_based_node);
|
|
BOOST_ASSERT(travel_mode > 0);
|
|
|
|
steps.push_back(RouteStep{path_point.from_edge_based_node,
|
|
step_name_id,
|
|
is_segregated,
|
|
name.to_string(),
|
|
ref.to_string(),
|
|
pronunciation.to_string(),
|
|
destinations.to_string(),
|
|
exits.to_string(),
|
|
NO_ROTARY_NAME,
|
|
NO_ROTARY_NAME,
|
|
from_alias<double>(segment_duration) / 10.,
|
|
distance,
|
|
from_alias<double>(segment_weight) / weight_multiplier,
|
|
travel_mode,
|
|
maneuver,
|
|
leg_geometry.FrontIndex(segment_index),
|
|
leg_geometry.BackIndex(segment_index) + 1,
|
|
{intersection},
|
|
is_left_hand_driving});
|
|
|
|
if (leg_data_index + 1 < leg_data.size())
|
|
{
|
|
step_name_id =
|
|
facade.GetNameIndex(leg_data[leg_data_index + 1].from_edge_based_node);
|
|
is_segregated =
|
|
facade.IsSegregated(leg_data[leg_data_index + 1].from_edge_based_node);
|
|
}
|
|
else
|
|
{
|
|
step_name_id = facade.GetNameIndex(target_node_id);
|
|
is_segregated = facade.IsSegregated(target_node_id);
|
|
}
|
|
|
|
// extract bearings
|
|
auto pre_turn_bearing = path_point.turn_edge
|
|
? facade.PreTurnBearing(*path_point.turn_edge)
|
|
: osrm::guidance::TurnBearing(0);
|
|
auto post_turn_bearing = path_point.turn_edge
|
|
? facade.PostTurnBearing(*path_point.turn_edge)
|
|
: osrm::guidance::TurnBearing(0);
|
|
bearings = std::make_pair<std::uint16_t, std::uint16_t>(pre_turn_bearing.Get(),
|
|
post_turn_bearing.Get());
|
|
const auto bearing_class = facade.GetBearingClass(path_point.turn_via_node);
|
|
auto bearing_data = bearing_class.getAvailableBearings();
|
|
|
|
util::guidance::LaneTupleIdPair lane_data = {{0, INVALID_LANEID},
|
|
INVALID_LANE_DESCRIPTIONID};
|
|
if (path_point.turn_edge && facade.HasLaneData(*path_point.turn_edge))
|
|
{
|
|
lane_data = facade.GetLaneData(*path_point.turn_edge);
|
|
}
|
|
|
|
intersection.in = bearing_class.findMatchingBearing(bearings.first);
|
|
intersection.out = bearing_class.findMatchingBearing(bearings.second);
|
|
intersection.location = facade.GetCoordinateOfNode(path_point.turn_via_node);
|
|
intersection.bearings.clear();
|
|
intersection.bearings.reserve(bearing_data.size());
|
|
intersection.lanes = lane_data.first;
|
|
intersection.lane_description = lane_data.second != INVALID_LANE_DESCRIPTIONID
|
|
? facade.GetTurnDescription(lane_data.second)
|
|
: extractor::TurnLaneDescription();
|
|
|
|
// Lanes in turn are bound by total number of lanes at the location
|
|
BOOST_ASSERT(intersection.lanes.lanes_in_turn <=
|
|
intersection.lane_description.size());
|
|
// No lanes at location and no turn lane or lanes at location and lanes in turn
|
|
BOOST_ASSERT((intersection.lane_description.empty() &&
|
|
intersection.lanes.lanes_in_turn == 0) ||
|
|
(!intersection.lane_description.empty() &&
|
|
intersection.lanes.lanes_in_turn != 0));
|
|
|
|
auto entry_class = path_point.turn_edge
|
|
? facade.GetEntryClass(*path_point.turn_edge)
|
|
: EMPTY_ENTRY_CLASS;
|
|
std::copy(bearing_data.begin(),
|
|
bearing_data.end(),
|
|
std::back_inserter(intersection.bearings));
|
|
intersection.entry.clear();
|
|
for (auto idx : util::irange<std::size_t>(0, intersection.bearings.size()))
|
|
{
|
|
intersection.entry.push_back(entry_class.allowsEntry(idx));
|
|
}
|
|
std::int16_t bearing_in_driving_direction =
|
|
util::bearing::reverse(std::round(bearings.first));
|
|
maneuver = {intersection.location,
|
|
bearing_in_driving_direction,
|
|
bearings.second,
|
|
turn_instruction,
|
|
WaypointType::None,
|
|
0};
|
|
segment_index++;
|
|
segment_duration = {0};
|
|
segment_weight = {0};
|
|
}
|
|
}
|
|
const auto distance = leg_geometry.segment_distances[segment_index];
|
|
const EdgeDuration duration = segment_duration + target_duration;
|
|
const EdgeWeight weight = segment_weight + target_weight;
|
|
// intersections contain the classes of exiting road
|
|
intersection.classes = facade.GetClasses(facade.GetClassData(target_node_id));
|
|
BOOST_ASSERT(duration >= EdgeDuration{0});
|
|
steps.push_back(RouteStep{leg_data[leg_data.size() - 1].from_edge_based_node,
|
|
step_name_id,
|
|
is_segregated,
|
|
facade.GetNameForID(step_name_id).to_string(),
|
|
facade.GetRefForID(step_name_id).to_string(),
|
|
facade.GetPronunciationForID(step_name_id).to_string(),
|
|
facade.GetDestinationsForID(step_name_id).to_string(),
|
|
facade.GetExitsForID(step_name_id).to_string(),
|
|
NO_ROTARY_NAME,
|
|
NO_ROTARY_NAME,
|
|
from_alias<double>(duration) / 10.,
|
|
distance,
|
|
from_alias<double>(weight) / weight_multiplier,
|
|
target_mode,
|
|
maneuver,
|
|
leg_geometry.FrontIndex(segment_index),
|
|
leg_geometry.BackIndex(segment_index) + 1,
|
|
{intersection},
|
|
facade.IsLeftHandDriving(target_node_id)});
|
|
}
|
|
// In this case the source + target are on the same edge segment
|
|
else
|
|
{
|
|
BOOST_ASSERT(source_node.fwd_segment_position == target_node.fwd_segment_position);
|
|
BOOST_ASSERT(source_traversed_in_reverse == target_traversed_in_reverse);
|
|
|
|
// The difference (target-source) should handle
|
|
// all variants for similar directions u-v and s-t (and opposite)
|
|
// s(t) t(s) source_traversed_in_reverse = target_traversed_in_reverse = false
|
|
// u-------------v
|
|
// |---| source_weight
|
|
// |---------| target_weight
|
|
|
|
// s(t) t(s) source_traversed_in_reverse = target_traversed_in_reverse = true
|
|
// u-------------v
|
|
// | |---------| source_weight
|
|
// | |---| target_weight
|
|
BOOST_ASSERT(target_weight >= source_weight);
|
|
const EdgeWeight weight = target_weight - source_weight;
|
|
|
|
// use rectified linear unit function to avoid negative duration values
|
|
// due to flooring errors in phantom snapping
|
|
BOOST_ASSERT(target_duration >= source_duration || weight == EdgeWeight{0});
|
|
const EdgeDuration duration =
|
|
std::max<EdgeDuration>({0}, target_duration - source_duration);
|
|
|
|
steps.push_back(RouteStep{source_node_id,
|
|
source_name_id,
|
|
is_segregated,
|
|
facade.GetNameForID(source_name_id).to_string(),
|
|
facade.GetRefForID(source_name_id).to_string(),
|
|
facade.GetPronunciationForID(source_name_id).to_string(),
|
|
facade.GetDestinationsForID(source_name_id).to_string(),
|
|
facade.GetExitsForID(source_name_id).to_string(),
|
|
NO_ROTARY_NAME,
|
|
NO_ROTARY_NAME,
|
|
from_alias<double>(duration) / 10.,
|
|
leg_geometry.segment_distances[segment_index],
|
|
from_alias<double>(weight) / weight_multiplier,
|
|
source_mode,
|
|
maneuver,
|
|
leg_geometry.FrontIndex(segment_index),
|
|
leg_geometry.BackIndex(segment_index) + 1,
|
|
{intersection},
|
|
facade.IsLeftHandDriving(source_node_id)});
|
|
}
|
|
|
|
BOOST_ASSERT(segment_index == number_of_segments - 1);
|
|
bearings = detail::getArriveBearings(leg_geometry, target_node, target_traversed_in_reverse);
|
|
|
|
intersection = {
|
|
target_node.location,
|
|
std::vector<short>({static_cast<short>(util::bearing::reverse(bearings.first))}),
|
|
std::vector<bool>({true}),
|
|
0,
|
|
IntermediateIntersection::NO_INDEX,
|
|
util::guidance::LaneTuple(),
|
|
{},
|
|
{}};
|
|
|
|
// This step has length zero, the only reason we need it is the target location
|
|
maneuver = {intersection.location,
|
|
bearings.first,
|
|
bearings.second,
|
|
osrm::guidance::TurnInstruction::NO_TURN(),
|
|
WaypointType::Arrive,
|
|
0};
|
|
|
|
BOOST_ASSERT(!leg_geometry.locations.empty());
|
|
steps.push_back(RouteStep{target_node_id,
|
|
target_name_id,
|
|
facade.IsSegregated(target_node_id),
|
|
facade.GetNameForID(target_name_id).to_string(),
|
|
facade.GetRefForID(target_name_id).to_string(),
|
|
facade.GetPronunciationForID(target_name_id).to_string(),
|
|
facade.GetDestinationsForID(target_name_id).to_string(),
|
|
facade.GetExitsForID(target_name_id).to_string(),
|
|
NO_ROTARY_NAME,
|
|
NO_ROTARY_NAME,
|
|
ZERO_DURATION,
|
|
ZERO_DISTANCE,
|
|
ZERO_WEIGHT,
|
|
target_mode,
|
|
maneuver,
|
|
leg_geometry.locations.size() - 1,
|
|
leg_geometry.locations.size(),
|
|
{intersection},
|
|
facade.IsLeftHandDriving(target_node_id)});
|
|
|
|
BOOST_ASSERT(steps.front().intersections.size() == 1);
|
|
BOOST_ASSERT(steps.front().intersections.front().bearings.size() == 1);
|
|
BOOST_ASSERT(steps.front().intersections.front().entry.size() == 1);
|
|
BOOST_ASSERT(steps.front().maneuver.waypoint_type == WaypointType::Depart);
|
|
|
|
BOOST_ASSERT(steps.back().intersections.size() == 1);
|
|
BOOST_ASSERT(steps.back().intersections.front().bearings.size() == 1);
|
|
BOOST_ASSERT(steps.back().intersections.front().entry.size() == 1);
|
|
BOOST_ASSERT(steps.back().maneuver.waypoint_type == WaypointType::Arrive);
|
|
BOOST_ASSERT(steps.back().intersections.front().lanes.lanes_in_turn == 0);
|
|
BOOST_ASSERT(steps.back().intersections.front().lanes.first_lane_from_the_right ==
|
|
INVALID_LANEID);
|
|
BOOST_ASSERT(steps.back().intersections.front().lane_description.empty());
|
|
// depart and arrive need to be trivial
|
|
BOOST_ASSERT(steps.front().maneuver.exit == 0 && steps.back().maneuver.exit == 0);
|
|
return steps;
|
|
}
|
|
|
|
} // namespace guidance
|
|
} // namespace engine
|
|
} // namespace osrm
|
|
|
|
#endif // ENGINE_GUIDANCE_SEGMENT_LIST_HPP_
|