diff --git a/features/guidance/dedicated-turn-roads.feature b/features/guidance/dedicated-turn-roads.feature index 210317ad4..d6fe959ca 100644 --- a/features/guidance/dedicated-turn-roads.feature +++ b/features/guidance/dedicated-turn-roads.feature @@ -313,3 +313,39 @@ Feature: Slipways and Dedicated Turn Lanes | a,d | new york,new york | depart,arrive | this is the sinatra route | | a,j | new york,1st street,1st street | depart,turn left,arrive | | | a,1 | new york,m street,1st street,1st street | depart,turn right,turn left,arrive | this can false be seen as a sliproad | + + # Merging into degree two loop on dedicated turn detection / 2927 + Scenario: Turn Instead of Ramp + Given the node map + | | | | | | | | | | | | | | | | f | + | | | | | g | | | | | | h | | | | | | + | | | | | | | | | | | | | d | | | e | + | i | | | | c | | | | | | j | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | b | | | | | | | | | | | | | | + | | | | | | | | | | | | | | | | | + | | | a | | | | | | | | | | | | | | + + And the ways + | nodes | highway | name | oneway | + | abi | primary | road | yes | + | bcjd | primary | loop | yes | + | dhgf | primary | loop | yes | + | fed | primary | loop | yes | + + And the nodes + | node | highway | + | g | traffic_signals | + | c | traffic_signals | + + # We don't actually care about routes here, this is all about endless loops in turn discovery + When I route I should get + | waypoints | route | turns | + | a,i | road,road,road | depart,fork slight left,arrive | diff --git a/features/guidance/roundabout.feature b/features/guidance/roundabout.feature index 4dcd0f0d8..a02c762be 100644 --- a/features/guidance/roundabout.feature +++ b/features/guidance/roundabout.feature @@ -36,6 +36,25 @@ Feature: Basic Roundabout | h,c | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | | h,e | gh,bcegb,bcegb | depart,roundabout-exit-undefined,arrive | + #2927 + Scenario: Only Roundabout + Given the node map + | | | a | | | + | | | | | | + | | | | | | + | b | | | | d | + | | | | | | + | | | | | | + | | | c | | | + + And the ways + | nodes | junction | + | abcda | roundabout | + + When I route I should get + | waypoints | route | turns | + | a,c | abcda,abcda | depart,arrive | + Scenario: Only Exit Given the node map | | | a | | | diff --git a/src/extractor/guidance/intersection_generator.cpp b/src/extractor/guidance/intersection_generator.cpp index 7402dff13..81a06f82d 100644 --- a/src/extractor/guidance/intersection_generator.cpp +++ b/src/extractor/guidance/intersection_generator.cpp @@ -1,11 +1,12 @@ -#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/constants.hpp" +#include "extractor/guidance/intersection_generator.hpp" #include "extractor/guidance/toolkit.hpp" #include #include #include #include +#include #include #include @@ -268,8 +269,11 @@ bool IntersectionGenerator::CanMerge(const NodeID node_at_intersection, return becomes_narrower; }; + const bool is_y_arm_first = isValidYArm(first_index, second_index); + const bool is_y_arm_second = isValidYArm(second_index, first_index); + // Only merge valid y-arms - if (!isValidYArm(first_index, second_index) || !isValidYArm(second_index, first_index)) + if (!is_y_arm_first || !is_y_arm_second) return false; if (angle_between < 60) @@ -577,10 +581,14 @@ IntersectionGenerator::GetActualNextIntersection(const NodeID starting_node, // to prevent endless loops const auto termination_node = node_based_graph.GetTarget(via_edge); - while (result.size() == 2 && - node_based_graph.GetEdgeData(via_edge).IsCompatibleTo( - node_based_graph.GetEdgeData(result[1].turn.eid))) + // using a maximum lookahead, we make sure not to end up in some form of loop + std::unordered_set visited_nodes; + while (visited_nodes.count(node_at_intersection) == 0 && + (result.size() == 2 && + node_based_graph.GetEdgeData(via_edge).IsCompatibleTo( + node_based_graph.GetEdgeData(result[1].turn.eid)))) { + visited_nodes.insert(node_at_intersection); node_at_intersection = node_based_graph.GetTarget(incoming_edge); incoming_edge = result[1].turn.eid; result = GetConnectedRoads(node_at_intersection, incoming_edge); diff --git a/src/extractor/guidance/sliproad_handler.cpp b/src/extractor/guidance/sliproad_handler.cpp index 23cea1458..16a40fa2b 100644 --- a/src/extractor/guidance/sliproad_handler.cpp +++ b/src/extractor/guidance/sliproad_handler.cpp @@ -56,9 +56,14 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) auto intersection = intersection_generator(at_node, road.turn.eid); auto in_edge = road.turn.eid; // skip over traffic lights - while (intersection.size() == 2) + // to prevent ending up in an endless loop, we remember all visited nodes. This is + // necessary, since merging of roads can actually create enterable loops of degree two + std::unordered_set visited_nodes; + auto node = at_node; + while (intersection.size() == 2 && visited_nodes.count(node) == 0) { - const auto node = node_based_graph.GetTarget(in_edge); + visited_nodes.insert(node); + node = node_based_graph.GetTarget(in_edge); if (node == at_node) { // we ended up in a loop without exit @@ -70,6 +75,11 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) output_node = node_based_graph.GetTarget(in_edge); intersection = intersection_generator(node, in_edge); } + if (intersection.size() <= 2) + { + output_node = SPECIAL_NODEID; + intersection.clear(); + } return intersection; }; @@ -162,6 +172,9 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) const auto next_road_next_intersection = findNextIntersectionForRoad(intersection_node_id, next_road, next_intersection_node); + if (next_road_next_intersection.empty()) + return intersection; + // If we are at a traffic loop at the end of a road, don't consider it a sliproad if (intersection_node_id == next_intersection_node) return intersection; @@ -194,13 +207,14 @@ operator()(const NodeID, const EdgeID source_edge_id, Intersection intersection) const auto link_data = node_based_graph.GetEdgeData(road.turn.eid); // Check if the road continues here const bool is_through_street = + !target_intersection.empty() && target_intersection.end() != - std::find_if(target_intersection.begin() + 1, - target_intersection.end(), - [this, &link_data](const ConnectedRoad &road) { - return node_based_graph.GetEdgeData(road.turn.eid).name_id == - link_data.name_id; - }); + std::find_if(target_intersection.begin() + 1, + target_intersection.end(), + [this, &link_data](const ConnectedRoad &road) { + return node_based_graph.GetEdgeData(road.turn.eid).name_id == + link_data.name_id; + }); // if the sliproad candidate is a through street, we cannot handle it as a sliproad if (is_through_street) diff --git a/src/extractor/guidance/turn_lane_augmentation.cpp b/src/extractor/guidance/turn_lane_augmentation.cpp index 564bd9d87..3939489cc 100644 --- a/src/extractor/guidance/turn_lane_augmentation.cpp +++ b/src/extractor/guidance/turn_lane_augmentation.cpp @@ -1,5 +1,3 @@ -#include "util/debug.hpp" - #include "extractor/guidance/turn_lane_augmentation.hpp" #include "extractor/guidance/turn_lane_types.hpp" #include "util/simple_logger.hpp"