bool Hint::IsValid(const util::Coordinate new_input_coordinates, const datafacade::BaseDataFacade &facade) const { auto is_same_input_coordinate = new_input_coordinates.lon == phantom.input_location.lon && new_input_coordinates.lat == phantom.input_location.lat; return is_same_input_coordinate && phantom.IsValid(facade.GetNumberOfNodes()) && facade.GetCheckSum() == data_checksum; }
// Extracts the geometry for each segment and calculates the traveled distance // Combines the geometry form the phantom node with the PathData // to the full route geometry. // // turn 0 1 2 3 4 // s...x...y...z...t // |---|segment 0 // |---| segment 1 // |---| segment 2 // |---| segment 3 inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, const std::vector<PathData> &leg_data, const PhantomNode &source_node, const PhantomNode &target_node) { LegGeometry geometry; // segment 0 first and last geometry.segment_offsets.push_back(0); geometry.locations.push_back(source_node.location); // Need to get the node ID preceding the source phantom node // TODO: check if this was traversed in reverse? std::vector<NodeID> reverse_geometry; facade.GetUncompressedGeometry(source_node.reverse_packed_geometry_id, reverse_geometry); geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode( reverse_geometry[reverse_geometry.size() - source_node.fwd_segment_position - 1])); std::vector<uint8_t> forward_datasource_vector; facade.GetUncompressedDatasources(source_node.forward_packed_geometry_id, forward_datasource_vector); auto cumulative_distance = 0.; auto current_distance = 0.; auto prev_coordinate = geometry.locations.front(); for (const auto &path_point : leg_data) { auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node); current_distance = util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate); cumulative_distance += current_distance; // all changes to this check have to be matched with assemble_steps if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn) { geometry.segment_distances.push_back(cumulative_distance); geometry.segment_offsets.push_back(geometry.locations.size()); cumulative_distance = 0.; } prev_coordinate = coordinate; geometry.annotations.emplace_back(LegGeometry::Annotation{ current_distance, path_point.duration_until_turn / 10., path_point.datasource_id}); geometry.locations.push_back(std::move(coordinate)); geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode(path_point.turn_via_node)); } current_distance = util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location); cumulative_distance += current_distance; // segment leading to the target node geometry.segment_distances.push_back(cumulative_distance); std::vector<DatasourceID> forward_datasources; facade.GetUncompressedDatasources(target_node.forward_packed_geometry_id, forward_datasources); geometry.annotations.emplace_back( LegGeometry::Annotation{current_distance, target_node.forward_weight / 10., forward_datasources[target_node.fwd_segment_position]}); geometry.segment_offsets.push_back(geometry.locations.size()); geometry.locations.push_back(target_node.location); // Need to get the node ID following the destination phantom node // TODO: check if this was traversed in reverse?? std::vector<NodeID> forward_geometry; facade.GetUncompressedGeometry(target_node.forward_packed_geometry_id, forward_geometry); geometry.osm_node_ids.push_back( facade.GetOSMNodeIDOfNode(forward_geometry[target_node.fwd_segment_position])); BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1); BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size()); BOOST_ASSERT(geometry.annotations.size() == geometry.locations.size() - 1); return geometry; }
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); if (route_data.empty()) summary_array[0] = source_node.name_id; BOOST_ASSERT(detail::MAX_USED_SEGMENTS > 0); BOOST_ASSERT(summary_array.begin() != summary_array.end()); summary = std::accumulate(std::next(summary_array.begin()), summary_array.end(), facade.GetNameForID(summary_array.front()), [&facade](std::string previous, const std::uint32_t name_id) { if (name_id != 0) { previous += ", " + facade.GetNameForID(name_id); } return previous; }); } return RouteLeg{duration, distance, summary, {}}; }
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; }
// Extracts the geometry for each segment and calculates the traveled distance // Combines the geometry form the phantom node with the PathData // to the full route geometry. // // turn 0 1 2 3 4 // s...x...y...z...t // |---|segment 0 // |---| segment 1 // |---| segment 2 // |---| segment 3 inline LegGeometry assembleGeometry(const datafacade::BaseDataFacade &facade, const std::vector<PathData> &leg_data, const PhantomNode &source_node, const PhantomNode &target_node, const bool reversed_source, const bool reversed_target) { LegGeometry geometry; // segment 0 first and last geometry.segment_offsets.push_back(0); geometry.locations.push_back(source_node.location); // u * v // 0 -- 1 -- 2 -- 3 // fwd_segment_position: 1 // source node fwd: 1 1 -> 2 -> 3 // source node rev: 2 0 <- 1 <- 2 const auto source_segment_start_coordinate = source_node.fwd_segment_position + (reversed_source ? 1 : 0); const auto source_node_id = reversed_source ? source_node.reverse_segment_id.id : source_node.forward_segment_id.id; const auto source_gemetry_id = facade.GetGeometryIndex(source_node_id).id; const std::vector<NodeID> source_geometry = facade.GetUncompressedForwardGeometry(source_gemetry_id); geometry.osm_node_ids.push_back( facade.GetOSMNodeIDOfNode(source_geometry[source_segment_start_coordinate])); auto cumulative_distance = 0.; auto current_distance = 0.; auto prev_coordinate = geometry.locations.front(); for (const auto &path_point : leg_data) { auto coordinate = facade.GetCoordinateOfNode(path_point.turn_via_node); current_distance = util::coordinate_calculation::haversineDistance(prev_coordinate, coordinate); cumulative_distance += current_distance; // all changes to this check have to be matched with assemble_steps if (path_point.turn_instruction.type != extractor::guidance::TurnType::NoTurn) { geometry.segment_distances.push_back(cumulative_distance); geometry.segment_offsets.push_back(geometry.locations.size()); cumulative_distance = 0.; } prev_coordinate = coordinate; geometry.annotations.emplace_back( LegGeometry::Annotation{current_distance, path_point.duration_until_turn / 10., path_point.weight_until_turn / facade.GetWeightMultiplier(), path_point.datasource_id}); geometry.locations.push_back(std::move(coordinate)); geometry.osm_node_ids.push_back(facade.GetOSMNodeIDOfNode(path_point.turn_via_node)); } current_distance = util::coordinate_calculation::haversineDistance(prev_coordinate, target_node.location); cumulative_distance += current_distance; // segment leading to the target node geometry.segment_distances.push_back(cumulative_distance); const auto target_node_id = reversed_target ? target_node.reverse_segment_id.id : target_node.forward_segment_id.id; const auto target_gemetry_id = facade.GetGeometryIndex(target_node_id).id; const std::vector<DatasourceID> forward_datasources = facade.GetUncompressedForwardDatasources(target_gemetry_id); // FIXME if source and target phantoms are on the same segment then duration and weight // will be from one projected point till end of segment // testbot/weight.feature:Start and target on the same and adjacent edge geometry.annotations.emplace_back(LegGeometry::Annotation{ current_distance, (reversed_target ? target_node.reverse_duration : target_node.forward_duration) / 10., (reversed_target ? target_node.reverse_weight : target_node.forward_weight) / facade.GetWeightMultiplier(), forward_datasources[target_node.fwd_segment_position]}); geometry.segment_offsets.push_back(geometry.locations.size()); geometry.locations.push_back(target_node.location); // u * v // 0 -- 1 -- 2 -- 3 // fwd_segment_position: 1 // target node fwd: 2 0 -> 1 -> 2 // target node rev: 1 1 <- 2 <- 3 const auto target_segment_end_coordinate = target_node.fwd_segment_position + (reversed_target ? 0 : 1); const std::vector<NodeID> target_geometry = facade.GetUncompressedForwardGeometry(target_gemetry_id); geometry.osm_node_ids.push_back( facade.GetOSMNodeIDOfNode(target_geometry[target_segment_end_coordinate])); BOOST_ASSERT(geometry.segment_distances.size() == geometry.segment_offsets.size() - 1); BOOST_ASSERT(geometry.locations.size() > geometry.segment_distances.size()); BOOST_ASSERT(geometry.annotations.size() == geometry.locations.size() - 1); return geometry; }