-
Notifications
You must be signed in to change notification settings - Fork 3.4k
/
lane_processing.cpp
217 lines (179 loc) · 10.3 KB
/
lane_processing.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
#include "util/for_each_pair.hpp"
#include "util/group_by.hpp"
#include "extractor/guidance/turn_instruction.hpp"
#include "engine/guidance/collapsing_utility.hpp"
#include <algorithm>
#include <iterator>
#include <unordered_set>
#include <utility>
using osrm::extractor::guidance::TurnInstruction;
using osrm::extractor::guidance::isLeftTurn;
using osrm::extractor::guidance::isRightTurn;
namespace osrm
{
namespace engine
{
namespace guidance
{
std::vector<RouteStep> anticipateLaneChange(std::vector<RouteStep> steps,
const double min_distance_needed_for_lane_change)
{
// Lane anticipation works on contiguous ranges of short steps that have lane information
const auto is_short_has_lanes = [&](const RouteStep &step) {
const auto has_lanes = step.intersections.front().lanes.lanes_in_turn > 0;
if (!has_lanes)
return false;
// The more unused lanes to the left and right of the turn there are, the higher
// the chance the user is driving on one of those and has to cross lanes.
// Scale threshold for these cases to be adaptive to the situation's complexity.
//
// Note: what we could do instead: do Lane Anticipation on all step pairs and then scale
// the threshold based on the lanes we're constraining the user to. Would need a re-write
// since at the moment we first group-by and only then do Lane Anticipation selectively.
//
// We do not have a source-target lane mapping, assume worst case for lanes to cross.
const auto to_cross = std::max(step.NumLanesToTheRight(), step.NumLanesToTheLeft());
const auto scale = 1 + to_cross;
const auto threshold = scale * min_distance_needed_for_lane_change;
const auto is_short = step.distance < threshold;
return is_short;
};
using StepIter = decltype(steps)::iterator;
using StepIterRange = std::pair<StepIter, StepIter>;
std::vector<StepIterRange> quick_lanes_ranges;
const auto range_back_inserter = [&](StepIterRange range) {
if (std::distance(range.first, range.second) > 1)
quick_lanes_ranges.push_back(std::move(range));
};
util::group_by(begin(steps), end(steps), is_short_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 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.
// 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 distance 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 distance shouldn't be unlimited.
double distance_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;
// 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
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;
// only prevent use lanes due to making all turns. don't make turns during curvy
// segments
if (previous_inst.type == TurnType::Suppressed)
distance_to_constrained += previous.distance;
else
distance_to_constrained = 0.;
const auto lane_delta = previous_lanes.lanes_in_turn - current_lanes.lanes_in_turn;
const auto can_make_all_turns =
distance_to_constrained > lane_delta * min_distance_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
// 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_lanes_right_of_turn = current.NumLanesToTheRight();
const LaneID current_num_lanes_left_of_turn = current.NumLanesToTheLeft();
// 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
- 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, current_lanes.lanes_in_turn);
previous_lanes = {current_lanes.lanes_in_turn, 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.
LaneID new_first_lane_from_the_right = previous_lanes.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, current_lanes.lanes_in_turn);
previous_lanes = {current_lanes.lanes_in_turn, 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();
}
if (previous_inst.type == TurnType::Suppressed &&
current_inst.type == TurnType::Suppressed && previous.mode == current.mode &&
previous_lanes == current_lanes)
{
previous.ElongateBy(current);
current.Invalidate();
}
});
};
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;
}
} // namespace guidance
} // namespace engine
} // namespace osrm