diff --git a/features/guidance/anticipate-lanes.feature b/features/guidance/anticipate-lanes.feature index cf0a9a16f..07c9b7350 100644 --- a/features/guidance/anticipate-lanes.feature +++ b/features/guidance/anticipate-lanes.feature @@ -818,3 +818,37 @@ Feature: Turn Lane Guidance When I route I should get | waypoints | route | turns | lanes | | a,e | MySt,MySt,MySt,MySt | depart,continue right,turn right,arrive | ,straight:false straight:false right:false right:true,left:false right:true, | + + @anticipate + Scenario: Don't Overdo It + Given the node map + """ + q r s t u v + | | | | | | + a - - - - - - - - - - b - - - - - - - - - - c - - - - - - - - - - d - - - - - - - - - - e - - - - - - - - - - f - - - - - - - - - - g - h - i + | | | | | | | + p o n m l k j + """ + + And the ways + | nodes | name | turn:lanes:forward | oneway | + | ab | road | left\|\|\| | yes | + | bc | road | left\|\|\| | yes | + | cd | road | left\|\|\| | yes | + | de | road | left\|\|\| | yes | + | ef | road | left\|\|\| | yes | + | fg | road | left\|\|\| | yes | + | gh | road | \|\|right | yes | + | hi | road | | yes | + | qbp | 1st | | no | + | rco | 2nd | | no | + | sdn | 3rd | | no | + | tem | 4th | | no | + | ufl | 5th | | no | + | vgk | 6th | | no | + | hj | 7th | | no | + + When I route I should get + | waypoints | route | turns | locations | lanes | + | a,i | road,road,road | depart,use lane straight,arrive | a,g,i | ,left:false none:true none:true none:false, | + | a,j | road,road,7th,7th | depart,use lane straight,turn right,arrive | a,f,h,j | ,left:false none:false none:false none:true,none:false none:false right:true, | diff --git a/include/engine/guidance/lane_processing.hpp b/include/engine/guidance/lane_processing.hpp index f2f8fb383..f7b1d31b4 100644 --- a/include/engine/guidance/lane_processing.hpp +++ b/include/engine/guidance/lane_processing.hpp @@ -20,7 +20,7 @@ namespace guidance // as separate maneuvers. OSRM_ATTR_WARN_UNUSED std::vector anticipateLaneChange(std::vector steps, - const double min_duration_needed_for_lane_change = 15); + const double min_duration_needed_for_lane_change = 10); } // namespace guidance } // namespace engine diff --git a/include/engine/guidance/post_processing.hpp b/include/engine/guidance/post_processing.hpp index 97bcbc903..1c1d680a6 100644 --- a/include/engine/guidance/post_processing.hpp +++ b/include/engine/guidance/post_processing.hpp @@ -28,6 +28,7 @@ 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. +// This is only a pre-check and does not necessarily allow collapsing turns!!! bool collapsable(const RouteStep &step, const RouteStep &next); // trim initial/final segment of very short length. diff --git a/src/engine/guidance/lane_processing.cpp b/src/engine/guidance/lane_processing.cpp index a7b49bf5b..e2cc0d478 100644 --- a/src/engine/guidance/lane_processing.cpp +++ b/src/engine/guidance/lane_processing.cpp @@ -74,6 +74,15 @@ std::vector anticipateLaneChange(std::vector steps, // We're walking backwards over all adjacent turns: // the current turn lanes constrain the lanes we have to take in the previous turn. + + // state for the lamda + // the number of lanes we have to change depends on the number of lanes that are allowed for + // a turn (in general) and the set of lanes which would allow for us to do the turn without + // a problem. In a sequence of turns, we have to look at how much time we need to switch the + // sequence. Given the turns in between, we would expect a bit longer than on a straight + // segment for a lane switch, but the total time shouldn't be unlimited. + double time_to_constrained = 0.0; + 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; @@ -88,7 +97,18 @@ std::vector anticipateLaneChange(std::vector steps, const bool lanes_to_constrain = previous_lanes.lanes_in_turn > 1; const bool lanes_fan_in = previous_lanes.lanes_in_turn > current_lanes.lanes_in_turn; - if (!lanes_to_constrain || !lanes_fan_in) + // only prevent use lanes due to making all turns. don't make turns during curvy + // segments + if (previous_inst.type == TurnType::UseLane) + time_to_constrained += previous.duration; + else + time_to_constrained = 0; + + const auto lane_delta = previous_lanes.lanes_in_turn - current_lanes.lanes_in_turn; + const auto can_make_all_turns = + time_to_constrained > lane_delta * min_duration_needed_for_lane_change; + + if (!lanes_to_constrain || !lanes_fan_in || can_make_all_turns) return; // We do not have a mapping from lanes to lanes. All we have is the lanes in the turn @@ -99,9 +119,6 @@ std::vector anticipateLaneChange(std::vector steps, const LaneID current_num_lanes_right_of_turn = current.NumLanesToTheRight(); const LaneID current_num_lanes_left_of_turn = current.NumLanesToTheLeft(); - const LaneID num_shared_lanes = std::min(current_lanes.lanes_in_turn, // - previous_lanes.lanes_in_turn); // - // 0/ Tag keep straight with the next turn's direction if available const auto previous_is_straight = !isLeftTurn(previous_inst) && !isRightTurn(previous_inst); @@ -121,14 +138,14 @@ std::vector anticipateLaneChange(std::vector steps, 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 + - current_lanes.lanes_in_turn; // 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); + std::min(current_num_lanes_left_of_turn, current_lanes.lanes_in_turn); - previous_lanes = {num_shared_lanes, new_first_lane_from_the_right}; + previous_lanes = {current_lanes.lanes_in_turn, new_first_lane_from_the_right}; }; const auto anticipate_for_right_turn = [&] { @@ -139,9 +156,9 @@ std::vector anticipateLaneChange(std::vector steps, // 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); + std::min(current_num_lanes_right_of_turn, current_lanes.lanes_in_turn); - previous_lanes = {num_shared_lanes, new_first_lane_from_the_right}; + previous_lanes = {current_lanes.lanes_in_turn, new_first_lane_from_the_right}; }; // 2/ When to anticipate a left, right turn @@ -179,13 +196,11 @@ std::vector anticipateLaneChange(std::vector steps, 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)) + if (previous_inst.type == TurnType::UseLane && current_inst.type == TurnType::UseLane && + previous.mode == current.mode && previous_lanes == current_lanes) { previous.ElongateBy(current); - current.maneuver.instruction = TurnInstruction::NO_TURN(); + current.Invalidate(); } }); };