mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-29 01:13:53 +03:00
Rank each street inside a neighborhood as quiet/busy, based on the number of rat-runs through it.
Show this through a heatmap and tooltips
This commit is contained in:
parent
5542632d61
commit
83bf6953d1
@ -1,9 +1,10 @@
|
||||
use geom::ArrowCap;
|
||||
use map_model::NORMAL_LANE_THICKNESS;
|
||||
use widgetry::mapspace::ToggleZoomed;
|
||||
use map_gui::tools::ColorNetwork;
|
||||
use map_model::{IntersectionID, RoadID, NORMAL_LANE_THICKNESS};
|
||||
use widgetry::mapspace::{ObjectID, ToggleZoomed, World};
|
||||
use widgetry::{
|
||||
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text, TextExt,
|
||||
VerticalAlignment, Widget,
|
||||
Color, EventCtx, GeomBatch, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State,
|
||||
Text, TextExt, Toggle, VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use super::rat_runs::{find_rat_runs, RatRuns};
|
||||
@ -16,6 +17,8 @@ pub struct BrowseRatRuns {
|
||||
current_idx: usize,
|
||||
|
||||
draw_path: ToggleZoomed,
|
||||
draw_heatmap: ToggleZoomed,
|
||||
world: World<Obj>,
|
||||
neighborhood: Neighborhood,
|
||||
}
|
||||
|
||||
@ -33,12 +36,23 @@ impl BrowseRatRuns {
|
||||
timer,
|
||||
)
|
||||
});
|
||||
let mut colorer = ColorNetwork::no_fading(app);
|
||||
colorer.ranked_roads(rat_runs.count_per_road.clone(), &app.cs.good_to_bad_red);
|
||||
// TODO These two will be on different scales, which'll look really weird!
|
||||
colorer.ranked_intersections(
|
||||
rat_runs.count_per_intersection.clone(),
|
||||
&app.cs.good_to_bad_red,
|
||||
);
|
||||
let world = make_world(ctx, app, &neighborhood, &rat_runs);
|
||||
|
||||
let mut state = BrowseRatRuns {
|
||||
panel: Panel::empty(ctx),
|
||||
rat_runs,
|
||||
current_idx: 0,
|
||||
draw_path: ToggleZoomed::empty(ctx),
|
||||
draw_heatmap: colorer.build(ctx),
|
||||
neighborhood,
|
||||
world,
|
||||
};
|
||||
state.recalculate(ctx, app);
|
||||
Box::new(state)
|
||||
@ -91,6 +105,16 @@ impl BrowseRatRuns {
|
||||
.hotkey(Key::RightArrow)
|
||||
.build_widget(ctx, "next rat run"),
|
||||
]),
|
||||
// TODO This should disable the individual path controls, or maybe even be a different
|
||||
// state entirely...
|
||||
Toggle::checkbox(
|
||||
ctx,
|
||||
"show heatmap of all rat-runs",
|
||||
Key::R,
|
||||
self.panel
|
||||
.maybe_is_checked("show heatmap of all rat-runs")
|
||||
.unwrap_or(true),
|
||||
),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||
.build(ctx);
|
||||
@ -140,28 +164,87 @@ impl State<App> for BrowseRatRuns {
|
||||
}
|
||||
"previous rat run" => {
|
||||
self.current_idx -= 1;
|
||||
self.panel
|
||||
.set_checked("show heatmap of all rat-runs", false);
|
||||
self.recalculate(ctx, app);
|
||||
}
|
||||
"next rat run" => {
|
||||
self.current_idx += 1;
|
||||
self.panel
|
||||
.set_checked("show heatmap of all rat-runs", false);
|
||||
self.recalculate(ctx, app);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
// Just trigger tooltips; no other interactions possible
|
||||
let _ = self.world.event(ctx);
|
||||
|
||||
Transition::Keep
|
||||
}
|
||||
|
||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||
self.panel.draw(g);
|
||||
|
||||
if self.panel.is_checked("show heatmap of all rat-runs") {
|
||||
self.draw_heatmap.draw(g);
|
||||
self.world.draw(g);
|
||||
} else {
|
||||
self.draw_path.draw(g);
|
||||
}
|
||||
|
||||
g.redraw(&self.neighborhood.fade_irrelevant);
|
||||
self.neighborhood.draw_filters.draw(g);
|
||||
if g.canvas.is_unzoomed() {
|
||||
self.neighborhood.labels.draw(g, app);
|
||||
}
|
||||
|
||||
self.draw_path.draw(g);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||
enum Obj {
|
||||
InteriorRoad(RoadID),
|
||||
InteriorIntersection(IntersectionID),
|
||||
}
|
||||
impl ObjectID for Obj {}
|
||||
|
||||
fn make_world(
|
||||
ctx: &mut EventCtx,
|
||||
app: &App,
|
||||
neighborhood: &Neighborhood,
|
||||
rat_runs: &RatRuns,
|
||||
) -> World<Obj> {
|
||||
let map = &app.primary.map;
|
||||
let mut world = World::bounded(map.get_bounds());
|
||||
|
||||
for r in &neighborhood.orig_perimeter.interior {
|
||||
world
|
||||
.add(Obj::InteriorRoad(*r))
|
||||
.hitbox(map.get_r(*r).get_thick_polygon())
|
||||
.drawn_in_master_batch()
|
||||
// TODO Not sure if tooltip() without this should imply it?
|
||||
.draw_hovered(GeomBatch::new())
|
||||
.tooltip(Text::from(format!(
|
||||
"{} rat-runs cross this street",
|
||||
rat_runs.count_per_road.get(*r)
|
||||
)))
|
||||
.build(ctx);
|
||||
}
|
||||
for i in &neighborhood.interior_intersections {
|
||||
world
|
||||
.add(Obj::InteriorIntersection(*i))
|
||||
.hitbox(map.get_i(*i).polygon.clone())
|
||||
.drawn_in_master_batch()
|
||||
.draw_hovered(GeomBatch::new())
|
||||
.tooltip(Text::from(format!(
|
||||
"{} rat-runs cross this intersection",
|
||||
rat_runs.count_per_intersection.get(*i)
|
||||
)))
|
||||
.build(ctx);
|
||||
}
|
||||
|
||||
world.initialize_hover(ctx);
|
||||
|
||||
world
|
||||
}
|
||||
|
@ -1,15 +1,17 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use abstutil::Timer;
|
||||
use abstutil::{Counter, Timer};
|
||||
use map_model::{
|
||||
DirectedRoadID, IntersectionID, LaneID, Map, Path, PathConstraints, PathRequest, PathStep,
|
||||
Position,
|
||||
Position, RoadID,
|
||||
};
|
||||
|
||||
use super::{ModalFilters, Neighborhood};
|
||||
|
||||
pub struct RatRuns {
|
||||
pub paths: Vec<Path>,
|
||||
pub count_per_road: Counter<RoadID>,
|
||||
pub count_per_intersection: Counter<IntersectionID>,
|
||||
}
|
||||
|
||||
pub fn find_rat_runs(
|
||||
@ -65,9 +67,33 @@ pub fn find_rat_runs(
|
||||
(pct * 1000.0) as usize
|
||||
});
|
||||
|
||||
// TODO Heatmap of roads used (any direction)
|
||||
// How many rat-runs pass through each street?
|
||||
let mut count_per_road = Counter::new();
|
||||
let mut count_per_intersection = Counter::new();
|
||||
for path in &paths {
|
||||
for step in path.get_steps() {
|
||||
match step {
|
||||
PathStep::Lane(l) => {
|
||||
if neighborhood.orig_perimeter.interior.contains(&l.road) {
|
||||
count_per_road.inc(l.road);
|
||||
}
|
||||
}
|
||||
PathStep::Turn(t) => {
|
||||
if neighborhood.interior_intersections.contains(&t.parent) {
|
||||
count_per_intersection.inc(t.parent);
|
||||
}
|
||||
}
|
||||
// Car paths don't make contraflow movements
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
RatRuns { paths }
|
||||
RatRuns {
|
||||
paths,
|
||||
count_per_road,
|
||||
count_per_intersection,
|
||||
}
|
||||
}
|
||||
|
||||
struct EntryExit {
|
||||
|
@ -417,6 +417,9 @@ impl Panel {
|
||||
None
|
||||
}
|
||||
}
|
||||
pub fn set_checked(&mut self, name: &str, on_off: bool) {
|
||||
self.find_mut::<Toggle>(name).enabled = on_off
|
||||
}
|
||||
|
||||
pub fn text_box(&self, name: &str) -> String {
|
||||
self.find::<TextBox>(name).get_line()
|
||||
|
Loading…
Reference in New Issue
Block a user