map_model/objects/turn.rs
1use std::fmt;
2
3use serde::{Deserialize, Serialize};
4
5use geom::{Angle, Line, PolyLine};
6
7use crate::{
8 DirectedRoadID, Direction, Intersection, IntersectionID, LaneID, Map, MovementID,
9 PathConstraints, RestrictionType,
10};
11
12/// Turns are uniquely identified by their (src, dst) lanes and their parent intersection.
13/// Intersection is needed to distinguish crosswalks that exist at two ends of a sidewalk.
14#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
15pub struct TurnID {
16 pub parent: IntersectionID,
17 /// src and dst must both belong to parent. No guarantees that src is incoming and dst is
18 /// outgoing for turns between sidewalks.
19 pub src: LaneID,
20 pub dst: LaneID,
21}
22
23impl fmt::Display for TurnID {
24 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
25 write!(f, "TurnID({}, {}, {})", self.src, self.dst, self.parent)
26 }
27}
28
29#[derive(Clone, Copy, Debug, Eq, PartialOrd, Ord, PartialEq, Serialize, Deserialize)]
30pub enum TurnType {
31 /// A marked zebra crossing, where pedestrians usually have priority
32 Crosswalk,
33 /// The corner where two sidewalks meet. Pedestrians can cross this without conflicting with
34 /// any vehicle traffic
35 SharedSidewalkCorner,
36 // These are for vehicle turns
37 Straight,
38 Right,
39 Left,
40 UTurn,
41 /// An unmarked crossing, where pedestrians may cross without priority over vehicles
42 // TODO During the next map regeneration, sort this list to be next to crosswalk. I want to
43 // avoid binary incompatibility in the meantime.
44 UnmarkedCrossing,
45}
46
47impl TurnType {
48 /// Is the turn a crosswalk or unmarked crossing?
49 pub fn pedestrian_crossing(self) -> bool {
50 self == TurnType::Crosswalk || self == TurnType::UnmarkedCrossing
51 }
52}
53
54// TODO This concept may be dated, now that Movements exist. Within a movement, the lane-changing
55// turns should be treated as less important.
56#[derive(Serialize, Deserialize, Debug, PartialEq, Clone, Copy, PartialOrd)]
57pub enum TurnPriority {
58 /// For stop signs: Can't currently specify this!
59 /// For traffic signals: Can't do this turn right now.
60 Banned,
61 /// For stop signs: cars have to stop before doing this turn, and are accepted with the lowest
62 /// priority.
63 /// For traffic signals: Cars can do this immediately if there are no previously accepted
64 /// conflicting turns.
65 Yield,
66 /// For stop signs: cars can do this without stopping. These can conflict!
67 /// For traffic signals: Must be non-conflicting.
68 Protected,
69}
70
71/// A Turn leads from the end of one Lane to the start of another. (Except for pedestrians;
72/// sidewalks are bidirectional.)
73#[derive(Serialize, Deserialize, Debug, Clone, PartialEq)]
74pub struct Turn {
75 pub id: TurnID,
76 pub turn_type: TurnType,
77 // TODO Some turns might not actually have geometry. Currently encoded by two equal points.
78 // Represent more directly?
79 pub geom: PolyLine,
80}
81
82impl Turn {
83 pub fn conflicts_with(&self, other: &Turn) -> bool {
84 if self.turn_type == TurnType::SharedSidewalkCorner
85 || other.turn_type == TurnType::SharedSidewalkCorner
86 {
87 return false;
88 }
89 if self.id == other.id {
90 return false;
91 }
92 if self.between_sidewalks() && other.between_sidewalks() {
93 return false;
94 }
95
96 if self.geom.first_pt() == other.geom.first_pt() {
97 return false;
98 }
99 if self.geom.last_pt() == other.geom.last_pt() {
100 return true;
101 }
102 self.geom.intersection(&other.geom).is_some()
103 }
104
105 // The relative angle of the turn, should be the angle from the src lane to the dst lane, but
106 // instead uses the first and last lines of the turn geometry, which is currently not quite the
107 // same angle as between the source and destination lanes
108 pub fn angle(&self) -> Angle {
109 if self.geom.points().len() < 3 {
110 return Angle::ZERO;
111 }
112
113 self.geom
114 .last_line()
115 .angle()
116 .shortest_rotation_towards(self.geom.first_line().angle())
117 }
118
119 pub fn between_sidewalks(&self) -> bool {
120 self.turn_type == TurnType::SharedSidewalkCorner
121 || self.turn_type == TurnType::Crosswalk
122 || self.turn_type == TurnType::UnmarkedCrossing
123 }
124
125 // TODO Maybe precompute this.
126 /// Penalties for (lane types, lane-changing, slow lane). The penalty may depend on the vehicle
127 /// performing the turn. Lower means preferable.
128 pub fn penalty(&self, constraints: PathConstraints, map: &Map) -> (usize, usize, usize) {
129 let from = map.get_l(self.id.src);
130 let to = map.get_l(self.id.dst);
131
132 // Starting from the farthest from the center line (right in the US), where is this travel
133 // lane? Filters by the lane type and ignores lanes that don't go to the target road.
134 let from_idx = {
135 let mut cnt = 0;
136 let r = map.get_r(from.id.road);
137 for (l, lt) in r.children(from.dir).iter().rev() {
138 if from.lane_type != *lt {
139 continue;
140 }
141 if map
142 .get_turns_from_lane(*l)
143 .into_iter()
144 .any(|t| t.id.dst.road == to.id.road)
145 {
146 cnt += 1;
147 if from.id == *l {
148 break;
149 }
150 }
151 }
152 cnt
153 };
154
155 // Starting from the farthest from the center line (right in the US), where is this travel
156 // lane? Filters by the lane type.
157 let to_idx = {
158 let mut cnt = 0;
159 let r = map.get_r(to.id.road);
160 for (l, lt) in r.children(to.dir).iter().rev() {
161 if to.lane_type != *lt {
162 continue;
163 }
164 cnt += 1;
165 if to.id == *l {
166 break;
167 }
168 }
169 cnt
170 };
171
172 // TODO I thought about different cases where there are the same/more/less lanes going in
173 // and out, but then actually, I think the reasonable thing in all cases is just to do
174 // this.
175 let lc_cost = ((from_idx as isize) - (to_idx as isize)).abs() as usize;
176
177 // If we're a bike, prefer bike lanes, then bus lanes. If we're a bus, prefer bus lanes.
178 // Otherwise, avoid special lanes, even if we're allowed to use them sometimes because they
179 // happen to double as turn lanes.
180 let lt_cost = if constraints == PathConstraints::Bike {
181 if to.is_biking() {
182 0
183 } else if to.is_bus() {
184 1
185 } else {
186 2
187 }
188 } else if constraints == PathConstraints::Bus {
189 if to.is_bus() {
190 0
191 } else {
192 1
193 }
194 } else if to.is_bus() {
195 // Cars should stay out of bus lanes unless it's required to make a turn
196 3
197 } else {
198 0
199 };
200
201 // Keep right (in the US)
202 let slow_lane = if to_idx > 1 { 1 } else { 0 };
203
204 (lt_cost, lc_cost, slow_lane)
205 }
206
207 pub fn is_crossing_arterial_intersection(&self, map: &Map) -> bool {
208 use crate::osm::RoadRank;
209 if !self.turn_type.pedestrian_crossing() {
210 return false;
211 }
212 // Distance-only metric has many false positives and negatives
213 // return turn.geom.length() > Distance::feet(41.0);
214
215 let intersection = map.get_i(self.id.parent);
216 intersection.roads.iter().any(|r| {
217 let rank = map.get_r(*r).get_rank();
218 rank == RoadRank::Arterial || rank == RoadRank::Highway
219 })
220 }
221
222 /// Is this turn legal, according to turn lane tagging?
223 pub(crate) fn permitted_by_lane(&self, map: &Map) -> bool {
224 if let Some(types) = map
225 .get_l(self.id.src)
226 .get_lane_level_turn_restrictions(map.get_parent(self.id.src), false)
227 {
228 types.contains(&self.turn_type)
229 } else {
230 true
231 }
232 }
233
234 /// Is this turn legal, according to turn restrictions defined between road segments?
235 pub(crate) fn permitted_by_road(&self, i: &Intersection, map: &Map) -> bool {
236 if self.between_sidewalks() {
237 return true;
238 }
239
240 let src = map.get_parent(self.id.src);
241 let dst = self.id.dst.road;
242
243 for (restriction, to) in &src.turn_restrictions {
244 // The restriction only applies to one direction of the road.
245 if !i.roads.contains(to) {
246 continue;
247 }
248 match restriction {
249 RestrictionType::BanTurns => {
250 if dst == *to {
251 return false;
252 }
253 }
254 RestrictionType::OnlyAllowTurns => {
255 if dst != *to {
256 return false;
257 }
258 }
259 }
260 }
261
262 true
263 }
264
265 /// If this turn is a crosswalk over a single road, return that road and which end of the road
266 /// is crossed.
267 pub fn crosswalk_over_road(&self, map: &Map) -> Option<DirectedRoadID> {
268 if !self.turn_type.pedestrian_crossing() {
269 return None;
270 }
271 // We cross multiple roads
272 if self.id.src.road != self.id.dst.road {
273 return None;
274 }
275 Some(DirectedRoadID {
276 road: self.id.src.road,
277 dir: if map.get_r(self.id.src.road).dst_i == self.id.parent {
278 Direction::Fwd
279 } else {
280 Direction::Back
281 },
282 })
283 }
284
285 /// Only appropriat for pedestrian crossings. The geometry of crosswalks will first cross part
286 /// of a sidewalk corner, then actually enter the road. Extract the piece that's in the road.
287 pub fn crosswalk_line(&self) -> Option<Line> {
288 let pts = self.geom.points();
289 if pts.len() < 3 {
290 warn!("Crosswalk {} was squished earlier", self.id);
291 return None;
292 }
293 Line::new(pts[1], pts[2]).ok()
294 }
295}
296
297impl TurnID {
298 pub fn to_movement(self, map: &Map) -> MovementID {
299 MovementID {
300 from: map.get_l(self.src).get_directed_parent(),
301 to: map.get_l(self.dst).get_directed_parent(),
302 parent: self.parent,
303 crosswalk: map.get_l(self.src).is_walkable(),
304 }
305 }
306}