1 use crate::{osm, DirectedRoadID, LaneID, Map, PathConstraints, Road, RoadID, TurnID};
2 use abstutil::{deserialize_usize, serialize_usize};
3 use geom::{Distance, Polygon};
4 use serde::{Deserialize, Serialize};
5 use std::collections::BTreeSet;
6 use std::fmt;
7 
8 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
9 pub struct IntersectionID(
10     #[serde(
11         serialize_with = "serialize_usize",
12         deserialize_with = "deserialize_usize"
13     )]
14     pub usize,
15 );
16 
17 impl fmt::Display for IntersectionID {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result18     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
19         write!(f, "Intersection #{}", self.0)
20     }
21 }
22 
23 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
24 pub enum IntersectionType {
25     StopSign,
26     TrafficSignal,
27     Border,
28     Construction,
29 }
30 
31 #[derive(Serialize, Deserialize, Debug)]
32 pub struct Intersection {
33     pub id: IntersectionID,
34     // This needs to be in clockwise orientation, or later rendering of sidewalk corners breaks.
35     pub polygon: Polygon,
36     pub turns: BTreeSet<TurnID>,
37     pub elevation: Distance,
38 
39     pub intersection_type: IntersectionType,
40     pub orig_id: osm::NodeID,
41 
42     // Note that a lane may belong to both incoming_lanes and outgoing_lanes.
43     // TODO narrow down when and why. is it just sidewalks in weird cases?
44     // TODO Change to BTreeSet, or otherwise emphasize to callers that the order of these isn't
45     // meaningful
46     pub incoming_lanes: Vec<LaneID>,
47     pub outgoing_lanes: Vec<LaneID>,
48 
49     // TODO Maybe DirectedRoadIDs
50     pub roads: BTreeSet<RoadID>,
51 }
52 
53 impl Intersection {
is_border(&self) -> bool54     pub fn is_border(&self) -> bool {
55         self.intersection_type == IntersectionType::Border
56     }
is_incoming_border(&self) -> bool57     pub fn is_incoming_border(&self) -> bool {
58         self.intersection_type == IntersectionType::Border && !self.outgoing_lanes.is_empty()
59     }
is_outgoing_border(&self) -> bool60     pub fn is_outgoing_border(&self) -> bool {
61         self.intersection_type == IntersectionType::Border && !self.incoming_lanes.is_empty()
62     }
63 
is_closed(&self) -> bool64     pub fn is_closed(&self) -> bool {
65         self.intersection_type == IntersectionType::Construction
66     }
67 
is_stop_sign(&self) -> bool68     pub fn is_stop_sign(&self) -> bool {
69         self.intersection_type == IntersectionType::StopSign
70     }
71 
is_traffic_signal(&self) -> bool72     pub fn is_traffic_signal(&self) -> bool {
73         self.intersection_type == IntersectionType::TrafficSignal
74     }
75 
is_light_rail(&self, map: &Map) -> bool76     pub fn is_light_rail(&self, map: &Map) -> bool {
77         self.roads.iter().all(|r| map.get_r(*r).is_light_rail())
78     }
79 
is_private(&self, map: &Map) -> bool80     pub fn is_private(&self, map: &Map) -> bool {
81         self.roads.iter().all(|r| map.get_r(*r).is_private())
82     }
83 
get_incoming_lanes<'a>( &'a self, map: &'a Map, constraints: PathConstraints, ) -> impl Iterator<Item = LaneID> + 'a84     pub fn get_incoming_lanes<'a>(
85         &'a self,
86         map: &'a Map,
87         constraints: PathConstraints,
88     ) -> impl Iterator<Item = LaneID> + 'a {
89         self.incoming_lanes
90             .iter()
91             .filter(move |l| constraints.can_use(map.get_l(**l), map))
92             .cloned()
93     }
94 
95     // Strict for bikes. If there are bike lanes, not allowed to use other lanes.
get_outgoing_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID>96     pub fn get_outgoing_lanes(&self, map: &Map, constraints: PathConstraints) -> Vec<LaneID> {
97         constraints.filter_lanes(self.outgoing_lanes.clone(), map)
98     }
99 
get_zorder(&self, map: &Map) -> isize100     pub fn get_zorder(&self, map: &Map) -> isize {
101         // TODO Not sure min makes sense -- what about a 1 and a 0? Prefer the nonzeros. If there's
102         // a -1 and a 1... need to see it to know what to do.
103         self.roads
104             .iter()
105             .map(|r| map.get_r(*r).zorder)
106             .min()
107             .unwrap()
108     }
109 
get_rank(&self, map: &Map) -> osm::RoadRank110     pub fn get_rank(&self, map: &Map) -> osm::RoadRank {
111         self.roads
112             .iter()
113             .map(|r| map.get_r(*r).get_rank())
114             .max()
115             .unwrap()
116     }
117 
get_roads_sorted_by_incoming_angle(&self, all_roads: &Vec<Road>) -> Vec<RoadID>118     pub(crate) fn get_roads_sorted_by_incoming_angle(&self, all_roads: &Vec<Road>) -> Vec<RoadID> {
119         let center = self.polygon.center();
120         let mut roads: Vec<RoadID> = self.roads.iter().cloned().collect();
121         roads.sort_by_key(|id| {
122             let r = &all_roads[id.0];
123             let endpt = if r.src_i == self.id {
124                 r.center_pts.first_pt()
125             } else if r.dst_i == self.id {
126                 r.center_pts.last_pt()
127             } else {
128                 unreachable!();
129             };
130             endpt.angle_to(center).normalized_degrees() as i64
131         });
132         roads
133     }
134 
some_outgoing_road(&self, map: &Map) -> Option<DirectedRoadID>135     pub fn some_outgoing_road(&self, map: &Map) -> Option<DirectedRoadID> {
136         self.outgoing_lanes
137             .get(0)
138             .map(|l| map.get_l(*l).get_directed_parent(map))
139     }
140 
some_incoming_road(&self, map: &Map) -> Option<DirectedRoadID>141     pub fn some_incoming_road(&self, map: &Map) -> Option<DirectedRoadID> {
142         self.incoming_lanes
143             .get(0)
144             .map(|l| map.get_l(*l).get_directed_parent(map))
145     }
146 
name(&self, lang: Option<&String>, map: &Map) -> String147     pub fn name(&self, lang: Option<&String>, map: &Map) -> String {
148         let road_names = self
149             .roads
150             .iter()
151             .map(|r| map.get_r(*r).get_name(lang))
152             .collect::<BTreeSet<_>>();
153         abstutil::plain_list_names(road_names)
154     }
155 }
156