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}