1use std::collections::HashMap;
2
3use abstutil::Timer;
4use geom::{Bounds, Distance, QuadTree, Tessellation};
5use map_model::{
6 AreaID, BuildingID, IntersectionID, LaneID, Map, ParkingLotID, Road, RoadID, TransitStopID,
7};
8use widgetry::{Color, Drawable, EventCtx, Fill, GeomBatch};
9
10use crate::colors::ColorScheme;
11use crate::options::Options;
12use crate::render::building::DrawBuilding;
13use crate::render::intersection::DrawIntersection;
14use crate::render::lane::DrawLane;
15use crate::render::parking_lot::DrawParkingLot;
16use crate::render::road::DrawRoad;
17use crate::render::transit_stop::DrawTransitStop;
18use crate::render::{DrawArea, Renderable};
19use crate::{AppLike, ID};
20
21pub struct DrawMap {
22 pub roads: Vec<DrawRoad>,
23 pub intersections: Vec<DrawIntersection>,
24 pub buildings: Vec<DrawBuilding>,
25 pub parking_lots: Vec<DrawParkingLot>,
26 pub bus_stops: HashMap<TransitStopID, DrawTransitStop>,
27 pub areas: Vec<DrawArea>,
28
29 pub boundary_polygon: Drawable,
30 pub draw_all_unzoomed_roads_and_intersections: Drawable,
31 pub draw_all_buildings: Drawable,
32 pub draw_all_building_outlines: Drawable,
33 pub draw_all_unzoomed_parking_lots: Drawable,
34 pub draw_all_areas: Drawable,
35
36 pub zorder_range: (isize, isize),
37 pub show_zorder: isize,
38
39 quadtree: QuadTree<ID>,
40}
41
42impl DrawMap {
43 pub fn new(
44 ctx: &mut EventCtx,
45 map: &Map,
46 opts: &Options,
47 cs: &ColorScheme,
48 timer: &mut Timer,
49 ) -> DrawMap {
50 let mut roads: Vec<DrawRoad> = Vec::new();
51 let mut low_z = 0;
52 let mut high_z = 0;
53 timer.start_iter("make DrawRoads", map.all_roads().len());
54 for r in map.all_roads() {
55 timer.next();
56 roads.push(DrawRoad::new(r));
57 low_z = low_z.min(r.zorder);
58 high_z = high_z.max(r.zorder);
59 }
60
61 let mut intersections: Vec<DrawIntersection> = Vec::new();
62 timer.start_iter("make DrawIntersections", map.all_intersections().len());
63 for i in map.all_intersections() {
64 timer.next();
65 intersections.push(DrawIntersection::new(i, map));
66 }
67
68 let draw_all_unzoomed_roads_and_intersections =
69 DrawMap::regenerate_unzoomed_layer(ctx, map, cs, opts, timer);
70
71 let (buildings, draw_all_buildings, draw_all_building_outlines) =
72 DrawMap::regenerate_buildings(ctx, map, cs, opts, timer);
73
74 timer.start("make DrawParkingLot");
75 let (parking_lots, draw_all_unzoomed_parking_lots) =
76 DrawMap::regenerate_parking_lots(ctx, map, cs, opts);
77 timer.stop("make DrawParkingLot");
78
79 timer.start_iter("make DrawTransitStop", map.all_transit_stops().len());
80 let mut bus_stops: HashMap<TransitStopID, DrawTransitStop> = HashMap::new();
81 for s in map.all_transit_stops().values() {
82 timer.next();
83 bus_stops.insert(s.id, DrawTransitStop::new(ctx, s, map, cs));
84 }
85
86 let mut areas: Vec<DrawArea> = Vec::new();
87 let mut all_areas = GeomBatch::new();
88 timer.start_iter("make DrawAreas", map.all_areas().len());
89 for a in map.all_areas() {
90 timer.next();
91 areas.push(DrawArea::new(ctx, a, cs, &mut all_areas));
92 }
93 timer.start("upload all areas");
94 let draw_all_areas = all_areas.upload(ctx);
95 timer.stop("upload all areas");
96
97 let boundary_polygon = ctx.upload(GeomBatch::from(vec![(
98 cs.map_background.clone(),
99 map.get_boundary_polygon().clone(),
100 )]));
101
102 timer.start("create quadtree");
103 let mut quadtree = QuadTree::builder();
104 for obj in &roads {
106 quadtree.add_with_box(obj.get_id(), obj.get_bounds(map));
107 }
108 for obj in &intersections {
109 quadtree.add_with_box(obj.get_id(), obj.get_bounds(map));
110 }
111 for obj in &buildings {
112 quadtree.add_with_box(obj.get_id(), obj.get_bounds(map));
113 }
114 for obj in &parking_lots {
115 quadtree.add_with_box(obj.get_id(), obj.get_bounds(map));
116 }
117 for obj in &areas {
119 quadtree.add_with_box(obj.get_id(), obj.get_bounds(map));
120 }
121 let quadtree = quadtree.build();
122 timer.stop("create quadtree");
123
124 info!(
125 "static DrawMap consumes {} MB on the GPU",
126 abstutil::prettyprint_usize(ctx.prerender.get_total_bytes_uploaded() / 1024 / 1024)
127 );
128
129 let bounds = map.get_bounds();
130 ctx.canvas.map_dims = (bounds.width(), bounds.height());
131
132 DrawMap {
133 roads,
134 intersections,
135 buildings,
136 parking_lots,
137 bus_stops,
138 areas,
139 boundary_polygon,
140 draw_all_unzoomed_roads_and_intersections,
141 draw_all_buildings,
142 draw_all_building_outlines,
143 draw_all_unzoomed_parking_lots,
144 draw_all_areas,
145
146 quadtree,
147
148 zorder_range: (low_z, high_z),
149 show_zorder: high_z,
150 }
151 }
152
153 pub fn regenerate_buildings(
154 ctx: &EventCtx,
155 map: &Map,
156 cs: &ColorScheme,
157 opts: &Options,
158 timer: &mut Timer,
159 ) -> (Vec<DrawBuilding>, Drawable, Drawable) {
160 let mut buildings: Vec<DrawBuilding> = Vec::new();
161 let mut all_buildings = GeomBatch::new();
162 let mut all_building_outlines = GeomBatch::new();
163 timer.start_iter("make DrawBuildings", map.all_buildings().len());
164 for b in map.all_buildings() {
165 timer.next();
166 buildings.push(DrawBuilding::new(
167 ctx,
168 b,
169 map,
170 cs,
171 opts,
172 &mut all_buildings,
173 &mut all_building_outlines,
174 ));
175 }
176 timer.start("upload all buildings");
177 let draw_all_buildings = all_buildings.upload(ctx);
178 let draw_all_building_outlines = all_building_outlines.upload(ctx);
179 timer.stop("upload all buildings");
180 (buildings, draw_all_buildings, draw_all_building_outlines)
181 }
182
183 pub fn regenerate_parking_lots(
184 ctx: &EventCtx,
185 map: &Map,
186 cs: &ColorScheme,
187 opts: &Options,
188 ) -> (Vec<DrawParkingLot>, Drawable) {
189 let mut parking_lots: Vec<DrawParkingLot> = Vec::new();
190 let mut all_unzoomed_parking_lots = GeomBatch::new();
191 for pl in map.all_parking_lots() {
192 parking_lots.push(DrawParkingLot::new(
193 ctx,
194 pl,
195 cs,
196 opts,
197 &mut all_unzoomed_parking_lots,
198 ));
199 }
200 (parking_lots, all_unzoomed_parking_lots.upload(ctx))
201 }
202
203 pub fn regenerate_unzoomed_layer(
204 ctx: &EventCtx,
205 map: &Map,
206 cs: &ColorScheme,
207 opts: &Options,
208 timer: &mut Timer,
209 ) -> Drawable {
210 timer.start("generate unzoomed roads and intersections");
211
212 let outline_color = Color::BLACK;
214 let outline_thickness = Distance::meters(1.0);
215 let outline_z_offset = 5;
218 let mut unzoomed_pieces: Vec<(isize, Fill, Tessellation)> = Vec::new();
219
220 for r in map.all_roads() {
221 let width = r.get_width();
222
223 unzoomed_pieces.push((
224 10 * r.zorder,
225 Fill::Color(if r.is_light_rail() {
226 cs.light_rail_track
227 } else if r.is_cycleway() {
228 cs.unzoomed_cycleway
229 } else if r.is_footway() {
230 cs.unzoomed_footway
231 } else if r.is_private() && cs.private_road.is_some() {
232 cs.private_road.unwrap()
233 } else {
234 cs.unzoomed_road_surface(r.get_rank())
235 }),
236 r.center_pts.make_polygons(width).into(),
237 ));
238
239 if cs.road_outlines {
240 for pl in [
242 r.center_pts.shift_left(width / 2.0),
243 r.center_pts.shift_right(width / 2.0),
244 ]
245 .into_iter()
246 .flatten()
247 {
248 if (opts.simplify_basemap && r.is_cycleway()) || r.is_footway() {
249 for p in pl.exact_dashed_polygons(
250 0.5 * outline_thickness,
251 Distance::meters(5.0),
252 Distance::meters(2.0),
253 ) {
254 unzoomed_pieces.push((
255 10 * r.zorder + outline_z_offset,
256 outline_color.into(),
257 p.into(),
258 ));
259 }
260 } else {
261 unzoomed_pieces.push((
262 10 * r.zorder + outline_z_offset,
263 outline_color.into(),
264 pl.make_polygons(outline_thickness).into(),
265 ));
266 }
267 }
268 }
269 }
270
271 let traffic_signal_icon = if opts.show_traffic_signal_icon {
272 GeomBatch::load_svg(ctx, "system/assets/map/traffic_signal.svg").scale(0.8)
273 } else {
274 GeomBatch::new()
275 };
276
277 for i in map.all_intersections() {
278 let zorder = 10 * i.get_zorder(map);
279 let intersection_color = if opts.simplify_basemap
280 || i.is_stop_sign()
281 || (i.is_traffic_signal() && opts.show_traffic_signal_icon)
282 {
283 if i.is_light_rail(map) {
287 cs.light_rail_track
288 } else if i.is_cycleway(map) {
289 cs.unzoomed_cycleway
290 } else if i.is_footway(map) {
291 cs.unzoomed_footway
292 } else if i.is_private(map) && cs.private_road.is_some() {
293 cs.private_road.unwrap()
294 } else {
295 cs.unzoomed_road_surface(i.get_rank(map))
296 }
297 } else {
298 cs.unzoomed_interesting_intersection
299 };
300 unzoomed_pieces.push((zorder, intersection_color.into(), i.polygon.clone().into()));
301
302 if cs.road_outlines {
303 for pl in DrawIntersection::get_unzoomed_outline(i, map) {
307 unzoomed_pieces.push((
308 zorder + outline_z_offset,
309 outline_color.into(),
310 pl.make_polygons(outline_thickness).into(),
311 ));
312 }
313 }
314
315 if opts.show_traffic_signal_icon && i.is_traffic_signal() {
316 let icon_zorder = 10 * i.roads.iter().map(|r| map.get_r(*r).zorder).max().unwrap();
319 for (fill, polygon, _) in traffic_signal_icon
320 .clone()
321 .centered_on(i.polygon.polylabel())
322 .consume()
323 {
324 unzoomed_pieces.push((icon_zorder + outline_z_offset, fill, polygon));
325 }
326 }
327 }
328 unzoomed_pieces.sort_by_key(|(z, _, _)| *z);
329 let mut unzoomed_batch = GeomBatch::new();
330 for (_, fill, poly) in unzoomed_pieces {
331 unzoomed_batch.push(fill, poly);
332 }
333
334 let draw_all_unzoomed_roads_and_intersections = unzoomed_batch.upload(ctx);
335 timer.stop("generate unzoomed roads and intersections");
336 draw_all_unzoomed_roads_and_intersections
337 }
338
339 pub fn get_r(&self, id: RoadID) -> &DrawRoad {
341 &self.roads[id.0]
342 }
343
344 pub fn get_l(&self, id: LaneID) -> &DrawLane {
345 &self.get_r(id.road).lanes[id.offset]
346 }
347
348 pub fn get_i(&self, id: IntersectionID) -> &DrawIntersection {
349 &self.intersections[id.0]
350 }
351
352 pub fn get_b(&self, id: BuildingID) -> &DrawBuilding {
353 &self.buildings[id.0]
354 }
355
356 pub fn get_pl(&self, id: ParkingLotID) -> &DrawParkingLot {
357 &self.parking_lots[id.0]
358 }
359
360 pub fn get_ts(&self, id: TransitStopID) -> &DrawTransitStop {
361 &self.bus_stops[&id]
362 }
363
364 pub fn get_a(&self, id: AreaID) -> &DrawArea {
365 &self.areas[id.0]
366 }
367
368 pub fn get_obj<'a>(&self, id: ID) -> &dyn Renderable {
369 match id {
370 ID::Road(id) => self.get_r(id),
371 ID::Lane(id) => self.get_l(id),
372 ID::Intersection(id) => self.get_i(id),
373 ID::Building(id) => self.get_b(id),
374 ID::ParkingLot(id) => self.get_pl(id),
375 ID::TransitStop(id) => self.get_ts(id),
376 ID::Area(id) => self.get_a(id),
377 }
378 }
379
380 pub fn get_matching_objects(&self, bounds: Bounds) -> Vec<ID> {
382 self.quadtree
383 .query_bbox_borrow(bounds)
384 .map(|id| id.clone())
385 .collect()
386 }
387
388 pub fn get_renderables_back_to_front(&self, bounds: Bounds, map: &Map) -> Vec<&dyn Renderable> {
390 let mut areas: Vec<&dyn Renderable> = Vec::new();
391 let mut parking_lots: Vec<&dyn Renderable> = Vec::new();
392 let mut lanes: Vec<&dyn Renderable> = Vec::new();
393 let mut roads: Vec<&dyn Renderable> = Vec::new();
394 let mut intersections: Vec<&dyn Renderable> = Vec::new();
395 let mut buildings: Vec<&dyn Renderable> = Vec::new();
396 let mut transit_stops: Vec<&dyn Renderable> = Vec::new();
397
398 for id in self.get_matching_objects(bounds) {
399 match id {
400 ID::Area(id) => areas.push(self.get_a(id)),
401 ID::Road(id) => {
402 let road = self.get_r(id);
403 for lane in &road.lanes {
404 lanes.push(lane);
405 }
406 for ts in &map.get_r(id).transit_stops {
407 transit_stops.push(self.get_ts(*ts));
408 }
409 roads.push(road);
410 }
411 ID::Intersection(id) => {
412 intersections.push(self.get_i(id));
413 }
414 ID::Building(id) => buildings.push(self.get_b(id)),
415 ID::ParkingLot(id) => {
416 parking_lots.push(self.get_pl(id));
417 }
418 ID::Lane(_) | ID::TransitStop(_) => {
419 panic!("{:?} shouldn't be in the quadtree", id)
420 }
421 }
422 }
423
424 let mut borrows: Vec<&dyn Renderable> = Vec::new();
426 borrows.extend(areas);
427 borrows.extend(parking_lots);
428 borrows.extend(lanes);
429 borrows.extend(roads);
430 borrows.extend(intersections);
431 borrows.extend(buildings);
432 borrows.extend(transit_stops);
433
434 borrows.retain(|x| x.get_zorder() <= self.show_zorder);
435
436 borrows.sort_by_key(|x| x.get_zorder());
438
439 borrows
440 }
441
442 pub fn zoomed_batch(ctx: &EventCtx, app: &dyn AppLike) -> GeomBatch {
445 let mut batch = GeomBatch::new();
448 let map = app.map();
449 let cs = app.cs();
450
451 batch.push(
452 cs.map_background.clone(),
453 map.get_boundary_polygon().clone(),
454 );
455
456 for a in map.all_areas() {
457 DrawArea::new(ctx, a, cs, &mut batch);
458 }
459
460 for pl in map.all_parking_lots() {
461 batch.append(
462 DrawParkingLot::new(ctx, pl, cs, app.opts(), &mut GeomBatch::new()).render(app),
463 );
464 }
465
466 for r in map.all_roads() {
467 for l in &r.lanes {
468 batch.append(DrawLane::new(l, r).render(ctx, app));
469 }
470 }
471
472 for r in map.all_roads() {
473 batch.append(DrawRoad::new(r).render(ctx, app));
474 }
475
476 for i in map.all_intersections() {
477 batch.append(DrawIntersection::new(i, map).render(ctx, app));
478 }
479
480 let mut bldgs_batch = GeomBatch::new();
481 let mut outlines_batch = GeomBatch::new();
482 for b in map.all_buildings() {
483 DrawBuilding::new(
484 ctx,
485 b,
486 map,
487 cs,
488 app.opts(),
489 &mut bldgs_batch,
490 &mut outlines_batch,
491 );
492 }
493 batch.append(bldgs_batch);
494 batch.append(outlines_batch);
495
496 batch
497 }
498
499 pub fn recreate_intersection(&mut self, i: IntersectionID, map: &Map) {
500 self.quadtree.remove(ID::Intersection(i)).unwrap();
501
502 let draw = DrawIntersection::new(map.get_i(i), map);
503 self.quadtree
504 .insert_with_box(draw.get_id(), draw.get_bounds(map));
505 self.intersections[i.0] = draw;
506 }
507
508 pub fn recreate_road(&mut self, road: &Road, map: &Map) {
509 self.quadtree.remove(ID::Road(road.id)).unwrap();
510
511 let draw = DrawRoad::new(road);
512 self.quadtree
513 .insert_with_box(draw.get_id(), draw.get_bounds(map));
514 self.roads[road.id.0] = draw;
515 }
516
517 pub fn free_memory(&mut self) {
518 for r in &mut self.roads {
520 r.clear_rendering();
521 }
522 for i in &mut self.intersections {
523 i.clear_rendering();
524 }
525 for b in &mut self.buildings {
526 b.clear_rendering();
527 }
528 for pl in &mut self.parking_lots {
529 pl.clear_rendering();
530 }
531 }
532}