void IntersectionHandler::assignFork(const EdgeID via_edge, ConnectedRoad &left, ConnectedRoad ¢er, ConnectedRoad &right) const { // TODO handle low priority road classes in a reasonable way if (left.entry_allowed && center.entry_allowed && right.entry_allowed) { left.turn.instruction = {TurnType::Fork, DirectionModifier::SlightLeft}; if (angularDeviation(center.turn.angle, 180) < MAXIMAL_ALLOWED_NO_TURN_DEVIATION) { const auto &in_data = node_based_graph.GetEdgeData(via_edge); const auto &out_data = node_based_graph.GetEdgeData(center.turn.eid); if (detail::requiresAnnouncement(in_data, out_data)) { center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight}; } else { center.turn.instruction = {TurnType::Suppressed, DirectionModifier::Straight}; } } else { center.turn.instruction = {TurnType::Fork, DirectionModifier::Straight}; } right.turn.instruction = {TurnType::Fork, DirectionModifier::SlightRight}; } else if (left.entry_allowed) { if (right.entry_allowed) assignFork(via_edge, left, right); else if (center.entry_allowed) assignFork(via_edge, left, center); else left.turn.instruction = {findBasicTurnType(via_edge, left), getTurnDirection(left.turn.angle)}; } else if (right.entry_allowed) { if (center.entry_allowed) assignFork(via_edge, center, right); else right.turn.instruction = {findBasicTurnType(via_edge, right), getTurnDirection(right.turn.angle)}; } else { if (center.entry_allowed) center.turn.instruction = {findBasicTurnType(via_edge, center), getTurnDirection(center.turn.angle)}; } }
void IntersectionHandler::assignTrivialTurns(const EdgeID via_eid, Intersection &intersection, const std::size_t begin, const std::size_t end) const { for (std::size_t index = begin; index != end; ++index) if (intersection[index].entry_allowed) intersection[index].turn.instruction = { findBasicTurnType(via_eid, intersection[index]), getTurnDirection(intersection[index].turn.angle)}; }
// Sets basic turn types as fallback for otherwise unhandled turns Intersection TurnAnalysis::setTurnTypes(const NodeID from_nid, const EdgeID, Intersection intersection) const { for (auto &road : intersection) { if (!road.entry_allowed) continue; const EdgeID onto_edge = road.turn.eid; const NodeID to_nid = node_based_graph.GetTarget(onto_edge); road.turn.instruction = {TurnType::Turn, (from_nid == to_nid) ? DirectionModifier::UTurn : getTurnDirection(road.turn.angle)}; } return intersection; }
// "Sliproads" occur when we've got a link between two roads (MOTORWAY_LINK, etc), but // the two roads are *also* directly connected shortly afterwards. // In these cases, we tag the turn-type as "sliproad", and then in post-processing // we emit a "turn", instead of "take the ramp"+"merge" Intersection TurnAnalysis::handleSliproads(const EdgeID source_edge_id, Intersection intersection) const { auto intersection_node_id = node_based_graph.GetTarget(source_edge_id); const auto linkTest = [this](const ConnectedRoad &road) { return // isLinkClass( // node_based_graph.GetEdgeData(road.turn.eid).road_classification.road_class) && !node_based_graph.GetEdgeData(road.turn.eid).roundabout && road.entry_allowed && angularDeviation(road.turn.angle, STRAIGHT_ANGLE) <= 2 * NARROW_TURN_ANGLE; }; bool hasNarrow = std::find_if(intersection.begin(), intersection.end(), linkTest) != intersection.end(); if (!hasNarrow) return intersection; const auto source_edge_data = node_based_graph.GetEdgeData(source_edge_id); // Find the continuation of the intersection we're on auto next_road = std::find_if( intersection.begin(), intersection.end(), [this, source_edge_data](const ConnectedRoad &road) { const auto road_edge_data = node_based_graph.GetEdgeData(road.turn.eid); // Test to see if the source edge and the one we're looking at are the same road return road_edge_data.road_classification.road_class == source_edge_data.road_classification.road_class && road_edge_data.name_id != EMPTY_NAMEID && road_edge_data.name_id == source_edge_data.name_id && road.entry_allowed && angularDeviation(road.turn.angle, STRAIGHT_ANGLE) < FUZZY_ANGLE_DIFFERENCE; }); const bool hasNext = next_road != intersection.end(); if (!hasNext) { return intersection; } // Threshold check, if the intersection is too far away, don't bother continuing const auto &next_road_data = node_based_graph.GetEdgeData(next_road->turn.eid); if (next_road_data.distance > MAX_SLIPROAD_THRESHOLD) { return intersection; } const auto next_road_next_intersection = intersection_generator(intersection_node_id, next_road->turn.eid); const auto next_intersection_node = node_based_graph.GetTarget(next_road->turn.eid); std::unordered_set<NameID> target_road_names; for (const auto &road : next_road_next_intersection) { const auto &target_data = node_based_graph.GetEdgeData(road.turn.eid); target_road_names.insert(target_data.name_id); } for (auto &road : intersection) { if (linkTest(road)) { auto target_intersection = intersection_generator(intersection_node_id, road.turn.eid); for (const auto &candidate_road : target_intersection) { const auto &candidate_data = node_based_graph.GetEdgeData(candidate_road.turn.eid); if (target_road_names.count(candidate_data.name_id) > 0 && node_based_graph.GetTarget(candidate_road.turn.eid) == next_intersection_node) { road.turn.instruction.type = TurnType::Sliproad; break; } } } } if (next_road->turn.instruction.type == TurnType::Fork) { const auto &next_data = node_based_graph.GetEdgeData(next_road->turn.eid); if (next_data.name_id == source_edge_data.name_id) { if (angularDeviation(next_road->turn.angle, STRAIGHT_ANGLE) < 5) next_road->turn.instruction.type = TurnType::Suppressed; else next_road->turn.instruction.type = TurnType::Continue; next_road->turn.instruction.direction_modifier = getTurnDirection(next_road->turn.angle); } else if (next_data.name_id != EMPTY_NAMEID) { next_road->turn.instruction.type = TurnType::NewName; next_road->turn.instruction.direction_modifier = getTurnDirection(next_road->turn.angle); } else { next_road->turn.instruction.type = TurnType::Suppressed; } } return intersection; }
TurnInstruction IntersectionHandler::getInstructionForObvious(const std::size_t num_roads, const EdgeID via_edge, const bool through_street, const ConnectedRoad &road) const { const auto type = findBasicTurnType(via_edge, road); // handle travel modes: const auto in_mode = node_based_graph.GetEdgeData(via_edge).travel_mode; const auto out_mode = node_based_graph.GetEdgeData(road.turn.eid).travel_mode; if (type == TurnType::OnRamp) { return {TurnType::OnRamp, getTurnDirection(road.turn.angle)}; } if (angularDeviation(road.turn.angle, 0) < 0.01) { return {TurnType::Turn, DirectionModifier::UTurn}; } if (type == TurnType::Turn) { const auto &in_data = node_based_graph.GetEdgeData(via_edge); const auto &out_data = node_based_graph.GetEdgeData(road.turn.eid); if (in_data.name_id != out_data.name_id && requiresNameAnnounced(name_table.GetNameForID(in_data.name_id), name_table.GetNameForID(out_data.name_id), street_name_suffix_table)) { // obvious turn onto a through street is a merge if (through_street) { return {TurnType::Merge, road.turn.angle > STRAIGHT_ANGLE ? DirectionModifier::SlightRight : DirectionModifier::SlightLeft}; } else { return {TurnType::NewName, getTurnDirection(road.turn.angle)}; } } else { if (in_mode == out_mode) return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; else return {TurnType::Notification, getTurnDirection(road.turn.angle)}; } } BOOST_ASSERT(type == TurnType::Continue); if (in_mode != out_mode) { return {TurnType::Notification, getTurnDirection(road.turn.angle)}; } if (num_roads > 2) { return {TurnType::Suppressed, getTurnDirection(road.turn.angle)}; } else { return {TurnType::NoTurn, getTurnDirection(road.turn.angle)}; } }