Improves lane handling for subsequent going straight, resolves #2625
Before we only worked on subsequent quick turns, as in: `right, right` keeps the user on the rightmost lanes. This changeset modifies the logic to work on any subsequent steps that are "quick" and have lane information we can constrain later. Because we do not have a from-lane => to-lanes mapping we take the lanes left and right of the turn lanes into account when heuristically assigning the leftmost / rightmost lanes. There are some edge cases where this still does not give us the optimal solution but it gets close to what is actually possible at the moment without having a lane mapping in post-processing. References: - https://github.com/Project-OSRM/osrm-backend/issues/2625 fix rebase
This commit is contained in:
parent
b6dbf81206
commit
86fd04e556
@ -257,7 +257,170 @@ Feature: Turn Lane Guidance
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,f | abx,bcy,cdz,dew,ef,ef | depart,turn right,turn left,turn right,turn left,arrive | ,straight:false right:false right:true right:false,left:false left:true straight:false,straight:false right:true right:false,left:true straight:false, |
|
||||
| a,f | abx,bcy,cdz,dew,ef,ef | depart,turn right,turn left,turn right,turn left,arrive | ,straight:false right:true right:false right:false,left:true left:false straight:false,straight:false right:true right:false,left:true straight:false, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for through, through with lanes
|
||||
Given the node map
|
||||
| | | | f | g | |
|
||||
| | | | | | |
|
||||
| a | b | c | d | | e |
|
||||
| | | | | | |
|
||||
| | | | h | i | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name |
|
||||
| ab | | main |
|
||||
| bc | left\|through\|through\|through\|right | main |
|
||||
| cd | left\|through\|right | main |
|
||||
| de | | main |
|
||||
| cf | | off |
|
||||
| ch | | off |
|
||||
| dg | | off |
|
||||
| di | | off |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,e | main,main,main,main | depart,use lane straight,use lane straight,arrive | ,left:false straight:false straight:true straight:false right:false,left:false straight:true right:false, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for through and collapse multiple use lanes
|
||||
Given the node map
|
||||
| | | e | f | g |
|
||||
| | | | | |
|
||||
| a | b | c | d | |
|
||||
| | | | | |
|
||||
| | | h | i | j |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name |
|
||||
| ab | left\|through\|through\|right | main |
|
||||
| bc | left\|through\|through\|right | main |
|
||||
| cd | left\|through\|through\|through\|right | main |
|
||||
| be | | off |
|
||||
| bh | | off |
|
||||
| cf | | off |
|
||||
| ci | | off |
|
||||
| dg | | off |
|
||||
| dj | | off |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,c | main,main,main | depart,use lane straight,arrive | ,left:false straight:true straight:true right:false, |
|
||||
| a,d | main,main,main | depart,use lane straight,arrive | ,left:false straight:true straight:true right:false, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for through followed by left/right
|
||||
Given the node map
|
||||
| | | f | g | |
|
||||
| | | | | d |
|
||||
| a | b | c | x | |
|
||||
| | | | | e |
|
||||
| | | h | i | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name |
|
||||
| ab | left\|through\|through\|through\|through\|right | main |
|
||||
| bc | left\|through\|through\|right | main |
|
||||
| cx | left\|right | main |
|
||||
| xd | | left |
|
||||
| xe | | right |
|
||||
| bf | | off |
|
||||
| bh | | off |
|
||||
| cg | | off |
|
||||
| ci | | off |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,d | main,main,main,left,left | depart,use lane straight,use lane straight,turn left,arrive | ,left:false straight:false straight:true straight:false straight:false right:false,left:false straight:true straight:false right:false,left:true right:false, |
|
||||
| a,e | main,main,main,right,right | depart,use lane straight,use lane straight,turn right,arrive | ,left:false straight:false straight:false straight:true straight:false right:false,left:false straight:false straight:true right:false,left:false right:true, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for through with turn before / after
|
||||
Given the node map
|
||||
| a | b | c |
|
||||
| | d | |
|
||||
| f | e | g |
|
||||
| | h | |
|
||||
| j | i | l |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name | oneway |
|
||||
| ab | right\|right\|right\|right | ab | yes |
|
||||
| cb | left\|left\|left\|left | cb | yes |
|
||||
| bd | | bdehi | |
|
||||
| de | left\|left\|through\|through\|through\|through\|right\|right | bdehi | |
|
||||
| ef | | ef | |
|
||||
| eg | | eg | |
|
||||
| eh | | bdehi | |
|
||||
| hi | left\|left\|right\|right | bdehi | |
|
||||
| ij | | ij | |
|
||||
| il | | il | |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes | # |
|
||||
| a,f | ab,bdehi,ef,ef | depart,turn right,turn right,arrive | ,right:false right:false right:true right:true,left:false left:false straight:false straight:false straight:false straight:false right:true right:true, | |
|
||||
| a,g | ab,bdehi,eg,eg | depart,turn right,turn left,arrive | ,right:true right:true right:false right:false,left:true left:true straight:false straight:false straight:false straight:false right:false right:false, | |
|
||||
| a,j | ab,bdehi,bdehi,ij,ij | depart,turn right,use lane straight,turn right,arrive | ,right:true right:true right:false right:false,left:false left:false straight:false straight:false straight:true straight:true right:false right:false,left:false left:false right:true right:true, | |
|
||||
| a,l | ab,bdehi,bdehi,il,il | depart,turn right,use lane straight,turn left,arrive | ,right:false right:false right:true right:true,left:false left:false straight:true straight:true straight:false straight:false right:false right:false,left:true left:true right:false right:false, | not perfect |
|
||||
| c,g | cb,bdehi,eg,eg | depart,turn left,turn left,arrive | ,left:true left:true left:false left:false,left:true left:true straight:false straight:false straight:false straight:false right:false right:false, | |
|
||||
| c,f | cb,bdehi,ef,ef | depart,turn left,turn right,arrive | ,left:false left:false left:true left:true,left:false left:false straight:false straight:false straight:false straight:false right:true right:true, | |
|
||||
| c,l | cb,bdehi,bdehi,il,il | depart,turn left,use lane straight,turn left,arrive | ,left:false left:false left:true left:true,left:false left:false straight:true straight:true straight:false straight:false right:false right:false,left:true left:true right:false right:false, | |
|
||||
| c,j | cb,bdehi,bdehi,ij,ij | depart,turn left,use lane straight,turn right,arrive | ,left:true left:true left:false left:false,left:false left:false straight:false straight:false straight:true straight:true right:false right:false,left:false left:false right:true right:true, | not perfect |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for turns with through before and after
|
||||
Given the node map
|
||||
| a | b | q | | s | h | i |
|
||||
| | | e | f | g | | |
|
||||
| c | d | r | | t | j | k |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name |
|
||||
| ab | through\|right\|right\|right | top |
|
||||
| be | | top |
|
||||
| bq | | off |
|
||||
| ef | left\|through\|through\|through\|through\|right | main |
|
||||
| fg | left\|left\|right\|right | main |
|
||||
| fs | | off |
|
||||
| ft | | off |
|
||||
| gh | | top |
|
||||
| hi | | top |
|
||||
| cd | left\|left\|left\|through | bot |
|
||||
| de | | bot |
|
||||
| dr | | off |
|
||||
| gj | | bot |
|
||||
| jk | | bot |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,i | top,main,main,top,top | depart,turn right,use lane straight,turn left,arrive | ,straight:false right:false right:true right:true,left:false straight:true straight:true straight:false straight:false right:false,left:true left:true right:false right:false, |
|
||||
| a,k | top,main,main,bot,bot | depart,turn right,use lane straight,turn right,arrive | ,straight:false right:true right:true right:false,left:false straight:false straight:false straight:true straight:true right:false,left:false left:false right:true right:true, |
|
||||
| c,i | bot,main,main,top,top | depart,turn left,use lane straight,turn left,arrive | ,left:false left:true left:true straight:false,left:false straight:true straight:true straight:false straight:false right:false,left:true left:true right:false right:false, |
|
||||
| c,k | bot,main,main,bot,bot | depart,turn left,use lane straight,turn right,arrive | ,left:true left:true left:false straight:false,left:false straight:false straight:false straight:true straight:true right:false,left:false left:false right:true right:true, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Anticipate Lanes for turn between throughs
|
||||
Given the node map
|
||||
| | q | | |
|
||||
| a | b | c | s |
|
||||
| | r | d | t |
|
||||
| | | e | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | name |
|
||||
| ab | left\|through\|through\|through\|through\|through\|right | main |
|
||||
| bq | | off |
|
||||
| br | | off |
|
||||
| bc | through\|through\|right\|right\|right | main |
|
||||
| cs | | off |
|
||||
| cd | left\|through\|through | main |
|
||||
| de | | main |
|
||||
| dt | | off |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,e | main,main,main,main,main | depart,use lane straight,continue right,use lane straight,arrive | ,left:false straight:false straight:false straight:false straight:true straight:true right:false,straight:false straight:false right:false right:true right:true,left:false straight:true straight:true, |
|
||||
|
||||
@anticipate @todo @bug @2661
|
||||
Scenario: Anticipate with lanes in roundabout: roundabouts as the unit of anticipation
|
||||
@ -331,7 +494,7 @@ Feature: Turn Lane Guidance
|
||||
| cd | | primary | roundabout |
|
||||
| de | | primary | roundabout |
|
||||
| ef | | primary | roundabout |
|
||||
| fg | slight_right | primary | roundabout |
|
||||
| fg | through\|slight_right | primary | roundabout |
|
||||
| gb | | primary | roundabout |
|
||||
| gh | | primary | |
|
||||
| cx | | primary | |
|
||||
@ -480,7 +643,30 @@ Feature: Turn Lane Guidance
|
||||
| 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,,straight:false right:true, |
|
||||
|
||||
@anticipate @bug @todo
|
||||
@anticipate
|
||||
Scenario: Anticipate none tags
|
||||
Given the node map
|
||||
| a | b | c |
|
||||
| | d | |
|
||||
| f | e | g |
|
||||
| | h | |
|
||||
|
||||
And the ways
|
||||
| nodes | turn:lanes:forward | highway | name |
|
||||
| ab | none\|none\|right\|right | primary | abc |
|
||||
| bc | | primary | abc |
|
||||
| bd | | primary | bdeh |
|
||||
| de | left\|none\|none\|right | primary | bdeh |
|
||||
| eh | | primary | bdeh |
|
||||
| ef | | primary | feg |
|
||||
| eg | | primary | feg |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,g | abc,bdeh,feg,feg | depart,turn right,turn left,arrive | ,none:false none:false right:true right:false,left:true none:false none:false right:false, |
|
||||
| a,f | abc,bdeh,feg,feg | depart,turn right,turn right,arrive | ,none:false none:false right:false right:true,left:false none:false none:false right:true, |
|
||||
|
||||
@anticipate
|
||||
Scenario: Tripple Right keeping Left
|
||||
Given the node map
|
||||
| a | | | | b | | i |
|
||||
@ -501,11 +687,11 @@ Feature: Turn Lane Guidance
|
||||
| feg | | tertiary | fourth |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,f | start,first,second,third,fourth,fourth | depart,turn right,turn right,turn right,end of road left,arrive | ,none:false none:true right:false right:false,none:false none:true right:false right:false,none:false none:true right:false right:false,left:true right:false right:false, |
|
||||
| a,g | start,first,second,third,fourth,fourth | depart,turn right,turn right,turn right,end of road right,arrive | ,none:false none:false right:true right:true,none:false none:false right:true right:true,none:false none:false right:true right:true,left:false right:true right:true, |
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,f | start,first,second,third,fourth,fourth | depart,turn right,turn right,turn right,turn left,arrive | ,none:false none:false right:true right:false,none:false none:false right:true right:false,none:false none:false right:true right:false,left:true right:false right:false, |
|
||||
| a,g | start,first,second,third,fourth,fourth | depart,turn right,turn right,turn right,turn right,arrive | ,none:false none:false right:true right:true,none:false none:false right:true right:true,none:false none:false right:true right:true,left:false right:true right:true, |
|
||||
|
||||
@anticipate @bug @todo
|
||||
@anticipate
|
||||
Scenario: Tripple Left keeping Right
|
||||
Given the node map
|
||||
| i | | b | | | | a |
|
||||
@ -526,6 +712,6 @@ Feature: Turn Lane Guidance
|
||||
| feg | | tertiary | fourth |
|
||||
|
||||
When I route I should get
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,f | start,first,second,third,fourth,fourth | depart,turn left,turn left,turn left,end of road right,arrive | ,left:false left:false none:true none:false,left:false left:false none:true none:false,left:false left:false none:true none:false,left:false left:false right:true, |
|
||||
| a,g | start,first,second,third,fourth,fourth | depart,turn left,turn left,turn left,end of road left,arrive | ,left:true left:true none:false none:false,left:true left:true none:false none:false,left:true left:true none:false none:false,left:true left:true right:false, |
|
||||
| waypoints | route | turns | lanes |
|
||||
| a,f | start,first,second,third,fourth,fourth | depart,turn left,turn left,turn left,turn right,arrive | ,left:false left:true none:false none:false,left:false left:true none:false none:false,left:false left:true none:false none:false,left:false left:false right:true, |
|
||||
| a,g | start,first,second,third,fourth,fourth | depart,turn left,turn left,turn left,turn left,arrive | ,left:true left:true none:false none:false,left:true left:true none:false none:false,left:true left:true none:false none:false,left:true left:true right:false, |
|
||||
|
@ -15,7 +15,6 @@ namespace engine
|
||||
{
|
||||
namespace guidance
|
||||
{
|
||||
|
||||
// passed as none-reference to modify in-place and move out again
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
|
||||
@ -28,6 +27,14 @@ std::vector<RouteStep> postProcess(std::vector<RouteStep> steps);
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
std::vector<RouteStep> collapseTurns(std::vector<RouteStep> steps);
|
||||
|
||||
// A check whether two instructions can be treated as one. This is only the case for very short
|
||||
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
|
||||
bool collapsable(const RouteStep &step, const RouteStep &next);
|
||||
|
||||
// Elongate a step by another. the data is added either at the front, or the back
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
RouteStep elongate(RouteStep step, const RouteStep &by_step);
|
||||
|
||||
// trim initial/final segment of very short length.
|
||||
// This function uses in/out parameter passing to modify both steps and geometry in place.
|
||||
// We use this method since both steps and geometry are closely coupled logically but
|
||||
|
@ -307,8 +307,11 @@ inline bool hasRoundaboutType(const TurnInstruction instruction)
|
||||
TurnType::EnterRoundaboutIntersectionAtExit,
|
||||
TurnType::ExitRoundaboutIntersection,
|
||||
TurnType::StayOnRoundabout};
|
||||
const auto valid_end = valid_types + 13;
|
||||
return std::find(valid_types, valid_end, instruction.type) != valid_end;
|
||||
|
||||
const auto *first = valid_types;
|
||||
const auto *last = first + sizeof(valid_types) / sizeof(valid_types[0]);
|
||||
|
||||
return std::find(first, last, instruction.type) != last;
|
||||
}
|
||||
|
||||
// Public service vehicle lanes and similar can introduce additional lanes into the lane string that
|
||||
|
@ -29,15 +29,15 @@ inline void print(const engine::guidance::RouteStep &step)
|
||||
|
||||
for (const auto &intersection : step.intersections)
|
||||
{
|
||||
std::cout << "(bearings:";
|
||||
std::cout << "(Lanes: " << static_cast<int>(intersection.lanes.lanes_in_turn) << " "
|
||||
<< static_cast<int>(intersection.lanes.first_lane_from_the_right)
|
||||
<< " bearings:";
|
||||
for (auto bearing : intersection.bearings)
|
||||
std::cout << " " << bearing;
|
||||
std::cout << ", entry: ";
|
||||
for (auto entry : intersection.entry)
|
||||
std::cout << " " << (entry ? "true" : "false");
|
||||
const auto lanes = intersection.lanes;
|
||||
std::cout<< " Lanes: (" << static_cast<int>(lanes.lanes_in_turn) << ", "
|
||||
<< static_cast<int>(lanes.first_lane_from_the_right) << "))";
|
||||
std::cout << ")";
|
||||
}
|
||||
std::cout << "] name[" << step.name_id << "]: " << step.name;
|
||||
}
|
||||
|
@ -4,8 +4,11 @@
|
||||
|
||||
#include "extractor/guidance/turn_instruction.hpp"
|
||||
#include "engine/guidance/toolkit.hpp"
|
||||
#include "engine/guidance/post_processing.hpp"
|
||||
|
||||
#include <iterator>
|
||||
#include <unordered_set>
|
||||
#include <utility>
|
||||
|
||||
using TurnInstruction = osrm::extractor::guidance::TurnInstruction;
|
||||
namespace TurnType = osrm::extractor::guidance::TurnType;
|
||||
@ -24,47 +27,45 @@ namespace guidance
|
||||
std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
|
||||
const double min_duration_needed_for_lane_change)
|
||||
{
|
||||
// Postprocessing does not strictly guarantee for only turns
|
||||
const auto is_turn = [](const RouteStep &step) {
|
||||
return step.maneuver.instruction.type != TurnType::NewName &&
|
||||
step.maneuver.instruction.type != TurnType::Notification;
|
||||
// Lane anticipation works on contiguous ranges of quick steps that have lane information
|
||||
const auto is_quick_has_lanes = [&](const RouteStep &step) {
|
||||
const auto is_quick = step.duration < min_duration_needed_for_lane_change;
|
||||
const auto has_lanes = step.intersections.front().lanes.lanes_in_turn > 0;
|
||||
return has_lanes && is_quick;
|
||||
};
|
||||
|
||||
const auto is_quick = [min_duration_needed_for_lane_change](const RouteStep &step) {
|
||||
return step.duration < min_duration_needed_for_lane_change;
|
||||
};
|
||||
|
||||
const auto is_quick_turn = [&](const RouteStep &step) {
|
||||
return is_turn(step) && is_quick(step);
|
||||
};
|
||||
|
||||
// Determine range of subsequent quick turns, candidates for possible lane anticipation
|
||||
using StepIter = decltype(steps)::iterator;
|
||||
using StepIterRange = std::pair<StepIter, StepIter>;
|
||||
|
||||
std::vector<StepIterRange> subsequent_quick_turns;
|
||||
std::vector<StepIterRange> quick_lanes_ranges;
|
||||
|
||||
const auto keep_turn_range = [&](StepIterRange range) {
|
||||
const auto range_back_inserter = [&](StepIterRange range) {
|
||||
if (std::distance(range.first, range.second) > 1)
|
||||
subsequent_quick_turns.push_back(std::move(range));
|
||||
quick_lanes_ranges.push_back(std::move(range));
|
||||
};
|
||||
|
||||
util::group_by(begin(steps), end(steps), is_quick_turn, keep_turn_range);
|
||||
util::group_by(begin(steps), end(steps), is_quick_has_lanes, range_back_inserter);
|
||||
|
||||
// The lanes for a keep straight depend on the next left/right turn. Tag them in advance.
|
||||
std::unordered_set<const RouteStep *> is_straight_left;
|
||||
std::unordered_set<const RouteStep *> is_straight_right;
|
||||
|
||||
// Walk backwards over all turns, constraining possible turn lanes.
|
||||
// Later turn lanes constrain earlier ones: we have to anticipate lane changes.
|
||||
const auto constrain_lanes = [](const StepIterRange &turns) {
|
||||
const auto constrain_lanes = [&](const StepIterRange &turns) {
|
||||
const std::reverse_iterator<StepIter> rev_first{turns.second};
|
||||
const std::reverse_iterator<StepIter> rev_last{turns.first};
|
||||
|
||||
// We're walking backwards over all adjacent turns:
|
||||
// the current turn lanes constrain the lanes we have to take in the previous turn.
|
||||
util::for_each_pair(rev_first, rev_last, [](RouteStep ¤t, RouteStep &previous) {
|
||||
util::for_each_pair(rev_first, rev_last, [&](RouteStep ¤t, RouteStep &previous) {
|
||||
const auto current_inst = current.maneuver.instruction;
|
||||
const auto current_lanes = current.intersections.front().lanes;
|
||||
|
||||
// Constrain the previous turn's lanes
|
||||
auto &previous_lanes = previous.intersections.front().lanes;
|
||||
const auto previous_inst = previous.maneuver.instruction;
|
||||
|
||||
// Lane mapping (N:M) from previous lanes (N) to current lanes (M), with:
|
||||
// N > M, N > 1 fan-in situation, constrain N lanes to min(N,M) shared lanes
|
||||
// otherwise nothing to constrain
|
||||
@ -74,34 +75,113 @@ std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
|
||||
if (!lanes_to_constrain || !lanes_fan_in)
|
||||
return;
|
||||
|
||||
// In case there is no lane information we work with one artificial lane
|
||||
const auto current_adjusted_lanes = std::max(current_lanes.lanes_in_turn, LaneID{1});
|
||||
// We do not have a mapping from lanes to lanes. All we have is the lanes in the turn
|
||||
// and all the lanes at that situation. To perfectly handle lane anticipation in cases
|
||||
// where lanes in the turn fan in but for example the overall lanes at that location
|
||||
// fan out, we would have to know the asymmetric mapping of lanes. This is currently
|
||||
// not possible at the moment. In the following we implement a heuristic instead.
|
||||
const LaneID current_num_all_lanes = current.intersections.front().lane_description.size();
|
||||
const LaneID current_num_lanes_right_of_turn = current_lanes.first_lane_from_the_right;
|
||||
const LaneID current_num_lanes_left_of_turn =
|
||||
current_num_all_lanes -
|
||||
(current_lanes.lanes_in_turn + current_num_lanes_right_of_turn);
|
||||
|
||||
const auto num_shared_lanes = std::min(current_adjusted_lanes, //
|
||||
previous_lanes.lanes_in_turn);
|
||||
const LaneID num_shared_lanes = std::min(current_lanes.lanes_in_turn, //
|
||||
previous_lanes.lanes_in_turn); //
|
||||
|
||||
if (isRightTurn(current_inst))
|
||||
// 0/ Tag keep straight with the next turn's direction if available
|
||||
const auto previous_is_straight =
|
||||
!isLeftTurn(previous_inst) && !isRightTurn(previous_inst);
|
||||
|
||||
if (previous_is_straight)
|
||||
{
|
||||
if (isLeftTurn(current_inst) || is_straight_left.count(¤t) > 0)
|
||||
is_straight_left.insert(&previous);
|
||||
else if (isRightTurn(current_inst) || is_straight_right.count(¤t) > 0)
|
||||
is_straight_right.insert(&previous);
|
||||
}
|
||||
|
||||
// 1/ How to anticipate left, right:
|
||||
const auto anticipate_for_left_turn = [&] {
|
||||
// Current turn is left turn, already keep left during previous turn.
|
||||
// This implies constraining the rightmost lanes in previous step.
|
||||
LaneID new_first_lane_from_the_right =
|
||||
previous_lanes.first_lane_from_the_right // start from rightmost lane
|
||||
+ previous_lanes.lanes_in_turn // one past leftmost lane
|
||||
- num_shared_lanes; // back number of new lanes
|
||||
|
||||
// The leftmost target lanes might not be involved in the turn. Figure out
|
||||
// how many lanes are to the left and not in the turn.
|
||||
new_first_lane_from_the_right -=
|
||||
std::min(current_num_lanes_left_of_turn, num_shared_lanes);
|
||||
|
||||
previous_lanes = {num_shared_lanes, new_first_lane_from_the_right};
|
||||
};
|
||||
|
||||
const auto anticipate_for_right_turn = [&] {
|
||||
// Current turn is right turn, already keep right during the previous turn.
|
||||
// This implies constraining the leftmost lanes in the previous turn step.
|
||||
previous_lanes = {num_shared_lanes, previous_lanes.first_lane_from_the_right};
|
||||
}
|
||||
else if (isLeftTurn(current_inst))
|
||||
{
|
||||
// Current turn is left turn, already keep left during previous turn.
|
||||
// This implies constraining the rightmost lanes in the previous turn step.
|
||||
const LaneID shared_lane_delta = previous_lanes.lanes_in_turn - num_shared_lanes;
|
||||
const LaneID previous_adjusted_lanes =
|
||||
std::min(current_adjusted_lanes, shared_lane_delta);
|
||||
const LaneID constraint_first_lane_from_the_right =
|
||||
previous_lanes.first_lane_from_the_right + previous_adjusted_lanes;
|
||||
LaneID new_first_lane_from_the_right = previous_lanes.first_lane_from_the_right;
|
||||
|
||||
previous_lanes = {num_shared_lanes, constraint_first_lane_from_the_right};
|
||||
// The rightmost target lanes might not be involved in the turn. Figure out
|
||||
// how many lanes are to the right and not in the turn.
|
||||
new_first_lane_from_the_right +=
|
||||
std::min(current_num_lanes_right_of_turn, num_shared_lanes);
|
||||
|
||||
previous_lanes = {num_shared_lanes, new_first_lane_from_the_right};
|
||||
};
|
||||
|
||||
// 2/ When to anticipate a left, right turn
|
||||
if (isLeftTurn(current_inst))
|
||||
anticipate_for_left_turn();
|
||||
else if (isRightTurn(current_inst))
|
||||
anticipate_for_right_turn();
|
||||
else // keepStraight
|
||||
{
|
||||
// Heuristic: we do not have a from-lanes -> to-lanes mapping. What we use
|
||||
// here instead in addition is the number of all lanes (not only the lanes
|
||||
// in a turn):
|
||||
//
|
||||
// -v-v v-v- straight follows
|
||||
// | | | |
|
||||
// <- v v -> keep straight here
|
||||
// | |
|
||||
// <-| |->
|
||||
//
|
||||
// A route from the top left to the bottom right here goes over a keep
|
||||
// straight. If we handle all keep straights as right turns (in right-sided
|
||||
// driving), we wrongly guide the user to the rightmost lanes in the first turn.
|
||||
// Not only is this wrong but the opposite of what we expect.
|
||||
//
|
||||
// The following implements a heuristic to determine a keep straight's
|
||||
// direction in relation to the next step. In the above example we would get:
|
||||
//
|
||||
// coming from right, going to left (in direction of way) -> handle as left turn
|
||||
|
||||
if (is_straight_left.count(¤t) > 0)
|
||||
anticipate_for_left_turn();
|
||||
else if (is_straight_right.count(¤t) > 0)
|
||||
anticipate_for_right_turn();
|
||||
else // FIXME: right-sided driving
|
||||
anticipate_for_right_turn();
|
||||
}
|
||||
|
||||
// We might have constrained the previous step in a way that makes it compatible
|
||||
// with the current step. If we did so we collapse it here and mark the current
|
||||
// step as invalid, scheduled for later removal.
|
||||
if (collapsable(previous, current))
|
||||
{
|
||||
previous = elongate(previous, current);
|
||||
current.maneuver.instruction = TurnInstruction::NO_TURN();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
std::for_each(begin(subsequent_quick_turns), end(subsequent_quick_turns), constrain_lanes);
|
||||
std::for_each(begin(quick_lanes_ranges), end(quick_lanes_ranges), constrain_lanes);
|
||||
|
||||
// Lane Anticipation might have collapsed steps after constraining lanes. Remove invalid steps.
|
||||
steps = removeNoTurnInstructions(std::move(steps));
|
||||
|
||||
return steps;
|
||||
}
|
||||
|
||||
|
@ -69,27 +69,6 @@ bool isCollapsableInstruction(const TurnInstruction instruction)
|
||||
(instruction.type == TurnType::Merge);
|
||||
}
|
||||
|
||||
// A check whether two instructions can be treated as one. This is only the case for very short
|
||||
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
|
||||
bool collapsable(const RouteStep &step, const RouteStep &next)
|
||||
{
|
||||
const auto is_short_step = step.distance < MAX_COLLAPSE_DISTANCE;
|
||||
const auto instruction_can_be_collapsed = isCollapsableInstruction(step.maneuver.instruction);
|
||||
|
||||
const auto is_use_lane = step.maneuver.instruction.type == TurnType::UseLane;
|
||||
const auto lanes_dont_change =
|
||||
step.intersections.front().lanes == next.intersections.front().lanes;
|
||||
|
||||
if (is_short_step && instruction_can_be_collapsed)
|
||||
return true;
|
||||
|
||||
// Prevent collapsing away important lane change steps
|
||||
if (is_short_step && is_use_lane && lanes_dont_change)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool compatible(const RouteStep &lhs, const RouteStep &rhs) { return lhs.mode == rhs.mode; }
|
||||
|
||||
double nameSegmentLength(std::size_t at, const std::vector<RouteStep> &steps)
|
||||
@ -336,40 +315,6 @@ void closeOffRoundabout(const bool on_roundabout,
|
||||
}
|
||||
}
|
||||
|
||||
// elongate a step by another. the data is added either at the front, or the back
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
RouteStep elongate(RouteStep step, const RouteStep &by_step)
|
||||
{
|
||||
step.duration += by_step.duration;
|
||||
step.distance += by_step.distance;
|
||||
|
||||
// by_step comes after step -> we append at the end
|
||||
if (step.geometry_end == by_step.geometry_begin + 1)
|
||||
{
|
||||
step.geometry_end = by_step.geometry_end;
|
||||
|
||||
// if we elongate in the back, we only need to copy the intersections to the beginning.
|
||||
// the bearings remain the same, as the location of the turn doesn't change
|
||||
step.intersections.insert(
|
||||
step.intersections.end(), by_step.intersections.begin(), by_step.intersections.end());
|
||||
}
|
||||
// by_step comes before step -> we append at the front
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(step.maneuver.waypoint_type == WaypointType::None &&
|
||||
by_step.maneuver.waypoint_type == WaypointType::None);
|
||||
BOOST_ASSERT(by_step.geometry_end == step.geometry_begin + 1);
|
||||
step.geometry_begin = by_step.geometry_begin;
|
||||
|
||||
// elongating in the front changes the location of the maneuver
|
||||
step.maneuver = by_step.maneuver;
|
||||
|
||||
step.intersections.insert(
|
||||
step.intersections.begin(), by_step.intersections.begin(), by_step.intersections.end());
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
void collapseTurnAt(std::vector<RouteStep> &steps,
|
||||
const std::size_t two_back_index,
|
||||
const std::size_t one_back_index,
|
||||
@ -571,8 +516,66 @@ bool isStaggeredIntersection(const RouteStep &previous, const RouteStep ¤t
|
||||
|
||||
} // namespace
|
||||
|
||||
// elongate a step by another. the data is added either at the front, or the back
|
||||
OSRM_ATTR_WARN_UNUSED
|
||||
RouteStep elongate(RouteStep step, const RouteStep &by_step)
|
||||
{
|
||||
step.duration += by_step.duration;
|
||||
step.distance += by_step.distance;
|
||||
|
||||
// by_step comes after step -> we append at the end
|
||||
if (step.geometry_end == by_step.geometry_begin + 1)
|
||||
{
|
||||
step.geometry_end = by_step.geometry_end;
|
||||
|
||||
// if we elongate in the back, we only need to copy the intersections to the beginning.
|
||||
// the bearings remain the same, as the location of the turn doesn't change
|
||||
step.intersections.insert(
|
||||
step.intersections.end(), by_step.intersections.begin(), by_step.intersections.end());
|
||||
}
|
||||
// by_step comes before step -> we append at the front
|
||||
else
|
||||
{
|
||||
BOOST_ASSERT(step.maneuver.waypoint_type == WaypointType::None &&
|
||||
by_step.maneuver.waypoint_type == WaypointType::None);
|
||||
BOOST_ASSERT(by_step.geometry_end == step.geometry_begin + 1);
|
||||
step.geometry_begin = by_step.geometry_begin;
|
||||
|
||||
// elongating in the front changes the location of the maneuver
|
||||
step.maneuver = by_step.maneuver;
|
||||
|
||||
step.intersections.insert(
|
||||
step.intersections.begin(), by_step.intersections.begin(), by_step.intersections.end());
|
||||
}
|
||||
return step;
|
||||
}
|
||||
|
||||
|
||||
|
||||
// Post processing can invalidate some instructions. For example StayOnRoundabout
|
||||
// is turned into exit counts. These instructions are removed by the following function
|
||||
|
||||
// A check whether two instructions can be treated as one. This is only the case for very short
|
||||
// maneuvers that can, in some form, be seen as one. Lookahead of one step.
|
||||
bool collapsable(const RouteStep &step, const RouteStep &next)
|
||||
{
|
||||
const auto is_short_step = step.distance < MAX_COLLAPSE_DISTANCE;
|
||||
const auto instruction_can_be_collapsed = isCollapsableInstruction(step.maneuver.instruction);
|
||||
|
||||
const auto is_use_lane = step.maneuver.instruction.type == TurnType::UseLane;
|
||||
const auto lanes_dont_change =
|
||||
step.intersections.front().lanes == next.intersections.front().lanes;
|
||||
|
||||
if (is_short_step && instruction_can_be_collapsed)
|
||||
return true;
|
||||
|
||||
// Prevent collapsing away important lane change steps
|
||||
if (is_short_step && is_use_lane && lanes_dont_change)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::vector<RouteStep> removeNoTurnInstructions(std::vector<RouteStep> steps)
|
||||
{
|
||||
// finally clean up the post-processed instructions.
|
||||
@ -1199,7 +1202,7 @@ std::vector<RouteStep> buildIntersections(std::vector<RouteStep> steps)
|
||||
{
|
||||
// count intersections. We cannot use exit, since intersections can follow directly
|
||||
// after a roundabout
|
||||
steps[last_valid_instruction] = elongate(steps[last_valid_instruction], step);
|
||||
steps[last_valid_instruction] = elongate(std::move(steps[last_valid_instruction]), step);
|
||||
step.maneuver.instruction = TurnInstruction::NO_TURN();
|
||||
}
|
||||
else if (!isSilent(instruction))
|
||||
@ -1273,7 +1276,7 @@ std::vector<RouteStep> collapseUseLane(std::vector<RouteStep> steps)
|
||||
step.intersections.front().lane_description))
|
||||
{
|
||||
const auto previous = getPreviousIndex(step_index);
|
||||
steps[previous] = elongate(steps[previous], steps[step_index]);
|
||||
steps[previous] = elongate(std::move(steps[previous]), steps[step_index]);
|
||||
// elongate(steps[step_index-1], steps[step_index]);
|
||||
invalidateStep(steps[step_index]);
|
||||
}
|
||||
|
@ -139,7 +139,7 @@ typename Intersection::const_iterator findBestMatch(const TurnLaneType::Mask tag
|
||||
// possible that it is forbidden. In addition, the best u-turn angle does not necessarily represent
|
||||
// the u-turn, since it could be a sharp-left turn instead on a road with a middle island.
|
||||
typename Intersection::const_iterator
|
||||
findBestMatchForReverse(const TurnLaneType::Mask &neighbor_tag, const Intersection &intersection)
|
||||
findBestMatchForReverse(const TurnLaneType::Mask neighbor_tag, const Intersection &intersection)
|
||||
{
|
||||
const auto neighbor_itr = findBestMatch(neighbor_tag, intersection);
|
||||
if (neighbor_itr + 1 == intersection.cend())
|
||||
|
Loading…
Reference in New Issue
Block a user