1use std::collections::{BTreeMap, BTreeSet};
2
3use anyhow::Result;
4use serde::{Deserialize, Serialize};
5
6use geom::{Distance, Duration, Speed};
7
8use crate::edits::perma_traffic_signal;
9use crate::make::traffic_signals::get_possible_policies;
10use crate::{
11 Intersection, IntersectionID, Map, Movement, MovementID, RoadID, TurnID, TurnPriority,
12};
13
14const CROSSWALK_PACE: Speed = Speed::const_meters_per_second(1.4);
17
18#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
22pub struct ControlTrafficSignal {
23 pub id: IntersectionID,
24 pub stages: Vec<Stage>,
25 pub offset: Duration,
26}
27
28#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
29pub struct Stage {
30 pub protected_movements: BTreeSet<MovementID>,
31 pub yield_movements: BTreeSet<MovementID>,
32 pub stage_type: StageType,
35}
36
37#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
38pub enum StageType {
39 Fixed(Duration),
40 Variable(Duration, Duration, Duration),
44}
45
46impl StageType {
47 pub fn simple_duration(&self) -> Duration {
49 match self {
50 StageType::Fixed(d) => *d,
51 StageType::Variable(duration, _, _) => *duration,
52 }
53 }
54}
55
56impl ControlTrafficSignal {
57 pub fn new(map: &Map, id: IntersectionID) -> ControlTrafficSignal {
58 let mut policies = ControlTrafficSignal::get_possible_policies(map, id);
59 if policies.len() == 1 {
60 warn!("Falling back to greedy_assignment for {}", id);
61 }
62 policies.remove(0).1
63 }
64
65 pub fn get_possible_policies(
66 map: &Map,
67 id: IntersectionID,
68 ) -> Vec<(String, ControlTrafficSignal)> {
69 get_possible_policies(map, id)
70 }
71
72 pub fn get_min_crossing_time(&self, idx: usize, i: &Intersection) -> Duration {
73 let mut max_distance = Distance::meters(0.0);
74 for movement in &self.stages[idx].protected_movements {
75 if movement.crosswalk {
76 max_distance = max_distance.max(i.movements[movement].geom.length());
77 }
78 }
79 let time = max_distance / CROSSWALK_PACE;
80 assert!(time >= Duration::ZERO);
81 Duration::seconds(time.inner_seconds().ceil())
83 }
84
85 pub fn validate(&self, i: &Intersection) -> Result<()> {
86 let expected_movements: BTreeSet<MovementID> = i.movements.keys().cloned().collect();
88 let mut actual_movements: BTreeSet<MovementID> = BTreeSet::new();
89 for stage in &self.stages {
90 actual_movements.extend(stage.protected_movements.iter());
91 actual_movements.extend(stage.yield_movements.iter());
92 }
93 if expected_movements != actual_movements {
94 bail!(
95 "Traffic signal assignment for {} broken. Missing {:?}, contains irrelevant {:?}",
96 self.id,
97 expected_movements
98 .difference(&actual_movements)
99 .cloned()
100 .collect::<Vec<_>>(),
101 actual_movements
102 .difference(&expected_movements)
103 .cloned()
104 .collect::<Vec<_>>()
105 );
106 }
107 for (stage_index, stage) in self.stages.iter().enumerate() {
108 for m1 in stage.protected_movements.iter().map(|m| &i.movements[m]) {
110 for m2 in stage.protected_movements.iter().map(|m| &i.movements[m]) {
111 if m1.conflicts_with(m2) {
112 bail!(
113 "Traffic signal has conflicting protected movements in one \
114 stage:\n{:?}\n\n{:?}",
115 m1,
116 m2
117 );
118 }
119 }
120 }
121
122 for m in stage.yield_movements.iter().map(|m| &i.movements[m]) {
124 assert!(!m.turn_type.pedestrian_crossing())
126 }
127 let min_crossing_time = self.get_min_crossing_time(stage_index, i);
129 if stage.stage_type.simple_duration() < min_crossing_time {
130 bail!(
131 "Traffic signal does not allow enough time in stage to complete the \
132 crosswalk\nStage Index{}\nStage : {:?}\nTime Required: {}\nTime Given: {}",
133 stage_index,
134 stage,
135 min_crossing_time,
136 stage.stage_type.simple_duration()
137 );
138 }
139 }
140 Ok(())
141 }
142
143 pub fn convert_to_ped_scramble(&mut self, i: &Intersection) -> bool {
146 self.internal_convert_to_ped_scramble(true, i)
147 }
148 pub fn convert_to_ped_scramble_without_promotion(&mut self, i: &Intersection) -> bool {
151 self.internal_convert_to_ped_scramble(false, i)
152 }
153
154 fn internal_convert_to_ped_scramble(
155 &mut self,
156 promote_yield_to_protected: bool,
157 i: &Intersection,
158 ) -> bool {
159 let orig = self.clone();
160
161 let mut all_walk_stage = Stage::new();
162 for m in i.movements.values() {
163 if m.turn_type.pedestrian_crossing() {
164 all_walk_stage.edit_movement(m, TurnPriority::Protected);
165 }
166 }
167
168 let mut replaced = std::mem::take(&mut self.stages);
170 let mut has_all_walk = false;
171 for stage in replaced.iter_mut() {
172 if !has_all_walk && stage == &all_walk_stage {
173 has_all_walk = true;
174 continue;
175 }
176
177 stage
179 .protected_movements
180 .retain(|m| !i.movements[m].turn_type.pedestrian_crossing());
181 if promote_yield_to_protected {
182 let mut promoted = Vec::new();
185 for m in &stage.yield_movements {
186 if stage.could_be_protected(*m, i) {
187 stage.protected_movements.insert(*m);
188 promoted.push(*m);
189 }
190 }
191 for m in promoted {
192 stage.yield_movements.remove(&m);
193 }
194 }
195 }
196 self.stages = replaced;
197
198 if !has_all_walk {
199 self.stages.push(all_walk_stage);
200 }
201 self != &orig
202 }
203
204 pub fn adjust_major_minor_timing(
209 &mut self,
210 major: Duration,
211 minor: Duration,
212 map: &Map,
213 ) -> Result<()> {
214 if self.stages.len() != 2 {
215 bail!("This intersection doesn't have 2 stages.");
216 }
217
218 let mut rank_per_road: BTreeMap<RoadID, usize> = BTreeMap::new();
220 for r in &map.get_i(self.id).roads {
221 rank_per_road.insert(*r, map.get_r(*r).get_detailed_rank());
222 }
223 let mut ranks: Vec<usize> = rank_per_road.values().cloned().collect();
224 ranks.sort_unstable();
225 ranks.dedup();
226 if ranks.len() == 1 {
227 bail!("This intersection doesn't have major/minor roads; they're all the same rank.");
228 }
229 let highest_rank = ranks.pop().unwrap();
230
231 let orig = self.clone();
233 for stage in &mut self.stages {
234 match stage.stage_type {
235 StageType::Fixed(_) => {}
236 _ => bail!("This intersection doesn't use fixed timing."),
237 }
238 if stage
240 .protected_movements
241 .iter()
242 .any(|m| !m.crosswalk && highest_rank == rank_per_road[&m.from.road])
243 {
244 stage.stage_type = StageType::Fixed(major);
245 } else {
246 stage.stage_type = StageType::Fixed(minor);
247 }
248 }
249
250 if self.simple_cycle_duration() != major + minor {
251 bail!("This intersection didn't already group major/minor roads together.");
252 }
253
254 if self == &orig {
255 bail!("This change had no effect.");
256 }
257
258 Ok(())
259 }
260
261 pub fn missing_turns(&self, i: &Intersection) -> BTreeSet<MovementID> {
262 let mut missing: BTreeSet<MovementID> = i.movements.keys().cloned().collect();
263 for stage in &self.stages {
264 for m in &stage.protected_movements {
265 missing.remove(m);
266 }
267 for m in &stage.yield_movements {
268 missing.remove(m);
269 }
270 }
271 missing
272 }
273
274 pub fn simple_cycle_duration(&self) -> Duration {
276 let mut total = Duration::ZERO;
277 for s in &self.stages {
278 total += s.stage_type.simple_duration();
279 }
280 total
281 }
282}
283
284impl Stage {
285 pub fn new() -> Stage {
286 Stage {
287 protected_movements: BTreeSet::new(),
288 yield_movements: BTreeSet::new(),
289 stage_type: StageType::Fixed(Duration::seconds(30.0)),
291 }
292 }
293
294 pub fn could_be_protected(&self, m1: MovementID, i: &Intersection) -> bool {
295 let movement1 = &i.movements[&m1];
296 for m2 in &self.protected_movements {
297 if m1 == *m2 || movement1.conflicts_with(&i.movements[m2]) {
298 return false;
299 }
300 }
301 true
302 }
303
304 pub fn get_priority_of_turn(&self, t: TurnID, i: &Intersection) -> TurnPriority {
305 self.get_priority_of_movement(i.turn_to_movement(t).0)
306 }
307
308 pub fn get_priority_of_movement(&self, m: MovementID) -> TurnPriority {
309 if self.protected_movements.contains(&m) {
310 TurnPriority::Protected
311 } else if self.yield_movements.contains(&m) {
312 TurnPriority::Yield
313 } else {
314 TurnPriority::Banned
315 }
316 }
317
318 pub fn edit_movement(&mut self, g: &Movement, pri: TurnPriority) {
319 if g.turn_type.pedestrian_crossing() {
320 self.enforce_minimum_crosswalk_time(g);
321 }
322 self.protected_movements.remove(&g.id);
323 self.yield_movements.remove(&g.id);
324 if pri == TurnPriority::Protected {
325 self.protected_movements.insert(g.id);
326 } else if pri == TurnPriority::Yield {
327 self.yield_movements.insert(g.id);
328 }
329 }
330 pub fn enforce_minimum_crosswalk_time(&mut self, movement: &Movement) {
331 let time = Duration::seconds(
333 (movement.geom.length() / CROSSWALK_PACE)
334 .inner_seconds()
335 .ceil(),
336 );
337 if time > self.stage_type.simple_duration() {
338 self.stage_type = match self.stage_type {
339 StageType::Fixed(_) => StageType::Fixed(time),
340 StageType::Variable(_, delay, additional) => {
341 StageType::Variable(time, delay, additional)
342 }
343 };
344 }
345 }
346
347 pub fn max_crosswalk_time(&self, i: &Intersection) -> Option<Duration> {
349 let mut max_distance = Distance::const_meters(0.0);
350 for m in &self.protected_movements {
351 if m.crosswalk {
352 max_distance = max_distance.max(i.movements[m].geom.length());
353 } else {
354 return None;
355 }
356 }
357 if max_distance > Distance::const_meters(0.0) {
358 let time = max_distance / CROSSWALK_PACE;
359 assert!(time >= Duration::ZERO);
360 Some(Duration::seconds(time.inner_seconds().ceil()))
362 } else {
363 None
364 }
365 }
366}
367
368impl ControlTrafficSignal {
369 pub fn export(&self, map: &Map) -> perma_traffic_signal::TrafficSignal {
370 perma_traffic_signal::TrafficSignal {
371 intersection_osm_node_id: map.get_i(self.id).orig_id.0,
372 plans: vec![perma_traffic_signal::Plan {
373 start_time_seconds: 0,
374 stages: self
375 .stages
376 .iter()
377 .map(|s| perma_traffic_signal::Stage {
378 protected_turns: s
379 .protected_movements
380 .iter()
381 .map(|mvmnt| mvmnt.to_permanent(map))
382 .collect(),
383 permitted_turns: s
384 .yield_movements
385 .iter()
386 .map(|mvmnt| mvmnt.to_permanent(map))
387 .collect(),
388 stage_type: match s.stage_type {
389 StageType::Fixed(d) => {
390 perma_traffic_signal::StageType::Fixed(d.inner_seconds() as usize)
391 }
392 StageType::Variable(min, delay, additional) => {
393 perma_traffic_signal::StageType::Variable(
394 min.inner_seconds() as usize,
395 delay.inner_seconds() as usize,
396 additional.inner_seconds() as usize,
397 )
398 }
399 },
400 })
401 .collect(),
402 offset_seconds: self.offset.inner_seconds() as usize,
403 }],
404 }
405 }
406
407 pub(crate) fn import(
408 mut raw: perma_traffic_signal::TrafficSignal,
409 id: IntersectionID,
410 map: &Map,
411 ) -> Result<ControlTrafficSignal> {
412 let plan = raw.plans.remove(0);
414 let mut stages = Vec::new();
415 for s in plan.stages {
416 let mut errors = Vec::new();
417 let mut protected_movements = BTreeSet::new();
418 for t in s.protected_turns {
419 match MovementID::from_permanent(t, map) {
420 Ok(mvmnt) => {
421 protected_movements.insert(mvmnt);
422 }
423 Err(err) => {
424 errors.push(err.to_string());
425 }
426 }
427 }
428 let mut permitted_movements = BTreeSet::new();
429 for t in s.permitted_turns {
430 match MovementID::from_permanent(t, map) {
431 Ok(mvmnt) => {
432 permitted_movements.insert(mvmnt);
433 }
434 Err(err) => {
435 errors.push(err.to_string());
436 }
437 }
438 }
439 if errors.is_empty() {
440 stages.push(Stage {
441 protected_movements,
442 yield_movements: permitted_movements,
443 stage_type: match s.stage_type {
444 perma_traffic_signal::StageType::Fixed(d) => {
445 StageType::Fixed(Duration::seconds(d as f64))
446 }
447 perma_traffic_signal::StageType::Variable(min, delay, additional) => {
448 StageType::Variable(
449 Duration::seconds(min as f64),
450 Duration::seconds(delay as f64),
451 Duration::seconds(additional as f64),
452 )
453 }
454 },
455 });
456 } else {
457 bail!("{}", errors.join("; "));
458 }
459 }
460 let ts = ControlTrafficSignal {
461 id,
462 stages,
463 offset: Duration::seconds(plan.offset_seconds as f64),
464 };
465 ts.validate(map.get_i(id))?;
466 Ok(ts)
467 }
468}