inline RouteLeg assembleLeg(const datafacade::BaseDataFacade &facade, const std::vector<PathData> &route_data, const LegGeometry &leg_geometry, const PhantomNode &source_node, const PhantomNode &target_node, const bool target_traversed_in_reverse, const bool needs_summary) { const auto target_duration = (target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight) / 10.; auto distance = std::accumulate( leg_geometry.segment_distances.begin(), leg_geometry.segment_distances.end(), 0.); auto duration = std::accumulate(route_data.begin(), route_data.end(), 0., [](const double sum, const PathData &data) { return sum + data.duration_until_turn; }) / 10.; // s // | // Given a route a---b---c where there is a right turn at c. // | // d // |--t // e // (a, b, c) gets compressed to (a,c) // (c, d, e) gets compressed to (c,e) // The duration of the turn (a,c) -> (c,e) will be the duration of (a,c) (e.g. the duration // of (a,b,c)). // The phantom node of s will contain: // `forward_weight`: duration of (a,s) // `forward_offset`: 0 (its the first segment) // The phantom node of t will contain: // `forward_weight`: duration of (d,t) // `forward_offset`: duration of (c, d) // path_data will have entries for (s,b), (b, c), (c, d) but (d, t) is only // caputed by the phantom node. So we need to add the target duration here. // On local segments, the target duration is already part of the duration, however. duration = duration + target_duration; if (route_data.empty()) { duration -= (target_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight) / 10.0; } std::string summary; if (needs_summary) { auto summary_array = detail::summarizeRoute<detail::MAX_USED_SEGMENTS>( route_data, target_node, target_traversed_in_reverse); BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0); BOOST_ASSERT(summary_array.begin() != summary_array.end()); // transform a name_id into a string containing either the name, or -if the name is empty- // the reference. const auto name_id_to_string = [&](const NameID name_id) { const auto name = facade.GetNameForID(name_id); if (!name.empty()) return name; else { const auto ref = facade.GetRefForID(name_id); return ref; } }; const auto not_empty = [&](const std::string &name) { return !name.empty(); }; const auto summary_names = summary_array | boost::adaptors::transformed(name_id_to_string) | boost::adaptors::filtered(not_empty); summary = boost::algorithm::join(summary_names, ", "); } return RouteLeg{duration, distance, summary, {}}; }
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 constexpr ZERO_DURATION = 0., ZERO_DISTANCE = 0.; const constexpr char *NO_ROTARY_NAME = ""; const EdgeWeight source_duration = source_traversed_in_reverse ? source_node.reverse_weight : source_node.forward_weight; const auto source_mode = source_traversed_in_reverse ? source_node.backward_travel_mode : source_node.forward_travel_mode; const EdgeWeight target_duration = target_traversed_in_reverse ? target_node.reverse_weight : target_node.forward_weight; const auto target_mode = target_traversed_in_reverse ? target_node.backward_travel_mode : target_node.forward_travel_mode; 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); StepManeuver maneuver{source_node.location, bearings.first, bearings.second, extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Depart, 0}; Intersection intersection{source_node.location, std::vector<short>({bearings.second}), std::vector<bool>({true}), Intersection::NO_INDEX, 0, util::guidance::LaneTuple(), {}}; if (leg_data.size() > 0) { // 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 int segment_duration = 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_node.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; // all changes to this check have to be matched with assemble_geometry if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn) { BOOST_ASSERT(segment_duration >= 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 distance = leg_geometry.segment_distances[segment_index]; steps.push_back(RouteStep{step_name_id, std::move(name), std::move(ref), std::move(pronunciation), std::move(destinations), NO_ROTARY_NAME, NO_ROTARY_NAME, segment_duration / 10.0, distance, path_point.travel_mode, maneuver, leg_geometry.FrontIndex(segment_index), leg_geometry.BackIndex(segment_index) + 1, {intersection}}); if (leg_data_index + 1 < leg_data.size()) { step_name_id = leg_data[leg_data_index + 1].name_id; } else { step_name_id = target_node.name_id; } // extract bearings bearings = std::make_pair<std::uint16_t, std::uint16_t>( path_point.pre_turn_bearing.Get(), path_point.post_turn_bearing.Get()); const auto entry_class = facade.GetEntryClass(path_point.entry_classid); const auto bearing_class = facade.GetBearingClass(facade.GetBearingClassID(path_point.turn_via_node)); auto bearing_data = bearing_class.getAvailableBearings(); 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_class.getAvailableBearings().size()); intersection.lanes = path_point.lane_data.first; intersection.lane_description = path_point.lane_data.second != INVALID_LANE_DESCRIPTIONID ? facade.GetTurnDescription(path_point.lane_data.second) : extractor::guidance::TurnLaneDescription(); std::copy(bearing_class.getAvailableBearings().begin(), bearing_class.getAvailableBearings().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::reverseBearing(std::round(bearings.first)); maneuver = {intersection.location, bearing_in_driving_direction, bearings.second, path_point.turn_instruction, WaypointType::None, 0}; segment_index++; segment_duration = 0; } } const auto distance = leg_geometry.segment_distances[segment_index]; const int duration = segment_duration + target_duration; BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{step_name_id, facade.GetNameForID(step_name_id), facade.GetRefForID(step_name_id), facade.GetPronunciationForID(step_name_id), facade.GetDestinationsForID(step_name_id), NO_ROTARY_NAME, NO_ROTARY_NAME, duration / 10., distance, target_mode, maneuver, leg_geometry.FrontIndex(segment_index), leg_geometry.BackIndex(segment_index) + 1, {intersection}}); } // 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); // s t // u-------------v // |---| source_duration // |---------| target_duration int duration = target_duration - source_duration; BOOST_ASSERT(duration >= 0); steps.push_back(RouteStep{source_node.name_id, facade.GetNameForID(source_node.name_id), facade.GetRefForID(source_node.name_id), facade.GetPronunciationForID(source_node.name_id), facade.GetDestinationsForID(source_node.name_id), NO_ROTARY_NAME, NO_ROTARY_NAME, duration / 10., leg_geometry.segment_distances[segment_index], source_mode, std::move(maneuver), leg_geometry.FrontIndex(segment_index), leg_geometry.BackIndex(segment_index) + 1, {intersection}}); } BOOST_ASSERT(segment_index == number_of_segments - 1); bearings = detail::getArriveBearings(leg_geometry); intersection = { target_node.location, std::vector<short>({static_cast<short>(util::bearing::reverseBearing(bearings.first))}), std::vector<bool>({true}), 0, Intersection::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, extractor::guidance::TurnInstruction::NO_TURN(), WaypointType::Arrive, 0}; BOOST_ASSERT(!leg_geometry.locations.empty()); steps.push_back(RouteStep{target_node.name_id, facade.GetNameForID(target_node.name_id), facade.GetRefForID(target_node.name_id), facade.GetPronunciationForID(target_node.name_id), facade.GetDestinationsForID(target_node.name_id), NO_ROTARY_NAME, NO_ROTARY_NAME, ZERO_DURATION, ZERO_DISTANCE, target_mode, std::move(maneuver), leg_geometry.locations.size() - 1, leg_geometry.locations.size(), {intersection}}); 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()); return steps; }