map_model/objects/
stop_signs.rs

1use std::collections::{BTreeMap, HashMap};
2
3use serde::{Deserialize, Serialize};
4
5use abstutil::{deserialize_btreemap, serialize_btreemap};
6
7use crate::{
8    osm, Direction, DrivingSide, IntersectionID, LaneID, Map, RoadID, TurnID, TurnPriority,
9    TurnType,
10};
11
12// TODO These are old notes, they don't reflect current reality. But some of the ideas here should
13// be implemented, so keeping them...
14// 1) Pedestrians always have right-of-way. (for now -- should be toggleable later)
15// 2) Incoming roads without a stop sign have priority over roads with a sign.
16// 3) Agents with a stop sign have to actually wait some amount of time before starting the turn.
17// 4) Before starting any turn, an agent should make sure it can complete the turn without making a
18//    higher-priority agent have to wait.
19//    - "Complete" the turn just means the optimistic "length / max_speed" calculation -- if they
20//      queue behind slow cars upstream a bit, blocking the intersection a little bit is nice and
21//      realistic.
22//    - The higher priority agent might not even be at the intersection yet! This'll be a little
23//      harder to implement.
24//    - "Higher priority" has two cases -- stop sign road always yields to a non-stop sign road. But
25//      also a non-stop sign road yields to another non-stop sign road. In other words, left turns
26//      yield to straight and ideally, lane-changing yields to straight too.
27//    - So there still is a notion of turn priorities -- priority (can never conflict with another
28//      priority), yield (can't impede a priority turn), stop (has to pause and can't impede a
29//      priority or yield turn). But I don't think we want to really depict this...
30// 5) Rule 4 gives us a notion of roads that conflict -- or actually, do we even need it? No! An
31//    intersection with no stop signs at all means everyone yields. An intersection with all stop
32//    signs means everyone pauses before proceeding.
33// 6) Additionally, individual turns can be banned completely.
34//    - Even though letting players manipulate this could make parts of the map unreachable?
35
36#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
37pub struct ControlStopSign {
38    pub id: IntersectionID,
39    /// Only roads incoming to the intersection are listed here.
40    #[serde(
41        serialize_with = "serialize_btreemap",
42        deserialize_with = "deserialize_btreemap"
43    )]
44    pub roads: BTreeMap<RoadID, RoadWithStopSign>,
45}
46
47#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
48pub struct RoadWithStopSign {
49    pub lane_closest_to_edge: LaneID,
50    pub must_stop: bool,
51}
52
53impl ControlStopSign {
54    pub fn new(map: &Map, id: IntersectionID) -> ControlStopSign {
55        let mut ss = ControlStopSign {
56            id,
57            roads: BTreeMap::new(),
58        };
59        // One-way outbound roads don't need a stop sign, so skip them entirely.
60        for r in map.get_i(id).get_sorted_incoming_roads(map) {
61            let r = map.get_r(r);
62            let want_dir = if r.dst_i == id {
63                Direction::Fwd
64            } else {
65                Direction::Back
66            };
67            let travel_lanes: Vec<LaneID> = r
68                .lanes
69                .iter()
70                .filter_map(|l| {
71                    if l.dir == want_dir && l.lane_type.is_for_moving_vehicles() {
72                        Some(l.id)
73                    } else {
74                        None
75                    }
76                })
77                .collect();
78            if !travel_lanes.is_empty() {
79                let lane_closest_to_edge = if (map.get_config().driving_side == DrivingSide::Right)
80                    == (want_dir == Direction::Fwd)
81                {
82                    *travel_lanes.last().unwrap()
83                } else {
84                    travel_lanes[0]
85                };
86                ss.roads.insert(
87                    r.id,
88                    RoadWithStopSign {
89                        lane_closest_to_edge,
90                        must_stop: false,
91                    },
92                );
93            }
94        }
95
96        // Degenerate roads and deadends don't need any stop signs. But be careful with
97        // roundabouts; we want it to be lower priority to enter a roundabout than continue through
98        // it.
99        if ss.roads.len() <= 2
100            && ss
101                .roads
102                .keys()
103                .all(|r| !map.get_r(*r).osm_tags.is("junction", "roundabout"))
104        {
105            return ss;
106        }
107        if map.get_i(id).is_cycleway(map) {
108            // Two cyclepaths intersecting can just yield.
109            return ss;
110        }
111
112        // Rank each road based on OSM highway type, and additionally:
113        // - Treat cycleways as lower priority than local roads (sad but typical reality)
114        // - Prioritize roundabouts, so they clear out faster than people enter them
115        // - Treat on/off ramps with less priority than the main part of the highway
116        // - Lower the priority of service roads
117        let mut rank: HashMap<RoadID, (osm::RoadRank, usize)> = HashMap::new();
118        for r in ss.roads.keys() {
119            let r = map.get_r(*r);
120            // Lower number is lower priority
121            let priority = if r.is_cycleway() || r.osm_tags.is(osm::HIGHWAY, "service") {
122                0
123            } else if r.osm_tags.is("junction", "roundabout") {
124                3
125            } else if r
126                .osm_tags
127                .get("highway")
128                .map(|hwy| hwy.ends_with("_link"))
129                .unwrap_or(false)
130            {
131                1
132            } else {
133                2
134            };
135            rank.insert(r.id, (r.get_rank(), priority));
136        }
137        let mut ranks = rank.values().cloned().collect::<Vec<_>>();
138        ranks.sort();
139        ranks.dedup();
140        // Highest rank is first
141        ranks.reverse();
142
143        // If all roads have the same rank, all-way stop. Otherwise, everything stops except the
144        // highest-priority roads.
145        for (r, cfg) in ss.roads.iter_mut() {
146            if ranks.len() == 1 || rank[r] != ranks[0] {
147                // Don't stop in the middle of something that's likely actually an intersection.
148                if !map.get_r(*r).is_extremely_short() {
149                    cfg.must_stop = true;
150                }
151            }
152        }
153        ss
154    }
155
156    /// Get the priority of a turn according to the stop sign -- either protected or yield, never
157    /// banned.
158    pub fn get_priority(&self, turn: TurnID, map: &Map) -> TurnPriority {
159        match map.get_t(turn).turn_type {
160            TurnType::SharedSidewalkCorner => TurnPriority::Protected,
161            TurnType::Crosswalk => TurnPriority::Protected,
162            TurnType::UnmarkedCrossing => TurnPriority::Yield,
163            _ => {
164                if self.roads[&turn.src.road].must_stop {
165                    TurnPriority::Yield
166                } else {
167                    TurnPriority::Protected
168                }
169            }
170        }
171    }
172
173    pub fn flip_sign(&mut self, r: RoadID) {
174        let ss = self.roads.get_mut(&r).unwrap();
175        ss.must_stop = !ss.must_stop;
176    }
177}