1use std::collections::{BTreeMap, BTreeSet};
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5
6use abstutil::MultiMap;
7use geom::{Angle, Distance, PolyLine, Pt2D};
8
9use crate::edits::perma_traffic_signal;
10use crate::{osm, DirectedRoadID, Direction, IntersectionID, Map, OriginalRoad, TurnID, TurnType};
11
12#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
19pub struct MovementID {
20 pub from: DirectedRoadID,
21 pub to: DirectedRoadID,
22 pub parent: IntersectionID,
23 pub crosswalk: bool,
25}
26
27#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
29pub struct CompressedMovementID {
30 pub i: IntersectionID,
31 pub idx: u8,
33}
34
35#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
39pub struct Movement {
40 pub id: MovementID,
41 pub turn_type: TurnType,
42 pub members: Vec<TurnID>,
43 pub geom: PolyLine,
45 pub angle: Angle,
46}
47
48impl Movement {
49 pub(crate) fn for_i(i: IntersectionID, map: &Map) -> BTreeMap<MovementID, Movement> {
50 let mut results = BTreeMap::new();
51 let mut movements: MultiMap<(DirectedRoadID, DirectedRoadID), TurnID> = MultiMap::new();
52 for turn in &map.get_i(i).turns {
53 let from = map.get_l(turn.id.src).get_directed_parent();
54 let to = map.get_l(turn.id.dst).get_directed_parent();
55 match turn.turn_type {
56 TurnType::SharedSidewalkCorner => {}
57 TurnType::Crosswalk | TurnType::UnmarkedCrossing => {
58 let id = MovementID {
59 from,
60 to,
61 parent: i,
62 crosswalk: true,
63 };
64 results.insert(
65 id,
66 Movement {
67 id,
68 turn_type: turn.turn_type,
69 members: vec![turn.id],
70 geom: turn.geom.clone(),
71 angle: turn.angle(),
72 },
73 );
74 }
75 _ => {
76 movements.insert((from, to), turn.id);
77 }
78 }
79 }
80 for ((from, to), members) in movements.consume() {
81 let geom = match movement_geom(
82 members.iter().map(|t| &map.get_t(*t).geom).collect(),
83 from,
84 to,
85 ) {
86 Ok(geom) => geom,
87 Err(err) => {
88 warn!("Weird movement geometry at {}: {}", i, err);
89 map.get_t(*members.iter().next().unwrap()).geom.clone()
91 }
92 };
93 let turn_types: BTreeSet<TurnType> =
94 members.iter().map(|t| map.get_t(*t).turn_type).collect();
95 if turn_types.len() > 1 {
96 warn!(
97 "Movement between {} and {} has weird turn types! {:?}",
98 from, to, turn_types
99 );
100 }
101 let members: Vec<TurnID> = members.into_iter().collect();
102 let id = MovementID {
103 from,
104 to,
105 parent: i,
106 crosswalk: false,
107 };
108 results.insert(
109 id,
110 Movement {
111 id,
112 turn_type: *turn_types.iter().next().unwrap(),
113 angle: map.get_t(members[0]).angle(),
114 members,
115 geom,
116 },
117 );
118 }
119 results
121 }
122
123 pub fn src_center_and_width(&self, map: &Map) -> (PolyLine, Distance) {
125 let r = map.get_r(self.id.from.road);
126
127 let mut leftmost = Distance::meters(99999.0);
128 let mut rightmost = Distance::ZERO;
129 let mut left = Distance::ZERO;
130
131 for l in &r.lanes {
132 let right = left + l.width;
133
134 if self.members.iter().any(|t| t.src == l.id) {
135 leftmost = leftmost.min(left);
136 rightmost = rightmost.max(right);
137 }
138
139 left = right;
140 }
141
142 let mut pl = r
143 .shift_from_left_side((leftmost + rightmost) / 2.0)
144 .unwrap();
145 if self.id.from.dir == Direction::Back {
146 pl = pl.reversed();
147 }
148 if !self.id.crosswalk || map.get_l(self.members[0].src).src_i != self.members[0].parent {
150 pl = pl.reversed()
151 };
152 (pl, rightmost - leftmost)
153 }
154
155 pub fn conflicts_with(&self, other: &Movement) -> bool {
156 if self.id == other.id {
157 return false;
158 }
159 if self.turn_type.pedestrian_crossing() && other.turn_type.pedestrian_crossing() {
160 return false;
161 }
162
163 if self.id.from == other.id.from
164 && !self.turn_type.pedestrian_crossing()
165 && !other.turn_type.pedestrian_crossing()
166 {
167 return false;
168 }
169 if self.id.to == other.id.to
170 && !self.turn_type.pedestrian_crossing()
171 && !other.turn_type.pedestrian_crossing()
172 {
173 return true;
174 }
175 self.geom.intersection(&other.geom).is_some()
183 }
184}
185
186fn movement_geom(
187 polylines: Vec<&PolyLine>,
188 from: DirectedRoadID,
189 to: DirectedRoadID,
190) -> Result<PolyLine> {
191 let num_pts = polylines[0].points().len();
192 for pl in &polylines {
193 if num_pts != pl.points().len() {
194 if false {
196 warn!(
197 "Movement between {} and {} can't make nice geometry",
198 from, to
199 );
200 }
201 return Ok(polylines[0].clone());
202 }
203 }
204
205 let mut pts = Vec::new();
206 for idx in 0..num_pts {
207 pts.push(Pt2D::center(
208 &polylines
209 .iter()
210 .map(|pl| pl.points()[idx])
211 .collect::<Vec<_>>(),
212 ));
213 }
214 PolyLine::deduping_new(pts)
215}
216
217impl MovementID {
218 pub fn to_permanent(&self, map: &Map) -> perma_traffic_signal::Turn {
219 let from = map.get_r(self.from.road).orig_id;
220 let to = map.get_r(self.to.road).orig_id;
221
222 perma_traffic_signal::Turn {
223 from: perma_traffic_signal::DirectedRoad {
224 osm_way_id: from.osm_way_id.0,
225 osm_node1: from.i1.0,
226 osm_node2: from.i2.0,
227 is_forwards: self.from.dir == Direction::Fwd,
228 },
229 to: perma_traffic_signal::DirectedRoad {
230 osm_way_id: to.osm_way_id.0,
231 osm_node1: to.i1.0,
232 osm_node2: to.i2.0,
233 is_forwards: self.to.dir == Direction::Fwd,
234 },
235 intersection_osm_node_id: map.get_i(self.parent).orig_id.0,
236 is_crosswalk: self.crosswalk,
237 }
238 }
239
240 pub fn from_permanent(id: perma_traffic_signal::Turn, map: &Map) -> Result<MovementID> {
241 Ok(MovementID {
242 from: find_r(id.from, map)?,
243 to: find_r(id.to, map)?,
244 parent: map.find_i_by_osm_id(osm::NodeID(id.intersection_osm_node_id))?,
245 crosswalk: id.is_crosswalk,
246 })
247 }
248}
249
250fn find_r(id: perma_traffic_signal::DirectedRoad, map: &Map) -> Result<DirectedRoadID> {
251 Ok(DirectedRoadID {
252 road: map.find_r_by_osm_id(OriginalRoad::new(
253 id.osm_way_id,
254 (id.osm_node1, id.osm_node2),
255 ))?,
256 dir: if id.is_forwards {
257 Direction::Fwd
258 } else {
259 Direction::Back
260 },
261 })
262}