mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-30 18:24:04 +03:00
A new generic UI to compare counts from two files.
Also moving some of the counts stuff to map_gui, accordingly
This commit is contained in:
parent
c33ba85cfe
commit
edfd320908
72
game/src/devtools/compare_counts.rs
Normal file
72
game/src/devtools/compare_counts.rs
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
use abstutil::Timer;
|
||||||
|
use map_gui::tools::compare_counts::{CompareCounts, Counts, Layer};
|
||||||
|
use map_gui::tools::PopupMsg;
|
||||||
|
use widgetry::{
|
||||||
|
EventCtx, GfxCtx, HorizontalAlignment, Panel, SimpleState, State, VerticalAlignment, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
|
use crate::app::{App, Transition};
|
||||||
|
|
||||||
|
pub struct GenericCompareCounts {
|
||||||
|
compare: CompareCounts,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenericCompareCounts {
|
||||||
|
pub fn new_state(
|
||||||
|
ctx: &mut EventCtx,
|
||||||
|
app: &mut App,
|
||||||
|
path1: String,
|
||||||
|
path2: String,
|
||||||
|
) -> Box<dyn State<App>> {
|
||||||
|
let mut timer = Timer::throwaway();
|
||||||
|
// TODO File loaders
|
||||||
|
let counts_a = match abstio::maybe_read_json::<Counts>(path1, &mut timer) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => {
|
||||||
|
return PopupMsg::new_state(ctx, "Error", vec![err.to_string()]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let counts_b = match abstio::maybe_read_json::<Counts>(path2, &mut timer) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(err) => {
|
||||||
|
return PopupMsg::new_state(ctx, "Error", vec![err.to_string()]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
let mut compare = CompareCounts::new(ctx, app, counts_a, counts_b, Layer::A);
|
||||||
|
compare.autoselect_layer();
|
||||||
|
|
||||||
|
let panel = Panel::new_builder(Widget::col(vec![
|
||||||
|
map_gui::tools::app_header(ctx, app, "Traffic count comparator"),
|
||||||
|
compare.get_panel_widget(ctx),
|
||||||
|
]))
|
||||||
|
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||||
|
.build(ctx);
|
||||||
|
|
||||||
|
<dyn SimpleState<_>>::new_state(panel, Box::new(GenericCompareCounts { compare }))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SimpleState<App> for GenericCompareCounts {
|
||||||
|
fn on_click(&mut self, _: &mut EventCtx, _: &mut App, _: &str, _: &Panel) -> Transition {
|
||||||
|
unreachable!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn other_event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
||||||
|
self.compare.other_event(ctx);
|
||||||
|
Transition::Keep
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panel_changed(
|
||||||
|
&mut self,
|
||||||
|
_: &mut EventCtx,
|
||||||
|
_: &mut App,
|
||||||
|
panel: &mut Panel,
|
||||||
|
) -> Option<Transition> {
|
||||||
|
assert!(self.compare.panel_changed(panel));
|
||||||
|
None
|
||||||
|
}
|
||||||
|
|
||||||
|
fn draw(&self, g: &mut GfxCtx, _: &App) {
|
||||||
|
self.compare.draw(g);
|
||||||
|
}
|
||||||
|
}
|
@ -11,6 +11,7 @@ use widgetry::{Choice, EventCtx, Key, Line, Panel, SimpleState, State, Widget};
|
|||||||
use crate::app::{App, Transition};
|
use crate::app::{App, Transition};
|
||||||
|
|
||||||
mod collisions;
|
mod collisions;
|
||||||
|
pub mod compare_counts;
|
||||||
mod destinations;
|
mod destinations;
|
||||||
pub mod kml;
|
pub mod kml;
|
||||||
mod polygon;
|
mod polygon;
|
||||||
|
@ -114,6 +114,9 @@ struct Args {
|
|||||||
/// Start by showing an ActDev scenario. Either "base" or "go_active".
|
/// Start by showing an ActDev scenario. Either "base" or "go_active".
|
||||||
#[structopt(long)]
|
#[structopt(long)]
|
||||||
actdev_scenario: Option<String>,
|
actdev_scenario: Option<String>,
|
||||||
|
/// Start in a tool for comparing traffic counts
|
||||||
|
#[structopt(long)]
|
||||||
|
compare_counts: Option<Vec<String>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Setup {
|
struct Setup {
|
||||||
@ -139,6 +142,7 @@ enum Mode {
|
|||||||
Ungap,
|
Ungap,
|
||||||
Devtools,
|
Devtools,
|
||||||
LoadKML(String),
|
LoadKML(String),
|
||||||
|
CompareCounts(String, String),
|
||||||
Gameplay(GameplayMode),
|
Gameplay(GameplayMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -182,6 +186,11 @@ fn run(mut settings: Settings) {
|
|||||||
Mode::Devtools
|
Mode::Devtools
|
||||||
} else if let Some(kml) = args.load_kml {
|
} else if let Some(kml) = args.load_kml {
|
||||||
Mode::LoadKML(kml)
|
Mode::LoadKML(kml)
|
||||||
|
} else if let Some(mut paths) = args.compare_counts {
|
||||||
|
if paths.len() != 2 {
|
||||||
|
panic!("--compare-counts takes exactly two paths");
|
||||||
|
}
|
||||||
|
Mode::CompareCounts(paths.remove(0), paths.remove(0))
|
||||||
} else {
|
} else {
|
||||||
Mode::SomethingElse
|
Mode::SomethingElse
|
||||||
},
|
},
|
||||||
@ -557,6 +566,11 @@ fn finish_app_setup(
|
|||||||
}
|
}
|
||||||
Mode::Devtools => devtools::DevToolsMode::new_state(ctx, app),
|
Mode::Devtools => devtools::DevToolsMode::new_state(ctx, app),
|
||||||
Mode::LoadKML(path) => crate::devtools::kml::ViewKML::new_state(ctx, app, Some(path)),
|
Mode::LoadKML(path) => crate::devtools::kml::ViewKML::new_state(ctx, app, Some(path)),
|
||||||
|
Mode::CompareCounts(path1, path2) => {
|
||||||
|
crate::devtools::compare_counts::GenericCompareCounts::new_state(
|
||||||
|
ctx, app, path1, path2,
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
vec![TitleScreen::new_state(ctx, app), state]
|
vec![TitleScreen::new_state(ctx, app), state]
|
||||||
|
@ -1,4 +1,3 @@
|
|||||||
mod compare;
|
|
||||||
mod ui;
|
mod ui;
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
@ -6,16 +5,15 @@ use std::collections::BTreeSet;
|
|||||||
use abstio::MapName;
|
use abstio::MapName;
|
||||||
use abstutil::{Counter, Timer};
|
use abstutil::{Counter, Timer};
|
||||||
use geom::{Duration, Time};
|
use geom::{Duration, Time};
|
||||||
|
use map_gui::tools::compare_counts::{CompareCounts, Counts};
|
||||||
use map_model::{Map, PathConstraints, PathRequest, PathStepV2, PathfinderCaching, RoutingParams};
|
use map_model::{Map, PathConstraints, PathRequest, PathStepV2, PathfinderCaching, RoutingParams};
|
||||||
use sim::{Scenario, TripEndpoint, TripMode};
|
use sim::{Scenario, TripEndpoint, TripMode};
|
||||||
use widgetry::EventCtx;
|
use widgetry::EventCtx;
|
||||||
|
|
||||||
use self::compare::{CompareCounts, Counts};
|
|
||||||
pub use self::ui::ShowResults;
|
pub use self::ui::ShowResults;
|
||||||
use crate::App;
|
use crate::App;
|
||||||
|
|
||||||
// TODO Configurable main road penalty, like in the pathfinding tool
|
// TODO Configurable main road penalty, like in the pathfinding tool
|
||||||
// TODO Don't allow crossing filters at all -- don't just disincentivize
|
|
||||||
// TODO Share structure or pieces with Ungap's predict mode
|
// TODO Share structure or pieces with Ungap's predict mode
|
||||||
// ... can't we just produce data of a certain shape, and have a UI pretty tuned for that?
|
// ... can't we just produce data of a certain shape, and have a UI pretty tuned for that?
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
|
|
||||||
use map_gui::load::FileLoader;
|
use map_gui::load::FileLoader;
|
||||||
use map_gui::tools::checkbox_per_mode;
|
use map_gui::tools::{checkbox_per_mode, PopupMsg};
|
||||||
use sim::{Scenario, TripMode};
|
use sim::{Scenario, TripMode};
|
||||||
use widgetry::mapspace::ToggleZoomed;
|
use widgetry::mapspace::ToggleZoomed;
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
@ -56,6 +56,7 @@ impl ShowResults {
|
|||||||
// TODO Dropdown for the scenario, and explain its source/limitations
|
// TODO Dropdown for the scenario, and explain its source/limitations
|
||||||
app.session.impact.filters.to_panel(ctx, app),
|
app.session.impact.filters.to_panel(ctx, app),
|
||||||
app.session.impact.compare_counts.get_panel_widget(ctx),
|
app.session.impact.compare_counts.get_panel_widget(ctx),
|
||||||
|
ctx.style().btn_plain.text("Save before/after counts to files").build_def(ctx),
|
||||||
]))
|
]))
|
||||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||||
.build(ctx);
|
.build(ctx);
|
||||||
@ -77,12 +78,31 @@ impl ShowResults {
|
|||||||
|
|
||||||
impl SimpleState<App> for ShowResults {
|
impl SimpleState<App> for ShowResults {
|
||||||
fn on_click(&mut self, ctx: &mut EventCtx, app: &mut App, x: &str, _: &Panel) -> Transition {
|
fn on_click(&mut self, ctx: &mut EventCtx, app: &mut App, x: &str, _: &Panel) -> Transition {
|
||||||
if x == "close" {
|
match x {
|
||||||
// Don't just Pop; if we updated the results, the UI won't warn the user about a slow
|
"close" => {
|
||||||
// loading
|
// Don't just Pop; if we updated the results, the UI won't warn the user about a slow
|
||||||
return Transition::Replace(BrowseNeighborhoods::new_state(ctx, app));
|
// loading
|
||||||
|
Transition::Replace(BrowseNeighborhoods::new_state(ctx, app))
|
||||||
|
}
|
||||||
|
"Save before/after counts to files" => {
|
||||||
|
let path1 = "counts_a.json";
|
||||||
|
let path2 = "counts_b.json";
|
||||||
|
abstio::write_json(
|
||||||
|
path1.to_string(),
|
||||||
|
&app.session.impact.compare_counts.counts_a.to_counts(),
|
||||||
|
);
|
||||||
|
abstio::write_json(
|
||||||
|
path2.to_string(),
|
||||||
|
&app.session.impact.compare_counts.counts_b.to_counts(),
|
||||||
|
);
|
||||||
|
Transition::Push(PopupMsg::new_state(
|
||||||
|
ctx,
|
||||||
|
"Saved",
|
||||||
|
vec![format!("Saved {} and {}", path1, path2)],
|
||||||
|
))
|
||||||
|
}
|
||||||
|
_ => unreachable!(),
|
||||||
}
|
}
|
||||||
unreachable!()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn other_event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
fn other_event(&mut self, ctx: &mut EventCtx, app: &mut App) -> Transition {
|
||||||
|
@ -1,19 +1,18 @@
|
|||||||
// TODO Some of this may warrant a standalone tool, or being in game/devtools
|
|
||||||
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
use abstio::MapName;
|
use abstio::MapName;
|
||||||
use abstutil::{prettyprint_usize, Counter};
|
use abstutil::{prettyprint_usize, Counter};
|
||||||
use geom::{Distance, Histogram, Statistic};
|
use geom::{Distance, Histogram, Statistic};
|
||||||
use map_gui::tools::{cmp_count, ColorNetwork, DivergingScale};
|
|
||||||
use map_model::{IntersectionID, RoadID};
|
use map_model::{IntersectionID, RoadID};
|
||||||
use widgetry::mapspace::{ObjectID, ToggleZoomed, ToggleZoomedBuilder, World};
|
use widgetry::mapspace::{ObjectID, ToggleZoomed, ToggleZoomedBuilder, World};
|
||||||
use widgetry::{Choice, Color, EventCtx, GeomBatch, GfxCtx, Line, Panel, Text, TextExt, Widget};
|
use widgetry::{
|
||||||
|
Choice, Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Panel, Text, TextExt, Widget,
|
||||||
|
};
|
||||||
|
|
||||||
use super::App;
|
use crate::tools::{cmp_count, ColorNetwork, DivergingScale};
|
||||||
|
use crate::AppLike;
|
||||||
|
|
||||||
// TODO
|
// TODO Document all of this!
|
||||||
// 3) Make a new UI with a file picker and CLI shortcuts
|
|
||||||
// 4) See if we can dedupe requests in the impact prediction -- using this tool to validate
|
// 4) See if we can dedupe requests in the impact prediction -- using this tool to validate
|
||||||
// 5) Download the sensor data and get it in this format (and maybe filter simulated data to only
|
// 5) Download the sensor data and get it in this format (and maybe filter simulated data to only
|
||||||
// match roads we have)
|
// match roads we have)
|
||||||
@ -31,8 +30,8 @@ pub struct Counts {
|
|||||||
|
|
||||||
pub struct CompareCounts {
|
pub struct CompareCounts {
|
||||||
pub layer: Layer,
|
pub layer: Layer,
|
||||||
counts_a: CountsUI,
|
pub counts_a: CountsUI,
|
||||||
counts_b: CountsUI,
|
pub counts_b: CountsUI,
|
||||||
world: World<Obj>,
|
world: World<Obj>,
|
||||||
relative_heatmap: ToggleZoomed,
|
relative_heatmap: ToggleZoomed,
|
||||||
}
|
}
|
||||||
@ -51,7 +50,9 @@ pub enum Layer {
|
|||||||
Compare,
|
Compare,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CountsUI {
|
pub struct CountsUI {
|
||||||
|
// TODO Just embed Counts directly, and make that serialize a Counter?
|
||||||
|
map: MapName,
|
||||||
description: String,
|
description: String,
|
||||||
heatmap: ToggleZoomed,
|
heatmap: ToggleZoomed,
|
||||||
per_road: Counter<RoadID>,
|
per_road: Counter<RoadID>,
|
||||||
@ -59,7 +60,7 @@ struct CountsUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl CountsUI {
|
impl CountsUI {
|
||||||
fn new(ctx: &EventCtx, app: &App, counts: Counts) -> CountsUI {
|
fn new(ctx: &EventCtx, app: &dyn AppLike, counts: Counts) -> CountsUI {
|
||||||
let mut per_road = Counter::new();
|
let mut per_road = Counter::new();
|
||||||
for (r, count) in counts.per_road {
|
for (r, count) in counts.per_road {
|
||||||
per_road.add(r, count);
|
per_road.add(r, count);
|
||||||
@ -70,9 +71,10 @@ impl CountsUI {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let mut colorer = ColorNetwork::no_fading(app);
|
let mut colorer = ColorNetwork::no_fading(app);
|
||||||
colorer.ranked_roads(per_road.clone(), &app.cs.good_to_bad_red);
|
colorer.ranked_roads(per_road.clone(), &app.cs().good_to_bad_red);
|
||||||
colorer.ranked_intersections(per_intersection.clone(), &app.cs.good_to_bad_red);
|
colorer.ranked_intersections(per_intersection.clone(), &app.cs().good_to_bad_red);
|
||||||
CountsUI {
|
CountsUI {
|
||||||
|
map: counts.map,
|
||||||
description: counts.description,
|
description: counts.description,
|
||||||
heatmap: colorer.build(ctx),
|
heatmap: colorer.build(ctx),
|
||||||
per_road,
|
per_road,
|
||||||
@ -82,18 +84,33 @@ impl CountsUI {
|
|||||||
|
|
||||||
fn empty(ctx: &EventCtx) -> Self {
|
fn empty(ctx: &EventCtx) -> Self {
|
||||||
Self {
|
Self {
|
||||||
|
map: MapName::new("zz", "place", "holder"),
|
||||||
description: String::new(),
|
description: String::new(),
|
||||||
heatmap: ToggleZoomed::empty(ctx),
|
heatmap: ToggleZoomed::empty(ctx),
|
||||||
per_road: Counter::new(),
|
per_road: Counter::new(),
|
||||||
per_intersection: Counter::new(),
|
per_intersection: Counter::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn to_counts(&self) -> Counts {
|
||||||
|
Counts {
|
||||||
|
map: self.map.clone(),
|
||||||
|
description: self.description.clone(),
|
||||||
|
per_road: self.per_road.clone().consume().into_iter().collect(),
|
||||||
|
per_intersection: self
|
||||||
|
.per_intersection
|
||||||
|
.clone()
|
||||||
|
.consume()
|
||||||
|
.into_iter()
|
||||||
|
.collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CompareCounts {
|
impl CompareCounts {
|
||||||
pub fn new(
|
pub fn new(
|
||||||
ctx: &mut EventCtx,
|
ctx: &mut EventCtx,
|
||||||
app: &App,
|
app: &dyn AppLike,
|
||||||
counts_a: Counts,
|
counts_a: Counts,
|
||||||
counts_b: Counts,
|
counts_b: Counts,
|
||||||
layer: Layer,
|
layer: Layer,
|
||||||
@ -123,7 +140,7 @@ impl CompareCounts {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn recalculate_b(&mut self, ctx: &EventCtx, app: &App, counts_b: Counts) {
|
pub fn recalculate_b(&mut self, ctx: &EventCtx, app: &dyn AppLike, counts_b: Counts) {
|
||||||
self.counts_b = CountsUI::new(ctx, app, counts_b);
|
self.counts_b = CountsUI::new(ctx, app, counts_b);
|
||||||
self.relative_heatmap =
|
self.relative_heatmap =
|
||||||
calculate_relative_heatmap(ctx, app, &self.counts_a, &self.counts_b);
|
calculate_relative_heatmap(ctx, app, &self.counts_a, &self.counts_b);
|
||||||
@ -152,10 +169,11 @@ impl CompareCounts {
|
|||||||
ctx,
|
ctx,
|
||||||
"layer",
|
"layer",
|
||||||
self.layer,
|
self.layer,
|
||||||
|
// TODO A dropdown is actually annoying, the hotkeys don't work without a click
|
||||||
vec![
|
vec![
|
||||||
Choice::new(&self.counts_a.description, Layer::A),
|
Choice::new(&self.counts_a.description, Layer::A).key(Key::Num1),
|
||||||
Choice::new(&self.counts_b.description, Layer::B),
|
Choice::new(&self.counts_b.description, Layer::B).key(Key::Num2),
|
||||||
Choice::new("compare", Layer::Compare),
|
Choice::new("compare", Layer::Compare).key(Key::Num3),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
])
|
])
|
||||||
@ -198,16 +216,27 @@ impl CompareCounts {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn relative_road_tooltip(&self, r: RoadID) -> Text {
|
fn relative_road_tooltip(&self, r: RoadID) -> Text {
|
||||||
let before = self.counts_a.per_road.get(r);
|
let a = self.counts_a.per_road.get(r);
|
||||||
let after = self.counts_b.per_road.get(r);
|
let b = self.counts_b.per_road.get(r);
|
||||||
let ratio = (after as f64) / (before as f64);
|
let ratio = (b as f64) / (a as f64);
|
||||||
|
|
||||||
let mut txt = Text::from_multiline(vec![
|
let mut txt = Text::from_multiline(vec![
|
||||||
Line(format!("Before: {}", prettyprint_usize(before))),
|
Line(format!(
|
||||||
Line(format!("After: {}", prettyprint_usize(after))),
|
"{}: {}",
|
||||||
|
self.counts_a.description,
|
||||||
|
prettyprint_usize(a)
|
||||||
|
)),
|
||||||
|
Line(format!(
|
||||||
|
"{}: {}",
|
||||||
|
self.counts_b.description,
|
||||||
|
prettyprint_usize(b)
|
||||||
|
)),
|
||||||
]);
|
]);
|
||||||
cmp_count(&mut txt, before, after);
|
cmp_count(&mut txt, a, b);
|
||||||
txt.add_line(Line(format!("After/before: {:.2}", ratio)));
|
txt.add_line(Line(format!(
|
||||||
|
"{}/{}: {:.2}",
|
||||||
|
self.counts_b.description, self.counts_a.description, ratio
|
||||||
|
)));
|
||||||
txt
|
txt
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -229,7 +258,7 @@ impl CompareCounts {
|
|||||||
|
|
||||||
fn calculate_relative_heatmap(
|
fn calculate_relative_heatmap(
|
||||||
ctx: &EventCtx,
|
ctx: &EventCtx,
|
||||||
app: &App,
|
app: &dyn AppLike,
|
||||||
counts_a: &CountsUI,
|
counts_a: &CountsUI,
|
||||||
counts_b: &CountsUI,
|
counts_b: &CountsUI,
|
||||||
) -> ToggleZoomed {
|
) -> ToggleZoomed {
|
||||||
@ -247,7 +276,7 @@ fn calculate_relative_heatmap(
|
|||||||
|
|
||||||
// What's physical road width look like?
|
// What's physical road width look like?
|
||||||
let mut hgram_width = Histogram::new();
|
let mut hgram_width = Histogram::new();
|
||||||
for r in app.map.all_roads() {
|
for r in app.map().all_roads() {
|
||||||
hgram_width.add(r.get_width());
|
hgram_width.add(r.get_width());
|
||||||
}
|
}
|
||||||
info!("Physical road widths: {}", hgram_width.describe());
|
info!("Physical road widths: {}", hgram_width.describe());
|
||||||
@ -275,14 +304,14 @@ fn calculate_relative_heatmap(
|
|||||||
// TODO Pretty arbitrary. Ideally we'd hide roads and intersections underneath...
|
// TODO Pretty arbitrary. Ideally we'd hide roads and intersections underneath...
|
||||||
let width = Distance::meters(2.0) + pct_count * Distance::meters(10.0);
|
let width = Distance::meters(2.0) + pct_count * Distance::meters(10.0);
|
||||||
|
|
||||||
draw_roads.push(color, app.map.get_r(r).center_pts.make_polygons(width));
|
draw_roads.push(color, app.map().get_r(r).center_pts.make_polygons(width));
|
||||||
}
|
}
|
||||||
ToggleZoomedBuilder::from(draw_roads).build(ctx)
|
ToggleZoomedBuilder::from(draw_roads).build(ctx)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn make_world(ctx: &mut EventCtx, app: &App) -> World<Obj> {
|
fn make_world(ctx: &mut EventCtx, app: &dyn AppLike) -> World<Obj> {
|
||||||
let mut world = World::bounded(app.map.get_bounds());
|
let mut world = World::bounded(app.map().get_bounds());
|
||||||
for r in app.map.all_roads() {
|
for r in app.map().all_roads() {
|
||||||
world
|
world
|
||||||
.add(Obj::Road(r.id))
|
.add(Obj::Road(r.id))
|
||||||
.hitbox(r.get_thick_polygon())
|
.hitbox(r.get_thick_polygon())
|
||||||
@ -290,7 +319,7 @@ fn make_world(ctx: &mut EventCtx, app: &App) -> World<Obj> {
|
|||||||
.invisibly_hoverable()
|
.invisibly_hoverable()
|
||||||
.build(ctx);
|
.build(ctx);
|
||||||
}
|
}
|
||||||
for i in app.map.all_intersections() {
|
for i in app.map().all_intersections() {
|
||||||
world
|
world
|
||||||
.add(Obj::Intersection(i.id))
|
.add(Obj::Intersection(i.id))
|
||||||
.hitbox(i.polygon.clone())
|
.hitbox(i.polygon.clone())
|
@ -36,6 +36,7 @@ mod city_picker;
|
|||||||
mod colors;
|
mod colors;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
mod command;
|
mod command;
|
||||||
|
pub mod compare_counts;
|
||||||
mod heatmap;
|
mod heatmap;
|
||||||
mod icons;
|
mod icons;
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
Loading…
Reference in New Issue
Block a user