Remove lanes from roundabouts, closes #2626

After half a day of looking at the tagging and the data came to the
following conclusion:

We can't keep the user to the innermost / outermost lanes depending on
the exit the route takes: we found situations where both heuristics were
wrong.

Even on popular roundabouts the tagging is often wrong or in the best
case not present at all.

There are at least two different ways to interpret roundabout
indications: 1/ where e.g. a right arrow on the lane indicates turn
restrictions for the roundabout and the need to take this lane to exit
the roundabout to the right (possibly skipping multiple exits) and 2/
where a right arrow just means this is a lane in a immediate right turn.

Example: Australia marks lanes with arrows that seem to indicate
"angles you can exit the roundabout from", for example, these two ways:
- http://www.openstreetmap.org/way/320941710
- http://www.openstreetmap.org/way/42918021

Whereas Germany marks lanes with "directions you can travel in these
lanes immediately after entering the roundabout":
- http://www.openstreetmap.org/way/52578338

These two different interpretations of how to draw the arrows on the
roads mean we have conflicting solutions to "which lanes can you use to
take exit B from entry A" based on locality.

Continuing to tag ways based on lane markings is no problem, but
unfortunately, we can't reliably resolve good advice for navigation
system users (like "use the inside lane to take the second exit at the
roundabout"), there are too many situations that would generate bad
instructions (instructions that tell users to go into a lane they
shouldn't use).
This commit is contained in:
Daniel J. Hofmann 2016-07-22 18:59:08 +02:00 committed by Patrick Niklaus
parent 1fc63e1e72
commit 748fd3efa9
No known key found for this signature in database
GPG Key ID: E426891B5F978B1B
5 changed files with 44 additions and 63 deletions

View File

@ -314,7 +314,7 @@ Feature: Turn Lane Guidance
| a,f | ab,df,df | depart,roundabout-exit-1,use lane slight right,arrive | ,,slight left:false slight left:false slight right:true, |
@anticipate
Scenario: Anticipate with lanes in roundabout where we stay on the roundabout for multiple exits
Scenario: No Lanes for Roundabouts, see #2626
Given the node map
| | | a | | |
| | | b | | |
@ -340,11 +340,11 @@ Feature: Turn Lane Guidance
| fy | | primary | |
When I route I should get
| waypoints | route | turns | lanes |
| a,h | ab,gh,gh | depart,roundabout-exit-5,arrive | ,slight right:false slight right:true, |
| waypoints | route | turns | lanes |
| a,h | ab,gh,gh | depart,roundabout-exit-5,arrive | ,, |
@anticipate
Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes
Scenario: No Lanes for Roundabouts, see #2626
Given the node map
| | | a | | |
| x | b | | d | y |
@ -360,13 +360,13 @@ Feature: Turn Lane Guidance
| da | | primary | roundabout | roundabout |
When I route I should get
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,slight right:false slight right:true, |
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,, |
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
@anticipate
Scenario: Departing or arriving inside a roundabout does not yet anticipate lanes (BIG version)
Scenario: No Lanes for Roundabouts, see #2626
Given the node map
| | | a | | |
| x | b | | d | y |
@ -416,13 +416,13 @@ Feature: Turn Lane Guidance
| da | | primary | roundabout | roundabout |
When I route I should get
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,slight right:false slight right:true, |
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,slight right:true slight right:true, |
| waypoints | route | turns | lanes |
| x,y | xb,dy,dy | depart,roundabout-exit-1,arrive | ,, |
| x,c | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
| x,a | xb,roundabout,roundabout | depart,roundabout-exit-undefined,arrive | ,, |
@anticipate
Scenario: Anticipate Lanes for turns before and / or after roundabout
Scenario: No Lanes for Roundabouts, see #2626
Given the node map
| a | b | | | x |
| | c | | | |
@ -447,7 +447,7 @@ Feature: Turn Lane Guidance
When I route I should get
| waypoints | route | turns | lanes |
| a,h | abx,bc,fg,gh,gh | depart,turn right,cdefc-exit-2,turn right,arrive | ,straight:false right:false right:false right:false right:true,right:false right:false right:false right:true,straight:false right:true, |
| a,h | abx,bc,fg,gh,gh | depart,turn right,cdefc-exit-2,turn right,arrive | ,straight:false right:false right:false right:false right:true,,straight:false right:true, |
@anticipate @bug @todo
Scenario: Tripple Right keeping Left

View File

@ -150,6 +150,7 @@ class RouteAPI : public BaseAPI
leg_geometry,
phantoms.source_phantom,
phantoms.target_phantom);
leg.steps = guidance::removeLanesFromRoundabouts(std::move(leg.steps));
leg.steps = guidance::anticipateLaneChange(std::move(leg.steps));
leg.steps = guidance::collapseUseLane(std::move(leg.steps));
leg_geometry = guidance::resyncGeometry(std::move(leg_geometry), leg.steps);

View File

@ -20,6 +20,9 @@ namespace guidance
std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
const double min_duration_needed_for_lane_change = 15);
// Remove all lane information from roundabouts. See #2626.
std::vector<RouteStep> removeLanesFromRoundabouts(std::vector<RouteStep> steps);
} // namespace guidance
} // namespace engine
} // namespace osrm

View File

@ -3,6 +3,7 @@
#include "util/guidance/toolkit.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/guidance/toolkit.hpp"
#include <iterator>
@ -104,6 +105,29 @@ std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
return steps;
}
std::vector<RouteStep> removeLanesFromRoundabouts(std::vector<RouteStep> steps)
{
using namespace util::guidance;
const auto removeLanes = [](RouteStep &step) {
for (auto &intersection : step.intersections)
{
intersection.lane_description = {};
intersection.lanes = {};
}
};
for (auto &step : steps)
{
const auto inst = step.maneuver.instruction;
if (entersRoundabout(inst) || staysOnRoundabout(inst) || leavesRoundabout(inst))
removeLanes(step);
}
return steps;
}
} // namespace guidance
} // namespace engine
} // namespace osrm

View File

@ -499,48 +499,6 @@ void collapseTurnAt(std::vector<RouteStep> &steps,
}
}
}
// Works on steps including silent and invalid instructions in order to do lane anticipation for
// roundabouts which later on get collapsed into a single multi-hop instruction.
std::vector<RouteStep> anticipateLaneChangeForRoundabouts(std::vector<RouteStep> steps)
{
using namespace util::guidance;
using StepIter = decltype(steps)::iterator;
using StepIterRange = std::pair<StepIter, StepIter>;
const auto anticipate_lanes_in_roundabout = [&](StepIterRange roundabout) {
// We do lane anticipation on the roundabout's enter and leave step only.
// TODO: This means, lanes _inside_ the roundabout are ignored at the moment.
auto enter = *roundabout.first;
const auto leave = *roundabout.second;
// Although the enter instruction may be a left/right turn, for right-sided driving the
// roundabout is counter-clockwise and therefore we need to always set it to a left turn.
// FIXME: assumes right-side driving (counter-clockwise roundabout flow)
const auto enter_direction = enter.maneuver.instruction.direction_modifier;
if (util::guidance::isRightTurn(enter.maneuver.instruction))
enter.maneuver.instruction.direction_modifier =
mirrorDirectionModifier(enter_direction);
// a roundabout is a continuous maneuver. We don't switch lanes within a roundabout, as long
// as it can be avoided.
auto enterAndLeave =
anticipateLaneChange({enter, leave}, std::numeric_limits<double>::max());
// Undo flipping direction on a right turn in a right-sided counter-clockwise roundabout.
// FIXME: assumes right-side driving (counter-clockwise roundabout flow)
enterAndLeave[0].maneuver.instruction.direction_modifier = enter_direction;
std::swap(*roundabout.first, enterAndLeave[0]);
std::swap(*roundabout.second, enterAndLeave[1]);
};
forEachRoundabout(begin(steps), end(steps), anticipate_lanes_in_roundabout);
return steps;
}
} // namespace
// Post processing can invalidate some instructions. For example StayOnRoundabout
@ -589,11 +547,6 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps)
if (steps.size() == 2)
return steps;
// Before we invalidate and remove silent instructions, we handle roundabouts (before they're
// getting collapsed into a single multi-hop instruction) by back-propagating exit lane
// constraints already to a roundabout's enter instruction.
steps = anticipateLaneChangeForRoundabouts(std::move(steps));
// Count Street Exits forward
bool on_roundabout = false;
bool has_entered_roundabout = false;
@ -1182,7 +1135,7 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
[containsTag](const util::guidance::LaneTupel lanes,
extractor::guidance::TurnLaneDescription lane_description) {
// the lane description is given left to right, lanes are counted from the right.
// Therefore we access the lane description yousing the reverse iterator
// Therefore we access the lane description using the reverse iterator
if (lanes.first_lane_from_the_right > 0 &&
containsTag(*(lane_description.rbegin() + (lanes.first_lane_from_the_right - 1)),
(extractor::guidance::TurnLaneType::straight |