diff --git a/features/guidance/staggered-intersections.feature b/features/guidance/staggered-intersections.feature new file mode 100644 index 000000000..63e0dc5c9 --- /dev/null +++ b/features/guidance/staggered-intersections.feature @@ -0,0 +1,92 @@ +@routing @guidance @staggered-intersections +Feature: Staggered Intersections + + Background: + Given the profile "car" + Given a grid size of 1 meters + # Note the one meter grid size: staggered intersections make zig-zags of a couple of meters only + + # https://www.openstreetmap.org/#map=19/39.26022/-84.25144 + Scenario: Staggered Intersection: Oak St, Cedar Dr + Given the node map + | | | j | | | + | a | b | c | | | + | | | d | | | + | | | e | f | g | + | | | h | | | + | | | i | | | + + And the ways + | nodes | highway | name | + | abc | residential | Oak St | + | efg | residential | Oak St | + | jcdehi | residential | Cedar Dr | + + When I route I should get + | waypoints | route | turns | + | a,g | Oak St,Oak St | depart,arrive | + | g,a | Oak St,Oak St | depart,arrive | + + Scenario: Staggered Intersection: do not collapse if long segment in between + Given the node map + | | | j | | | + | a | b | c | | | + | | | | | | + | | | | | | + | | | d | | | + | | | | | | + | | | | | | + | | | e | f | g | + | | | h | | | + | | | i | | | + + And the ways + | nodes | highway | name | + | abc | residential | Oak St | + | efg | residential | Oak St | + | jcdehi | residential | Cedar Dr | + + When I route I should get + | waypoints | route | turns | + | a,g | Oak St,Cedar Dr,Oak St,Oak St | depart,turn right,turn left,arrive | + | g,a | Oak St,Cedar Dr,Oak St,Oak St | depart,turn right,turn left,arrive | + + Scenario: Staggered Intersection: do not collapse if not left-right or right-left + Given the node map + | | | j | | | + | a | b | c | | | + | | | d | | | + | g | f | e | | | + | | | h | | | + | | | i | | | + + And the ways + | nodes | highway | name | + | abc | residential | Oak St | + | efg | residential | Oak St | + | jcdehi | residential | Cedar Dr | + + When I route I should get + | waypoints | route | turns | + | a,g | Oak St,Oak St,Oak St | depart,continue uturn,arrive | + | g,a | Oak St,Oak St,Oak St | depart,continue uturn,arrive | + + Scenario: Staggered Intersection: do not collapse if the names are not the same + Given the node map + | | | j | | | + | a | b | c | | | + | | | d | | | + | | | e | f | g | + | | | h | | | + | | | i | | | + + And the ways + | nodes | highway | name | + | abc | residential | Oak St | + | efg | residential | Elm St | + | jcdehi | residential | Cedar Dr | + + When I route I should get + | waypoints | route | turns | + | a,g | Oak St,Cedar Dr,Elm St,Elm St | depart,turn right,turn left,arrive | + | g,a | Elm St,Cedar Dr,Oak St,Oak St | depart,turn right,turn left,arrive | diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index 25b8d3069..b16550262 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -17,6 +17,7 @@ #include #include #include +#include #include #include @@ -513,6 +514,33 @@ void collapseTurnAt(std::vector &steps, } } } + +// Staggered intersection are very short zig-zags of a few meters. +// We do not want to announce these short left-rights or right-lefts: +// +// * -> b a -> * +// | or | becomes a -> b +// a -> * * -> b +// +bool isStaggeredIntersection(const RouteStep &previous, const RouteStep ¤t) +{ + // Base decision on distance since the zig-zag is a visual clue. + const constexpr auto MAX_STAGGERED_DISTANCE = 3; // debatable, but keep short to be on safe side + + using namespace util::guidance; + + const auto left_right = isLeftTurn(previous.maneuver.instruction) && // + isRightTurn(current.maneuver.instruction); + const auto right_left = isRightTurn(previous.maneuver.instruction) && // + isLeftTurn(current.maneuver.instruction); + + // A RouteStep holds distance/duration from the maneuver to the subsequent step. + // We are only interested in the distance between the first and the second. + const auto is_short = previous.distance < MAX_STAGGERED_DISTANCE; + + return is_short && (left_right || right_left); +} + } // namespace // Post processing can invalidate some instructions. For example StayOnRoundabout @@ -753,12 +781,13 @@ std::vector collapseTurns(std::vector steps) // A name oszillation changes from name A shortly to name B and back to A. // In these cases, the name change will be suppressed. else if (one_back_index > 0 && compatible(current_step, one_back_step) && - isCollapsableInstruction(current_step.maneuver.instruction) && - isCollapsableInstruction(one_back_step.maneuver.instruction)) + ((isCollapsableInstruction(current_step.maneuver.instruction) && + isCollapsableInstruction(one_back_step.maneuver.instruction)) || + isStaggeredIntersection(one_back_step, current_step))) { const auto two_back_index = getPreviousIndex(one_back_index); BOOST_ASSERT(two_back_index < steps.size()); - // valid, since one_back is collapsable: + // valid, since one_back is collapsable or a turn and therefore not depart: const auto &coming_from_name = steps[two_back_index].name; if (current_step.name == coming_from_name) {