mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 17:37:22 +03:00
Flesh out the LTN impact tool -- calculate impact after, add tooltips
This commit is contained in:
parent
fc705318a9
commit
c6e26e5e20
@ -3,7 +3,7 @@ use std::collections::HashMap;
|
|||||||
use abstutil::{prettyprint_usize, Counter, Timer};
|
use abstutil::{prettyprint_usize, Counter, Timer};
|
||||||
use geom::{Duration, Polygon};
|
use geom::{Duration, Polygon};
|
||||||
use map_gui::colors::ColorSchemeChoice;
|
use map_gui::colors::ColorSchemeChoice;
|
||||||
use map_gui::tools::ColorNetwork;
|
use map_gui::tools::{cmp_count, ColorNetwork};
|
||||||
use map_gui::{AppLike, ID};
|
use map_gui::{AppLike, ID};
|
||||||
use map_model::{
|
use map_model::{
|
||||||
DirectedRoadID, Direction, PathRequest, RoadID, RoutingParams, Traversable,
|
DirectedRoadID, Direction, PathRequest, RoadID, RoutingParams, Traversable,
|
||||||
@ -450,7 +450,7 @@ impl State<App> for AllRoutesExplorer {
|
|||||||
let baseline = self.baseline_counts.get(r);
|
let baseline = self.baseline_counts.get(r);
|
||||||
let current = self.current_counts.get(r);
|
let current = self.current_counts.get(r);
|
||||||
let mut txt = Text::new();
|
let mut txt = Text::new();
|
||||||
txt.append_all(cmp_count(current, baseline));
|
cmp_count(&mut txt, baseline, current);
|
||||||
txt.add_line(format!("{} baseline", prettyprint_usize(baseline)));
|
txt.add_line(format!("{} baseline", prettyprint_usize(baseline)));
|
||||||
txt.add_line(format!("{} now", prettyprint_usize(current)));
|
txt.add_line(format!("{} now", prettyprint_usize(current)));
|
||||||
self.tooltip = Some(txt);
|
self.tooltip = Some(txt);
|
||||||
@ -490,26 +490,6 @@ fn calculate_demand(app: &App, requests: &[PathRequest], timer: &mut Timer) -> C
|
|||||||
counter
|
counter
|
||||||
}
|
}
|
||||||
|
|
||||||
fn cmp_count(after: usize, before: usize) -> Vec<TextSpan> {
|
|
||||||
match after.cmp(&before) {
|
|
||||||
std::cmp::Ordering::Equal => {
|
|
||||||
vec![Line("same")]
|
|
||||||
}
|
|
||||||
std::cmp::Ordering::Less => {
|
|
||||||
vec![
|
|
||||||
Line(prettyprint_usize(before - after)).fg(Color::GREEN),
|
|
||||||
Line(" less"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
std::cmp::Ordering::Greater => {
|
|
||||||
vec![
|
|
||||||
Line(prettyprint_usize(after - before)).fg(Color::RED),
|
|
||||||
Line(" more"),
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Evaluate why an alternative path wasn't chosen, by showing the cost to reach every road from
|
/// Evaluate why an alternative path wasn't chosen, by showing the cost to reach every road from
|
||||||
/// one start.
|
/// one start.
|
||||||
pub struct PathCostDebugger {
|
pub struct PathCostDebugger {
|
||||||
|
@ -125,6 +125,10 @@ impl State<App> for BrowseNeighborhoods {
|
|||||||
"Calculate" => {
|
"Calculate" => {
|
||||||
return Transition::Push(super::impact::ShowResults::new_state(ctx, app));
|
return Transition::Push(super::impact::ShowResults::new_state(ctx, app));
|
||||||
}
|
}
|
||||||
|
"Force recalculation" => {
|
||||||
|
app.session.impact = None;
|
||||||
|
return Transition::Push(super::impact::ShowResults::new_state(ctx, app));
|
||||||
|
}
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
},
|
},
|
||||||
Outcome::Changed(_) => {
|
Outcome::Changed(_) => {
|
||||||
@ -270,9 +274,16 @@ fn impact_widget(ctx: &EventCtx, app: &App) -> Widget {
|
|||||||
Line(format!("We need to load a {} file", size)),
|
Line(format!("We need to load a {} file", size)),
|
||||||
])
|
])
|
||||||
.into_widget(ctx),
|
.into_widget(ctx),
|
||||||
ctx.style()
|
Widget::row(vec![
|
||||||
.btn_solid_primary
|
ctx.style()
|
||||||
.text("Calculate")
|
.btn_solid_primary
|
||||||
.build_def(ctx),
|
.text("Calculate")
|
||||||
|
.build_def(ctx),
|
||||||
|
// TODO Bad UI! Detect edits and do this. I'm being lazy.
|
||||||
|
ctx.style()
|
||||||
|
.btn_solid_primary
|
||||||
|
.text("Force recalculation")
|
||||||
|
.build_def(ctx),
|
||||||
|
]),
|
||||||
])
|
])
|
||||||
}
|
}
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
use abstio::MapName;
|
use abstio::MapName;
|
||||||
use abstutil::{Counter, Timer};
|
use abstutil::{prettyprint_usize, Counter, Timer};
|
||||||
use map_gui::load::FileLoader;
|
use map_gui::load::FileLoader;
|
||||||
use map_gui::tools::ColorNetwork;
|
use map_gui::tools::{cmp_count, ColorNetwork};
|
||||||
|
use map_gui::ID;
|
||||||
use map_model::{PathRequest, PathStepV2, RoadID};
|
use map_model::{PathRequest, PathStepV2, RoadID};
|
||||||
use sim::{Scenario, TripEndpoint, TripMode};
|
use sim::{Scenario, TripEndpoint, TripMode};
|
||||||
use widgetry::mapspace::ToggleZoomed;
|
use widgetry::mapspace::ToggleZoomed;
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
EventCtx, GfxCtx, HorizontalAlignment, Panel, SimpleState, State, TextExt, VerticalAlignment,
|
Choice, EventCtx, GfxCtx, HorizontalAlignment, Line, Panel, SimpleState, State, Text, TextExt,
|
||||||
Widget,
|
VerticalAlignment, Widget,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{App, Transition};
|
use crate::{App, Transition};
|
||||||
|
|
||||||
// TODO Tooltips
|
|
||||||
// TODO Intersections
|
// TODO Intersections
|
||||||
// TODO Toggle before/after / compare directly
|
// 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?
|
||||||
|
|
||||||
pub struct Results {
|
pub struct Results {
|
||||||
map: MapName,
|
map: MapName,
|
||||||
@ -66,12 +68,15 @@ impl Results {
|
|||||||
fn recalculate_impact(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
|
fn recalculate_impact(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
|
||||||
self.before_counts = Counter::new();
|
self.before_counts = Counter::new();
|
||||||
self.after_counts = Counter::new();
|
self.after_counts = Counter::new();
|
||||||
|
|
||||||
let map = &app.map;
|
let map = &app.map;
|
||||||
|
|
||||||
|
// Before the filters
|
||||||
for path in timer
|
for path in timer
|
||||||
.parallelize("calculate routes", self.all_driving_trips.clone(), |req| {
|
.parallelize(
|
||||||
map.pathfind_v2(req)
|
"calculate routes before filters",
|
||||||
})
|
self.all_driving_trips.clone(),
|
||||||
|
|req| map.pathfind_v2(req),
|
||||||
|
)
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
{
|
{
|
||||||
@ -82,14 +87,46 @@ impl Results {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut colorer = ColorNetwork::no_fading(app);
|
let mut colorer = ColorNetwork::no_fading(app);
|
||||||
colorer.ranked_roads(self.before_counts.clone(), &app.cs.good_to_bad_red);
|
colorer.ranked_roads(self.before_counts.clone(), &app.cs.good_to_bad_red);
|
||||||
self.before_draw_heatmap = colorer.build(ctx);
|
self.before_draw_heatmap = colorer.build(ctx);
|
||||||
|
|
||||||
|
// After the filters
|
||||||
|
let mut params = map.routing_params().clone();
|
||||||
|
app.session.modal_filters.update_routing_params(&mut params);
|
||||||
|
let cache_custom = true;
|
||||||
|
for path in timer
|
||||||
|
.parallelize(
|
||||||
|
"calculate routes after filters",
|
||||||
|
self.all_driving_trips.clone(),
|
||||||
|
|req| map.pathfind_v2_with_params(req, ¶ms, cache_custom),
|
||||||
|
)
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
{
|
||||||
|
for step in path.get_steps() {
|
||||||
|
// No Contraflow steps for driving paths
|
||||||
|
if let PathStepV2::Along(dr) = step {
|
||||||
|
self.after_counts.inc(dr.road);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let mut colorer = ColorNetwork::no_fading(app);
|
||||||
|
colorer.ranked_roads(self.after_counts.clone(), &app.cs.good_to_bad_red);
|
||||||
|
self.after_draw_heatmap = colorer.build(ctx);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct ShowResults;
|
#[derive(Clone, Copy, Debug, PartialEq)]
|
||||||
|
enum Layer {
|
||||||
|
Before,
|
||||||
|
After,
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct ShowResults {
|
||||||
|
layer: Layer,
|
||||||
|
tooltip: Option<Text>,
|
||||||
|
}
|
||||||
|
|
||||||
impl ShowResults {
|
impl ShowResults {
|
||||||
pub fn new_state(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
pub fn new_state(ctx: &mut EventCtx, app: &App) -> Box<dyn State<App>> {
|
||||||
@ -115,35 +152,89 @@ impl ShowResults {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let layer = Layer::Before;
|
||||||
let panel = Panel::new_builder(Widget::col(vec![
|
let panel = Panel::new_builder(Widget::col(vec![
|
||||||
map_gui::tools::app_header(ctx, app, "Low traffic neighborhoods"),
|
map_gui::tools::app_header(ctx, app, "Low traffic neighborhoods"),
|
||||||
Widget::row(vec![
|
Widget::row(vec![
|
||||||
"Impact prediction".text_widget(ctx),
|
"Impact prediction".text_widget(ctx),
|
||||||
ctx.style().btn_close_widget(ctx),
|
ctx.style().btn_close_widget(ctx),
|
||||||
]),
|
]),
|
||||||
|
"This shows how many driving trips cross each road".text_widget(ctx),
|
||||||
|
Widget::row(vec![
|
||||||
|
"Show what?".text_widget(ctx).centered_vert(),
|
||||||
|
Widget::dropdown(
|
||||||
|
ctx,
|
||||||
|
"layer",
|
||||||
|
layer,
|
||||||
|
vec![
|
||||||
|
Choice::new("before", Layer::Before),
|
||||||
|
Choice::new("after", Layer::After),
|
||||||
|
],
|
||||||
|
),
|
||||||
|
]),
|
||||||
]))
|
]))
|
||||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||||
.build(ctx);
|
.build(ctx);
|
||||||
<dyn SimpleState<_>>::new_state(panel, Box::new(ShowResults))
|
<dyn SimpleState<_>>::new_state(
|
||||||
|
panel,
|
||||||
|
Box::new(ShowResults {
|
||||||
|
layer,
|
||||||
|
tooltip: None,
|
||||||
|
}),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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, _: &mut EventCtx, _: &mut App, x: &str, _: &Panel) -> Transition {
|
||||||
if x == "close" {
|
if x == "close" {
|
||||||
return Transition::Pop;
|
return Transition::Pop;
|
||||||
}
|
}
|
||||||
unreachable!()
|
unreachable!()
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO Or on_mouseover?
|
fn on_mouseover(&mut self, ctx: &mut EventCtx, app: &mut App) {
|
||||||
fn other_event(&mut self, ctx: &mut EventCtx, _: &mut App) -> Transition {
|
|
||||||
ctx.canvas_movement();
|
ctx.canvas_movement();
|
||||||
Transition::Keep
|
self.tooltip = None;
|
||||||
|
if let Some(r) = match app.mouseover_unzoomed_roads_and_intersections(ctx) {
|
||||||
|
Some(ID::Road(r)) => Some(r),
|
||||||
|
Some(ID::Lane(l)) => Some(l.road),
|
||||||
|
_ => None,
|
||||||
|
} {
|
||||||
|
let impact = app.session.impact.as_ref().unwrap();
|
||||||
|
let before = impact.before_counts.get(r);
|
||||||
|
let after = impact.after_counts.get(r);
|
||||||
|
let mut txt = Text::from_multiline(vec![
|
||||||
|
Line(format!("Before: {}", prettyprint_usize(before))),
|
||||||
|
Line(format!("After: {}", prettyprint_usize(after))),
|
||||||
|
]);
|
||||||
|
cmp_count(&mut txt, before, after);
|
||||||
|
self.tooltip = Some(txt);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn panel_changed(
|
||||||
|
&mut self,
|
||||||
|
_: &mut EventCtx,
|
||||||
|
_: &mut App,
|
||||||
|
panel: &mut Panel,
|
||||||
|
) -> Option<Transition> {
|
||||||
|
self.layer = panel.dropdown_value("layer");
|
||||||
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
fn draw(&self, g: &mut GfxCtx, app: &App) {
|
||||||
let impact = app.session.impact.as_ref().unwrap();
|
let impact = app.session.impact.as_ref().unwrap();
|
||||||
impact.before_draw_heatmap.draw(g);
|
match self.layer {
|
||||||
|
Layer::Before => {
|
||||||
|
impact.before_draw_heatmap.draw(g);
|
||||||
|
}
|
||||||
|
Layer::After => {
|
||||||
|
impact.after_draw_heatmap.draw(g);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if let Some(ref txt) = self.tooltip {
|
||||||
|
g.draw_mouse_tooltip(txt.clone());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -126,6 +126,8 @@ impl RoutePlanner {
|
|||||||
let mut draw = ToggleZoomed::builder();
|
let mut draw = ToggleZoomed::builder();
|
||||||
|
|
||||||
// First the route respecting the filters
|
// First the route respecting the filters
|
||||||
|
// TODO Like in rat_runs, we need to actually enforce this, not just penalize... though,
|
||||||
|
// should it matter except for when a cell is disconnected?
|
||||||
let (total_time_after, total_dist_after) = {
|
let (total_time_after, total_dist_after) = {
|
||||||
let mut params = map.routing_params().clone();
|
let mut params = map.routing_params().clone();
|
||||||
app.session.modal_filters.update_routing_params(&mut params);
|
app.session.modal_filters.update_routing_params(&mut params);
|
||||||
|
@ -19,7 +19,8 @@ pub use self::title_screen::{Executable, TitleScreen};
|
|||||||
pub use self::trip_files::{TripManagement, TripManagementState};
|
pub use self::trip_files::{TripManagement, TripManagementState};
|
||||||
pub use self::turn_explorer::TurnExplorer;
|
pub use self::turn_explorer::TurnExplorer;
|
||||||
pub use self::ui::{
|
pub use self::ui::{
|
||||||
cmp_dist, cmp_duration, percentage_bar, ChooseSomething, FilePicker, PopupMsg, PromptInput,
|
cmp_count, cmp_dist, cmp_duration, percentage_bar, ChooseSomething, FilePicker, PopupMsg,
|
||||||
|
PromptInput,
|
||||||
};
|
};
|
||||||
pub use self::url::URLManager;
|
pub use self::url::URLManager;
|
||||||
pub use self::waypoints::{InputWaypoints, WaypointID};
|
pub use self::waypoints::{InputWaypoints, WaypointID};
|
||||||
|
@ -4,6 +4,7 @@ use std::cmp::Ordering;
|
|||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use abstutil::prettyprint_usize;
|
||||||
use geom::{Distance, Duration, Polygon};
|
use geom::{Distance, Duration, Polygon};
|
||||||
use widgetry::{
|
use widgetry::{
|
||||||
hotkeys, Choice, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key, Line, Menu, Outcome,
|
hotkeys, Choice, Color, DrawBaselayer, EventCtx, GeomBatch, GfxCtx, Key, Line, Menu, Outcome,
|
||||||
@ -316,3 +317,24 @@ pub fn cmp_duration(
|
|||||||
Ordering::Equal => {}
|
Ordering::Equal => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Less is better
|
||||||
|
pub fn cmp_count(txt: &mut Text, before: usize, after: usize) {
|
||||||
|
match after.cmp(&before) {
|
||||||
|
std::cmp::Ordering::Equal => {
|
||||||
|
txt.add_line(Line("same"));
|
||||||
|
}
|
||||||
|
std::cmp::Ordering::Less => {
|
||||||
|
txt.add_appended(vec![
|
||||||
|
Line(prettyprint_usize(before - after)).fg(Color::GREEN),
|
||||||
|
Line(" less"),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
std::cmp::Ordering::Greater => {
|
||||||
|
txt.add_appended(vec![
|
||||||
|
Line(prettyprint_usize(after - before)).fg(Color::RED),
|
||||||
|
Line(" more"),
|
||||||
|
]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user