diff --git a/features/guidance/anticipate-lanes.feature b/features/guidance/anticipate-lanes.feature index 7b127755d..c9822c5be 100644 --- a/features/guidance/anticipate-lanes.feature +++ b/features/guidance/anticipate-lanes.feature @@ -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, | diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp index d5437900a..b33694c0c 100644 --- a/include/engine/guidance/post_processing.hpp +++ b/include/engine/guidance/post_processing.hpp @@ -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 postProcess(std::vector steps); @@ -28,6 +27,14 @@ std::vector postProcess(std::vector steps); OSRM_ATTR_WARN_UNUSED std::vector collapseTurns(std::vector 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 diff --git a/include/extractor/guidance/toolkit.hpp b/include/extractor/guidance/toolkit.hpp index 7df23d434..4d7dc2e09 100644 --- a/include/extractor/guidance/toolkit.hpp +++ b/include/extractor/guidance/toolkit.hpp @@ -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 diff --git a/include/util/debug.hpp b/include/util/debug.hpp index a35267349..32083e411 100644 --- a/include/util/debug.hpp +++ b/include/util/debug.hpp @@ -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(intersection.lanes.lanes_in_turn) << " " + << static_cast(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(lanes.lanes_in_turn) << ", " - << static_cast(lanes.first_lane_from_the_right) << "))"; + std::cout << ")"; } std::cout << "] name[" << step.name_id << "]: " << step.name; } diff --git a/src/engine/guidance/lane_processing.cpp b/src/engine/guidance/lane_processing.cpp index 5213fc6d8..51d478973 100644 --- a/src/engine/guidance/lane_processing.cpp +++ b/src/engine/guidance/lane_processing.cpp @@ -4,8 +4,11 @@ #include "extractor/guidance/turn_instruction.hpp" #include "engine/guidance/toolkit.hpp" +#include "engine/guidance/post_processing.hpp" #include +#include +#include using TurnInstruction = osrm::extractor::guidance::TurnInstruction; namespace TurnType = osrm::extractor::guidance::TurnType; @@ -24,47 +27,45 @@ namespace guidance std::vector anticipateLaneChange(std::vector 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; - std::vector subsequent_quick_turns; + std::vector 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 is_straight_left; + std::unordered_set 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 rev_first{turns.second}; const std::reverse_iterator 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 anticipateLaneChange(std::vector 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; } diff --git a/src/engine/guidance/post_processing.cpp b/src/engine/guidance/post_processing.cpp index ef864079b..b7cc5ef52 100644 --- a/src/engine/guidance/post_processing.cpp +++ b/src/engine/guidance/post_processing.cpp @@ -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 &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 &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 removeNoTurnInstructions(std::vector steps) { // finally clean up the post-processed instructions. @@ -1199,7 +1202,7 @@ std::vector buildIntersections(std::vector 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 collapseUseLane(std::vector 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]); } diff --git a/src/extractor/guidance/turn_lane_matcher.cpp b/src/extractor/guidance/turn_lane_matcher.cpp index d8af0fd7d..19c916e9d 100644 --- a/src/extractor/guidance/turn_lane_matcher.cpp +++ b/src/extractor/guidance/turn_lane_matcher.cpp @@ -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())