1use std::collections::{BTreeMap, BTreeSet};
2
3use maplit::btreeset;
4
5use geom::{ArrowCap, Distance, PolyLine, Polygon};
6use map_model::{osm, Direction, IntersectionID, Map, RoadID};
7use widgetry::{Drawable, EventCtx, GeomBatch};
8
9use crate::logic::{possible_destination_roads, CustomBoundary, Partitioning, Shortcuts};
10use crate::{is_private, App, NeighbourhoodID};
11
12pub struct Neighbourhood {
14 pub id: NeighbourhoodID,
15
16 pub borders: BTreeSet<IntersectionID>,
21 pub interior_intersections: BTreeSet<IntersectionID>,
23 pub boundary_polygon: Polygon,
24
25 pub interior_roads: BTreeSet<RoadID>,
28 pub perimeter_roads: BTreeSet<RoadID>,
31 pub suspicious_perimeter_roads: BTreeSet<RoadID>,
34 pub connected_exterior_roads: BTreeSet<RoadID>,
38
39 pub cells: Vec<Cell>,
40 pub shortcuts: Shortcuts,
41}
42
43pub struct Cell {
45 pub roads: BTreeMap<RoadID, DistanceInterval>,
49 pub borders: BTreeSet<IntersectionID>,
51}
52
53impl Cell {
54 pub fn is_disconnected(&self) -> bool {
56 self.borders.is_empty()
57 }
58
59 pub fn border_arrows(&self, app: &App) -> Vec<Polygon> {
60 let mut arrows = Vec::new();
61 for i in &self.borders {
62 for r in self.roads.keys() {
67 let road = app.per_map.map.get_r(*r);
68 if road.modal_filter.is_some() {
72 continue;
73 }
74
75 let angle_in = if road.src_i == *i {
77 road.center_pts.first_line().angle()
78 } else if road.dst_i == *i {
79 road.center_pts.last_line().angle().opposite()
80 } else {
81 continue;
83 };
84
85 let center = app.per_map.map.get_i(*i).polygon.center();
86 let pt_farther = center.project_away(Distance::meters(40.0), angle_in.opposite());
87 let pt_closer = center.project_away(Distance::meters(10.0), angle_in.opposite());
88
89 let thickness = Distance::meters(6.0);
91 if let Some(dir) = road.oneway_for_driving() {
92 let pl = if road.src_i == *i {
93 PolyLine::must_new(vec![pt_farther, pt_closer])
94 } else {
95 PolyLine::must_new(vec![pt_closer, pt_farther])
96 };
97 arrows.push(
98 pl.maybe_reverse(dir == Direction::Back)
99 .make_arrow(thickness, ArrowCap::Triangle),
100 );
101 } else {
102 arrows.push(
104 PolyLine::must_new(vec![pt_closer, pt_farther])
105 .make_double_arrow(thickness, ArrowCap::Triangle),
106 );
107 }
108 }
109 }
110 arrows
111 }
112}
113
114pub struct DistanceInterval {
116 pub start: Distance,
117 pub end: Distance,
118}
119
120impl Neighbourhood {
121 pub fn new(app: &App, id: NeighbourhoodID) -> Neighbourhood {
122 Self::new_without_app(&app.per_map.map, app.partitioning(), id)
123 }
124
125 pub fn new_without_app(
126 map: &Map,
127 partitioning: &Partitioning,
128 id: NeighbourhoodID,
129 ) -> Neighbourhood {
130 if let Some(custom) = partitioning.custom_boundaries.get(&id) {
131 return Self::new_custom(map, id, custom.clone());
132 }
133
134 let orig_perimeter = partitioning.neighbourhood_block(id).perimeter.clone();
135
136 let mut n = Neighbourhood {
137 id,
138 interior_roads: orig_perimeter.interior.clone(),
139 perimeter_roads: BTreeSet::new(),
140 borders: BTreeSet::new(),
141 interior_intersections: BTreeSet::new(),
142 boundary_polygon: Polygon::dummy(),
143 suspicious_perimeter_roads: BTreeSet::new(),
144 connected_exterior_roads: BTreeSet::new(),
145
146 cells: Vec::new(),
147 shortcuts: Shortcuts::empty(),
148 };
149
150 n.boundary_polygon = match orig_perimeter.clone().flip_side_of_road().to_block(map) {
154 Ok(block) => block.polygon,
155 Err(_) => orig_perimeter.clone().to_block(map).unwrap().polygon,
156 };
157 if let Some(polygon) = partitioning.get_info(id).override_drawing_boundary.clone() {
158 n.boundary_polygon = polygon;
159 }
160
161 for id in &orig_perimeter.roads {
162 let road = map.get_r(id.road);
163 if road.get_rank() == osm::RoadRank::Local {
166 n.interior_roads.insert(road.id);
167 n.suspicious_perimeter_roads.insert(road.id);
168 } else {
169 n.perimeter_roads.insert(road.id);
170 n.borders.insert(road.src_i);
171 n.borders.insert(road.dst_i);
172 }
173 }
174
175 n.finish_init(map);
176 n
177 }
178
179 fn new_custom(map: &Map, id: NeighbourhoodID, custom: CustomBoundary) -> Neighbourhood {
180 let mut n = Neighbourhood {
181 id,
182 interior_roads: custom.interior_roads,
183 perimeter_roads: BTreeSet::new(),
185 borders: custom.borders,
186 interior_intersections: BTreeSet::new(),
187 boundary_polygon: custom.boundary_polygon,
188 suspicious_perimeter_roads: BTreeSet::new(),
189 connected_exterior_roads: BTreeSet::new(),
190
191 cells: Vec::new(),
192 shortcuts: Shortcuts::empty(),
193 };
194 n.finish_init(map);
195 n
196 }
197
198 fn finish_init(&mut self, map: &Map) {
199 for r in &self.interior_roads {
200 let road = map.get_r(*r);
201 for i in [road.src_i, road.dst_i] {
202 if !self.borders.contains(&i) {
203 self.interior_intersections.insert(i);
204 }
205 }
206 }
207
208 let mut exterior: BTreeSet<RoadID> = BTreeSet::new();
210 for r in [&self.perimeter_roads, &self.interior_roads]
211 .into_iter()
212 .flatten()
213 {
214 exterior.extend(possible_destination_roads(map, *r, None));
215 }
216
217 debug!(
218 "BUILDING CONNECTED_EXTERIOR_ROADS: exterior.len() = {}",
219 exterior.len()
220 );
221 debug!(
222 "BUILDING CONNECTED_EXTERIOR_ROADS: perimeter_roads.len() = {}",
223 &self.perimeter_roads.len()
224 );
225 debug!(
226 "BUILDING CONNECTED_EXTERIOR_ROADS: interior_roads.len() = {}",
227 &self.interior_roads.len()
228 );
229
230 exterior.retain(|r| !self.perimeter_roads.contains(r) & !self.interior_roads.contains(r));
232 self.connected_exterior_roads = exterior;
233
234 debug!(
235 "BUILDING CONNECTED_EXTERIOR_ROADS: connected_exterior_roads.len() = {}",
236 &self.connected_exterior_roads.len()
237 );
238
239 self.edits_changed(map);
240 }
241
242 pub fn edits_changed(&mut self, map: &Map) {
244 self.cells = find_cells(map, &self.interior_roads, &self.borders);
245
246 self.shortcuts = Shortcuts::new(map, self, &mut abstutil::Timer::throwaway());
249 }
250
251 pub fn fade_irrelevant(&self, ctx: &EventCtx, app: &App) -> Drawable {
252 let fade_area = Polygon::with_holes(
253 app.per_map
254 .map
255 .get_boundary_polygon()
256 .get_outer_ring()
257 .clone(),
258 vec![self.boundary_polygon.clone().into_outer_ring()],
259 );
260 GeomBatch::from(vec![(app.cs.fade_map_dark, fade_area)]).upload(ctx)
261 }
262}
263
264fn find_cells(
267 map: &Map,
268 interior_roads: &BTreeSet<RoadID>,
269 borders: &BTreeSet<IntersectionID>,
270) -> Vec<Cell> {
271 let mut cells = Vec::new();
272 let mut visited = BTreeSet::new();
273
274 for start in interior_roads {
275 if visited.contains(start) || map.get_r(*start).modal_filter.is_some() {
276 continue;
277 }
278 let start = *start;
279 let road = map.get_r(start);
280 if !crate::is_driveable(road, map) {
282 continue;
283 }
284 let connected_to_public_road = [road.src_i, road.dst_i]
289 .into_iter()
290 .flat_map(|i| &map.get_i(i).roads)
291 .any(|r| *r != start && !is_private(map.get_r(*r)));
292 if !connected_to_public_road {
293 continue;
294 }
295
296 let cell = floodfill(map, start, borders, interior_roads);
297 visited.extend(cell.roads.keys().cloned());
298
299 cells.push(cell);
300 }
301
302 for (road, filter) in map.all_roads_with_modal_filter() {
304 if borders.contains(&road.src_i) {
305 let mut cell = Cell {
306 roads: BTreeMap::new(),
307 borders: btreeset! { road.src_i },
308 };
309 cell.roads.insert(
310 road.id,
311 DistanceInterval {
312 start: Distance::ZERO,
313 end: filter.dist,
314 },
315 );
316 cells.push(cell);
317 }
318 if borders.contains(&road.dst_i) {
319 let mut cell = Cell {
320 roads: BTreeMap::new(),
321 borders: btreeset! { road.dst_i },
322 };
323 cell.roads.insert(
324 road.id,
325 DistanceInterval {
326 start: filter.dist,
327 end: road.length(),
328 },
329 );
330 cells.push(cell);
331 }
332 }
333
334 cells
335}
336
337fn floodfill(
338 map: &Map,
339 start: RoadID,
340 neighbourhood_borders: &BTreeSet<IntersectionID>,
341 interior_roads: &BTreeSet<RoadID>,
342) -> Cell {
343 let mut visited_roads: BTreeMap<RoadID, DistanceInterval> = BTreeMap::new();
344 let mut cell_borders = BTreeSet::new();
345 let mut queue = vec![start];
347
348 assert!(map.get_r(start).modal_filter.is_none());
350 assert!(crate::is_driveable(map.get_r(start), map));
351
352 while !queue.is_empty() {
353 let current = map.get_r(queue.pop().unwrap());
354 if visited_roads.contains_key(¤t.id) {
355 continue;
356 }
357 visited_roads.insert(
358 current.id,
359 DistanceInterval {
360 start: Distance::ZERO,
361 end: current.length(),
362 },
363 );
364
365 for i in [current.src_i, current.dst_i] {
366 if neighbourhood_borders.contains(&i) {
371 cell_borders.insert(i);
372 continue;
373 }
374
375 for next in &map.get_i(i).roads {
376 let next_road = map.get_r(*next);
377 if let Some(ref filter) = map.get_i(i).modal_filter {
378 if !filter.allows_turn(current.id, *next) {
379 continue;
380 }
381 }
382 if let Some(ref filter) = map.get_r(*next).modal_filter {
383 let mut visited_start = next_road.src_i == i;
385 let mut visited_end = next_road.dst_i == i;
386 if let Some(interval) = visited_roads.get(next) {
388 if interval.start == Distance::ZERO {
389 visited_start = true;
390 }
391 if interval.end == next_road.length() {
392 visited_end = true;
393 }
394 }
395 visited_roads.insert(
396 *next,
397 DistanceInterval {
398 start: if visited_start {
399 Distance::ZERO
400 } else {
401 filter.dist
402 },
403 end: if visited_end {
404 next_road.length()
405 } else {
406 filter.dist
407 },
408 },
409 );
410 continue;
411 }
412
413 if !crate::is_driveable(next_road, map) {
414 continue;
415 }
416 if !interior_roads.contains(next) {
418 error!("A cell leaked out to {next} from {i}");
419 continue;
420 }
421
422 queue.push(*next);
423 }
424 }
425 }
426
427 Cell {
428 roads: visited_roads,
429 borders: cell_borders,
430 }
431}