1use std::collections::{BTreeMap, BTreeSet};
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5
6use abstutil::{deserialize_usize, serialize_usize};
7use geom::{Distance, Polygon};
8
9use crate::{
10 osm, CompressedMovementID, DiagonalFilter, DirectedRoadID, IntersectionControl,
11 IntersectionKind, LaneID, Map, Movement, MovementID, PathConstraints, Road, RoadID, RoadSideID,
12 SideOfRoad, Turn, TurnID,
13};
14
15#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
16pub struct IntersectionID(
17 #[serde(
18 serialize_with = "serialize_usize",
19 deserialize_with = "deserialize_usize"
20 )]
21 pub usize,
22);
23
24impl fmt::Display for IntersectionID {
25 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
26 write!(f, "Intersection #{}", self.0)
27 }
28}
29
30#[derive(Serialize, Deserialize, Clone, Debug)]
34pub struct Intersection {
35 pub id: IntersectionID,
36 pub polygon: Polygon,
38 pub turns: Vec<Turn>,
39 pub elevation: Distance,
40
41 pub kind: IntersectionKind,
42 pub control: IntersectionControl,
43 pub orig_id: osm::NodeID,
44
45 pub incoming_lanes: Vec<LaneID>,
50 pub outgoing_lanes: Vec<LaneID>,
51
52 pub roads: Vec<RoadID>,
54
55 pub modal_filter: Option<DiagonalFilter>,
56
57 pub merged: bool,
59 #[serde(skip_serializing, skip_deserializing)]
62 pub movements: BTreeMap<MovementID, Movement>,
63}
64
65impl Intersection {
66 pub fn is_border(&self) -> bool {
67 self.kind == IntersectionKind::MapEdge
68 }
69 pub fn is_incoming_border(&self) -> bool {
70 self.kind == IntersectionKind::MapEdge && !self.outgoing_lanes.is_empty()
71 }
72 pub fn is_outgoing_border(&self) -> bool {
73 self.kind == IntersectionKind::MapEdge && !self.incoming_lanes.is_empty()
74 }
75
76 pub fn is_closed(&self) -> bool {
77 self.control == IntersectionControl::Construction
78 }
79
80 pub fn is_stop_sign(&self) -> bool {
81 self.control == IntersectionControl::Signed
82 }
83
84 pub fn is_traffic_signal(&self) -> bool {
85 self.control == IntersectionControl::Signalled
86 }
87
88 pub fn is_light_rail(&self, map: &Map) -> bool {
89 self.roads.iter().all(|r| map.get_r(*r).is_light_rail())
90 }
91
92 pub fn is_private(&self, map: &Map) -> bool {
93 self.roads.iter().all(|r| map.get_r(*r).is_private())
94 }
95
96 pub fn is_footway(&self, map: &Map) -> bool {
97 self.roads.iter().all(|r| map.get_r(*r).is_footway())
98 }
99
100 pub fn is_cycleway(&self, map: &Map) -> bool {
101 self.roads.iter().all(|r| map.get_r(*r).is_cycleway())
102 }
103
104 pub fn is_degenerate(&self) -> bool {
107 self.roads.len() == 2
108 }
109
110 pub fn is_deadend_for_driving(&self, map: &Map) -> bool {
112 self.roads
114 .iter()
115 .filter(|r| PathConstraints::Car.can_use_road(map.get_r(**r), map))
116 .count()
117 == 1
118 }
119
120 pub fn is_deadend_for_everyone(&self) -> bool {
122 self.roads.len() == 1
123 }
124
125 pub fn get_incoming_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> {
126 self.incoming_lanes
127 .iter()
128 .filter(move |l| constraints.can_use(map.get_l(**l), map))
129 .cloned()
130 .collect()
131 }
132
133 pub fn get_outgoing_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> {
135 constraints.filter_lanes(self.outgoing_lanes.clone(), map)
136 }
137
138 pub fn get_zorder(&self, map: &Map) -> isize {
140 self.roads
143 .iter()
144 .map(|r| map.get_r(*r).zorder)
145 .min()
146 .unwrap()
147 }
148
149 pub fn get_rank(&self, map: &Map) -> osm::RoadRank {
150 self.roads
151 .iter()
152 .map(|r| map.get_r(*r).get_rank())
153 .max()
154 .unwrap()
155 }
156
157 pub fn get_road_sides_sorted(&self, map: &Map) -> Vec<RoadSideID> {
160 let mut deduped = self.roads.clone();
162 deduped.dedup();
163
164 let mut sides = Vec::new();
165 for r in deduped {
166 let r = map.get_r(r);
167 if r.src_i == r.dst_i {
168 if r.center_pts.is_clockwise() {
172 sides.push(RoadSideID {
173 road: r.id,
174 side: SideOfRoad::Left,
175 });
176 } else {
177 sides.push(RoadSideID {
178 road: r.id,
179 side: SideOfRoad::Right,
180 });
181 }
182 continue;
183 }
184 if r.dst_i == self.id {
185 sides.push(RoadSideID {
186 road: r.id,
187 side: SideOfRoad::Right,
188 });
189 sides.push(RoadSideID {
190 road: r.id,
191 side: SideOfRoad::Left,
192 });
193 } else {
194 sides.push(RoadSideID {
195 road: r.id,
196 side: SideOfRoad::Left,
197 });
198 sides.push(RoadSideID {
199 road: r.id,
200 side: SideOfRoad::Right,
201 });
202 }
203 }
204 sides
205 }
206
207 pub fn get_sorted_incoming_roads(&self, map: &Map) -> Vec<RoadID> {
212 let mut roads = self.roads.clone();
213 roads.retain(|r| !map.get_r(*r).incoming_lanes(self.id).is_empty());
214 roads
215 }
216
217 pub fn some_outgoing_road(&self, map: &Map) -> Option<DirectedRoadID> {
218 self.outgoing_lanes
219 .get(0)
220 .map(|l| map.get_l(*l).get_directed_parent())
221 }
222
223 pub fn some_incoming_road(&self, map: &Map) -> Option<DirectedRoadID> {
224 self.incoming_lanes
225 .get(0)
226 .map(|l| map.get_l(*l).get_directed_parent())
227 }
228
229 pub fn name(&self, lang: Option<&String>, map: &Map) -> String {
230 let road_names = self
231 .roads
232 .iter()
233 .map(|r| map.get_r(*r).get_name(lang))
234 .collect::<BTreeSet<_>>();
235 abstutil::plain_list_names(road_names)
236 }
237
238 pub fn turn_to_movement(&self, turn: TurnID) -> (MovementID, CompressedMovementID) {
240 for (idx, m) in self.movements.values().enumerate() {
241 if m.members.contains(&turn) {
242 return (
243 m.id,
244 CompressedMovementID {
245 i: self.id,
246 idx: u8::try_from(idx).unwrap(),
247 },
248 );
249 }
250 }
251
252 panic!(
253 "{} doesn't belong to any movements in {} or is a SharedSidewalkCorner maybe",
254 turn, self.id
255 )
256 }
257
258 pub fn find_road_between<'a>(&self, other: IntersectionID, map: &'a Map) -> Option<&'a Road> {
259 for r in &self.roads {
260 let road = map.get_r(*r);
261 if road.other_endpt(self.id) == other {
262 return Some(road);
263 }
264 }
265 None
266 }
267}