map_model/make/
walking_turns.rs1use geom::{Distance, PolyLine, Pt2D, EPSILON_DIST};
2
3use crate::{
4 Direction, DrivingSide, Intersection, IntersectionID, Lane, LaneID, Map, Turn, TurnID, TurnType,
5};
6
7pub fn make_walking_turns(map: &Map, i: &Intersection) -> Vec<Turn> {
12 let driving_side = map.config.driving_side;
13
14 let mut lanes: Vec<Option<&Lane>> = Vec::new();
17 let mut sorted_roads = i.roads.clone();
18 if driving_side == DrivingSide::Left {
20 sorted_roads.reverse();
21 }
22
23 for r in sorted_roads {
24 let road = map.get_r(r);
25 let mut fwd = None;
26 let mut back = None;
27 for l in &road.lanes {
28 if l.lane_type.is_walkable() {
29 if l.dir == Direction::Fwd {
30 fwd = Some(l);
31 } else {
32 back = Some(l);
33 }
34 }
35 }
36
37 let (in_lane, out_lane) = if road.src_i == i.id {
38 (back, fwd)
39 } else {
40 (fwd, back)
41 };
42
43 if map.get_r(r).is_footway() {
45 if in_lane.is_some() {
46 lanes.push(in_lane);
47 }
48 if out_lane.is_some() {
49 lanes.push(out_lane);
50 }
51 } else {
52 lanes.push(in_lane);
53 lanes.push(out_lane);
54 }
55 }
56
57 if lanes.iter().filter(|l| l.is_some()).count() <= 1 {
59 return Vec::new();
60 }
61
62 if i.is_deadend_for_everyone() {
64 let (l1, l2) = (lanes[0].unwrap(), lanes[1].unwrap());
65 return vec![Turn {
66 id: turn_id(i.id, l1.id, l2.id),
67 turn_type: TurnType::SharedSidewalkCorner,
68 geom: make_shared_sidewalk_corner(i, l1, l2),
69 }];
70 }
71
72 while lanes[0].is_none() {
74 lanes.rotate_left(1);
75 }
76 let mut result: Vec<Turn> = Vec::new();
77
78 let mut from: Option<&Lane> = lanes[0];
79 let mut adj = true;
80 for l in lanes.iter().skip(1).chain(lanes.iter().take(1)) {
81 if from.is_none() {
82 from = *l;
83 adj = true;
84 continue;
85 }
86 let l1 = from.unwrap();
87
88 if l.is_none() {
89 adj = false;
90 continue;
91 }
92 let l2 = l.unwrap();
93
94 if adj && l1.id.road != l2.id.road {
95 result.push(Turn {
96 id: turn_id(i.id, l1.id, l2.id),
97 turn_type: TurnType::SharedSidewalkCorner,
98 geom: make_shared_sidewalk_corner(i, l1, l2),
99 });
100
101 from = Some(l2);
102 } else {
104 result.push(Turn {
105 id: turn_id(i.id, l1.id, l2.id),
106 turn_type: TurnType::Crosswalk,
107 geom: make_crosswalk(i, l1, l2),
108 });
109 from = Some(l2);
110 adj = true;
111 }
112 }
113
114 match result
121 .iter()
122 .filter(|t| t.turn_type == TurnType::Crosswalk)
123 .count()
124 {
125 1 | 2 => {
126 result.remove(
127 result
128 .iter()
129 .position(|t| t.turn_type == TurnType::Crosswalk)
130 .unwrap(),
131 );
132 }
133 _ => {}
134 }
135
136 result
137}
138
139pub fn filter_turns(mut input: Vec<Turn>, map: &Map, i: &Intersection) -> Vec<Turn> {
145 for r in &i.roads {
146 if map.get_r(*r).is_extremely_short() {
147 input.retain(|t| {
148 !(t.id.src.road == *r && t.id.dst.road == *r && t.turn_type.pedestrian_crossing())
149 });
150 }
151 }
152
153 for turn in &mut input {
154 if let Some(dr) = turn.crosswalk_over_road(map) {
155 let road = map.get_r(dr.road);
156 let keep = if dr.dir == Direction::Fwd {
157 road.crosswalk_forward
158 } else {
159 road.crosswalk_backward
160 };
161 if !keep {
162 turn.turn_type = TurnType::UnmarkedCrossing;
163 }
164 } else if turn.turn_type.pedestrian_crossing() {
165 for l in [turn.id.src, turn.id.dst] {
170 let road = map.get_parent(l);
171 if !road.crosswalk_forward || !road.crosswalk_backward {
172 turn.turn_type = TurnType::UnmarkedCrossing;
173 }
174 }
175 }
176 }
177
178 input
179}
180
181fn make_crosswalk(i: &Intersection, l1: &Lane, l2: &Lane) -> PolyLine {
182 let l1_line = l1.end_line(i.id);
183 let l2_line = l2.end_line(i.id);
184
185 PolyLine::deduping_new(vec![
188 l1_line.pt2(),
189 l1_line.unbounded_dist_along(
190 l1_line.length()
191 + if i.is_degenerate() {
192 Distance::const_meters(2.5)
193 } else {
194 l1.width / 2.0
195 },
196 ),
197 l2_line.unbounded_dist_along(
198 l2_line.length()
199 + if i.is_degenerate() {
200 Distance::const_meters(2.5)
201 } else {
202 l2.width / 2.0
203 },
204 ),
205 l2_line.pt2(),
206 ])
207 .unwrap_or_else(|_| baseline_geometry(l1.endpoint(i.id), l2.endpoint(i.id)))
208}
209
210fn make_shared_sidewalk_corner(i: &Intersection, l1: &Lane, l2: &Lane) -> PolyLine {
212 let baseline = baseline_geometry(l1.endpoint(i.id), l2.endpoint(i.id));
214
215 let dir = if i
217 .polygon
218 .center()
219 .angle_to(l1.endpoint(i.id))
220 .simple_shortest_rotation_towards(i.polygon.center().angle_to(l2.endpoint(i.id)))
221 > 0.0
222 {
223 1.0
224 } else {
225 -1.0
226 } * if i.is_deadend_for_everyone() {
228 -1.0
229 } else {
230 1.0
231 };
232 let corner1 = l1
235 .end_line(i.id)
236 .shift_either_direction(dir * l1.width / 2.0)
237 .pt2();
238 let corner2 = l2
239 .end_line(i.id)
240 .shift_either_direction(-dir * l2.width / 2.0)
241 .pt2();
242
243 if false {
245 return i
246 .polygon
247 .get_outer_ring()
248 .get_shorter_slice_btwn(corner1, corner2)
249 .unwrap();
250 }
251
252 let mut pts_between = vec![l2.endpoint(i.id)];
255 let mut i_pts = i.polygon.get_outer_ring().clone().into_points();
257
258 i_pts.pop();
260
261 if dir < 0.0 {
262 i_pts.reverse();
263 }
264
265 for _ in 0..i_pts.len() {
266 if i_pts[0].approx_eq(corner2, Distance::meters(0.5)) {
267 break;
268 }
269 i_pts.rotate_left(1);
270 }
271
272 for idx in 0..i_pts.len() {
273 if i_pts[idx].approx_eq(corner1, Distance::meters(0.5)) {
274 i_pts.truncate(idx + 1);
275 break;
276 }
277 }
278
279 if i_pts.len() < 2 {
280 return baseline;
282 }
283
284 if let Ok(pl) = PolyLine::new(i_pts)
285 .and_then(|pl| pl.shift_either_direction(dir * l1.width.min(l2.width) / 2.0))
286 {
287 pts_between.extend(pl.points().iter().take(pl.points().len() - 1).skip(1));
289 } else {
290 warn!(
291 "SharedSidewalkCorner between {} and {} has weird collapsing geometry, so \
292 just doing straight line",
293 l1.id, l2.id
294 );
295 return baseline;
296 }
297
298 pts_between.push(l1.endpoint(i.id));
299 pts_between.dedup();
300
301 pts_between.reverse();
302
303 if abstutil::contains_duplicates(
304 &pts_between
305 .iter()
306 .map(|pt| pt.to_hashable())
307 .collect::<Vec<_>>(),
308 ) || pts_between.len() < 2
309 {
310 warn!(
311 "SharedSidewalkCorner between {} and {} has weird duplicate geometry, so just doing \
312 straight line",
313 l1.id, l2.id
314 );
315 return baseline;
316 }
317 if let Ok(result) = PolyLine::new(pts_between) {
318 if result.length() > 10.0 * baseline.length() {
319 warn!(
320 "SharedSidewalkCorner between {} and {} explodes to {} long, so just doing straight \
321 line",
322 l1.id,
323 l2.id,
324 result.length()
325 );
326 return baseline;
327 }
328 result
329 } else {
330 baseline
331 }
332}
333
334fn baseline_geometry(pt1: Pt2D, pt2: Pt2D) -> PolyLine {
340 PolyLine::new(vec![pt1, pt2]).unwrap_or_else(|_| {
341 PolyLine::must_new(vec![
342 pt1,
343 pt1.offset(EPSILON_DIST.inner_meters(), EPSILON_DIST.inner_meters()),
344 ])
345 })
346}
347
348fn turn_id(parent: IntersectionID, src: LaneID, dst: LaneID) -> TurnID {
349 TurnID { parent, src, dst }
350}