map_model/objects/
movement.rs

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/// A movement is like a turn, but with less detail -- it identifies a movement from one directed
13/// road to another.
14/// One road usually has 4 crosswalks, each a singleton Movement. We need all of the information
15/// here to keep each crosswalk separate.
16///
17/// We don't create movements for SharedSidewalkCorners.
18#[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    /// Could be a Crosswalk or UnmarkedCrossing
24    pub crosswalk: bool,
25}
26
27/// This is cheaper to store than a MovementID. It simply indexes into the list of movements.
28#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
29pub struct CompressedMovementID {
30    pub i: IntersectionID,
31    // There better not be any intersection with more than 256 movements...
32    pub idx: u8,
33}
34
35/// A Movement groups all turns from one road to another, letting traffic signals and pathfinding
36/// operate at a higher level of abstraction.
37// TODO Unclear how this plays with different lane types
38#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
39pub struct Movement {
40    pub id: MovementID,
41    pub turn_type: TurnType,
42    pub members: Vec<TurnID>,
43    /// The "overall" path of movement, aka, an "average" of the turn geometry
44    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                    // Just use one of the turns
90                    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        // The result might be empty for border intersections; that's fine
120        results
121    }
122
123    /// Polyline points FROM intersection
124    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        // Flip direction, so we point away from the intersection
149        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        // TODO If you hit a panic below, you've probably got two separate roads overlapping.
176        // Fix it in OSM. Examples: https://www.openstreetmap.org/changeset/87465499,
177        // https://www.openstreetmap.org/changeset/85952811
178        /*if self.geom == other.geom {
179            println!("*********** {:?} and {:?} have the same geom", self.id, other.id);
180            return true;
181        }*/
182        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            // Kiiiiinda spammy
195            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}