Collapse Staggered Intersections.

Staggered intersection are very short zig-zags of only a few meters.
They are common in rural and exurban areas, especially in the US.

(In addition, these cases could as well be tagging issues)

We do not want to announce these short left-rights or right-lefts:

          * -> b      a -> *
          |       or       |       becomes  a   ->   b
     a -> *                * -> b

Here is one example:

- https://www.openstreetmap.org/edit#map=20/39.26017/-84.25182

And here are two edge-cases that we don't handle at the moment:

- http://www.openstreetmap.org/edit#map=20/38.87900/-76.98519
- http://www.openstreetmap.org/edit#map=19/45.51056/-122.63462

and probably should not handle since the distance in between is
quite long (roughly 7-15 meters). For these we want to announce
two turns to not confuse the user.

Thanks to @1ec5 for raising this issue and @karenzshea for
providing additional US examples and cultural insights.
This commit is contained in:
Daniel J. Hofmann 2016-08-03 12:13:07 +02:00 committed by Patrick Niklaus
parent e8ce119972
commit b1e309b4eb
No known key found for this signature in database
GPG Key ID: E426891B5F978B1B
2 changed files with 124 additions and 3 deletions

View File

@ -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 |

View File

@ -17,6 +17,7 @@
#include <cmath>
#include <cstddef>
#include <iostream>
#include <iostream>
#include <limits>
#include <utility>
@ -513,6 +514,33 @@ void collapseTurnAt(std::vector<RouteStep> &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 &current)
{
// 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<RouteStep> collapseTurns(std::vector<RouteStep> 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)
{