game/ungap/
bike_network.rs

1use std::collections::HashMap;
2
3use map_model::{LaneType, PathConstraints, Road};
4use widgetry::mapspace::DrawUnzoomedShapes;
5use widgetry::{Color, Drawable, EventCtx, GeomBatch, GfxCtx};
6
7use crate::app::App;
8
9lazy_static::lazy_static! {
10    pub static ref DEDICATED_TRAIL: Color = Color::GREEN;
11    pub static ref PROTECTED_BIKE_LANE: Color = Color::hex("#A4DE02");
12    pub static ref PAINTED_BIKE_LANE: Color = Color::hex("#76BA1B");
13    pub static ref GREENWAY: Color = Color::hex("#4C9A2A");
14}
15
16/// Shows the bike network while unzoomed. Handles thickening the roads at low zoom levels.
17pub struct DrawNetworkLayer {
18    draw_roads: DrawUnzoomedShapes,
19    draw_intersections: Drawable,
20}
21
22impl DrawNetworkLayer {
23    pub fn new(ctx: &EventCtx, app: &App) -> DrawNetworkLayer {
24        let mut lines = DrawUnzoomedShapes::builder();
25        let mut intersections = HashMap::new();
26        for r in app.primary.map.all_roads() {
27            let mut bike_lane = false;
28            let mut buffer = false;
29            for l in &r.lanes {
30                if l.lane_type == LaneType::Biking {
31                    bike_lane = true;
32                } else if matches!(l.lane_type, LaneType::Buffer(_)) {
33                    buffer = true;
34                }
35            }
36
37            let color = if app
38                .primary
39                .map
40                .get_edits()
41                .original_roads
42                .contains_key(&r.id)
43            {
44                Color::CYAN
45            } else if r.is_cycleway() {
46                *DEDICATED_TRAIL
47            } else if bike_lane && buffer {
48                *PROTECTED_BIKE_LANE
49            } else if bike_lane {
50                *PAINTED_BIKE_LANE
51            } else if is_greenway(r) {
52                *GREENWAY
53            } else {
54                continue;
55            };
56
57            lines.add_line(r.center_pts.clone(), r.get_width(), color);
58
59            // Arbitrarily pick a color when two different types of roads meet
60            intersections.insert(r.src_i, color);
61            intersections.insert(r.dst_i, color);
62        }
63
64        let mut batch = GeomBatch::new();
65        for (i, color) in intersections {
66            // No clear way to thicken the intersection at different zoom levels
67            batch.push(color, app.primary.map.get_i(i).polygon.clone());
68        }
69
70        DrawNetworkLayer {
71            draw_roads: lines.build(),
72            draw_intersections: ctx.upload(batch),
73        }
74    }
75
76    pub fn draw(&self, g: &mut GfxCtx) {
77        g.redraw(&self.draw_intersections);
78        self.draw_roads.draw(g);
79    }
80}
81
82// TODO Check how other greenways are tagged.
83// https://www.openstreetmap.org/way/262778812 has bicycle=designated, cycleway=shared_lane...
84pub fn is_greenway(road: &Road) -> bool {
85    !road
86        .access_restrictions
87        .allow_through_traffic
88        .contains(PathConstraints::Car)
89        && road
90            .access_restrictions
91            .allow_through_traffic
92            .contains(PathConstraints::Bike)
93}