Warn the user before doing expensive mode shift calculations. #448 (#774)

This commit is contained in:
Dustin Carlino 2021-10-11 15:10:28 -07:00 committed by GitHub
parent 112848f23b
commit 915b902c9a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 138 additions and 115 deletions

View File

@ -136,6 +136,12 @@ impl Manifest {
}
None
}
/// Look up an entry.
pub fn get_entry(&self, path: &str) -> Option<&Entry> {
let path = path.strip_prefix(&crate::path("")).unwrap_or(path);
self.entries.get(&format!("data/{}", path))
}
}
/// Player-chosen groups of files to opt into downloading

View File

@ -35,6 +35,18 @@ pub fn prettyprint_usize(x: usize) -> String {
result
}
pub fn prettyprint_bytes(bytes: u64) -> String {
if bytes < 1024 {
return format!("{} bytes", bytes);
}
let kb = (bytes as f64) / 1024.0;
if kb < 1024.0 {
return format!("{} KB", kb as usize);
}
let mb = kb / 1024.0;
format!("{} MB", mb as usize)
}
pub fn abbreviated_format(x: usize) -> String {
if x >= 1000 {
let ks = x as f32 / 1000.0;

View File

@ -1,6 +1,7 @@
use std::collections::HashSet;
use abstutil::{prettyprint_usize, Counter, Timer};
use abstio::Manifest;
use abstutil::{prettyprint_bytes, prettyprint_usize, Counter, Timer};
use geom::{Distance, Duration, Polygon, UnitFmt};
use map_gui::load::FileLoader;
use map_gui::tools::{open_browser, ColorNetwork};
@ -29,39 +30,11 @@ impl TakeLayers for ShowGaps {
impl ShowGaps {
pub fn new_state(ctx: &mut EventCtx, app: &mut App, layers: Layers) -> Box<dyn State<App>> {
let map_name = app.primary.map.get_name().clone();
let change_key = app.primary.map.get_edits_change_key();
if app.session.mode_shift.key().as_ref() == Some(&(map_name.clone(), change_key)) {
return Box::new(ShowGaps {
Box::new(ShowGaps {
top_panel: make_top_panel(ctx, app),
layers,
tooltip: None,
});
}
let scenario_name = crate::pregame::default_scenario_for_map(&map_name);
if scenario_name == "home_to_work" {
// TODO Should we generate and use this scenario? Or maybe just disable this mode
// entirely?
app.session
.mode_shift
.set((map_name, change_key), ModeShiftData::empty(ctx));
ShowGaps::new_state(ctx, app, layers)
} else {
FileLoader::<App, Scenario>::new_state(
ctx,
abstio::path_scenario(&map_name, &scenario_name),
Box::new(move |ctx, app, _, maybe_scenario| {
// TODO Handle corrupt files
let scenario = maybe_scenario.unwrap();
let data = ctx.loading_screen("predict mode shift", |ctx, timer| {
ModeShiftData::from_scenario(ctx, app, scenario, timer)
});
app.session.mode_shift.set((map_name, change_key), data);
Transition::Replace(ShowGaps::new_state(ctx, app, layers))
}),
)
}
})
}
}
@ -70,12 +43,12 @@ impl State<App> for ShowGaps {
ctx.canvas_movement();
if ctx.redo_mouseover() {
self.tooltip = None;
if let Some(data) = app.session.mode_shift.value() {
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 data = app.session.mode_shift.value().unwrap();
let count = data.gaps.count_per_road.get(r);
if count > 0 {
// TODO Word more precisely... or less verbosely.
@ -86,12 +59,35 @@ impl State<App> for ShowGaps {
}
}
}
}
match self.top_panel.event(ctx) {
Outcome::Clicked(x) => {
if x == "read about how this prediction works" {
open_browser("https://a-b-street.github.io/docs/software/bike_network/tech_details.html#predict-impact");
return Transition::Keep;
} else if x == "Calculate" {
let change_key = app.primary.map.get_edits_change_key();
let map_name = app.primary.map.get_name().clone();
let scenario_name = crate::pregame::default_scenario_for_map(&map_name);
return Transition::Push(FileLoader::<App, Scenario>::new_state(
ctx,
abstio::path_scenario(&map_name, &scenario_name),
Box::new(move |ctx, app, timer, maybe_scenario| {
// TODO Handle corrupt files
let scenario = maybe_scenario.unwrap();
let data = ModeShiftData::from_scenario(ctx, app, scenario, timer);
app.session.mode_shift.set((map_name, change_key), data);
Transition::Multi(vec![
Transition::Pop,
Transition::ConsumeState(Box::new(|state, ctx, app| {
let state = state.downcast::<ShowGaps>().ok().unwrap();
vec![ShowGaps::new_state(ctx, app, state.take_layers())]
})),
])
}),
));
}
return Tab::PredictImpact
@ -122,8 +118,9 @@ impl State<App> for ShowGaps {
self.top_panel.draw(g);
self.layers.draw(g, app);
let data = app.session.mode_shift.value().unwrap();
if let Some(data) = app.session.mode_shift.value() {
data.gaps.draw.draw(g);
}
if let Some(ref txt) = self.tooltip {
g.draw_mouse_tooltip(txt.clone());
}
@ -131,19 +128,14 @@ impl State<App> for ShowGaps {
}
fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
let map_name = app.primary.map.get_name().clone();
let change_key = app.primary.map.get_edits_change_key();
let col;
if app.session.mode_shift.key().as_ref() == Some(&(map_name.clone(), change_key)) {
let data = app.session.mode_shift.value().unwrap();
if data.all_candidate_trips.is_empty() {
return Tab::PredictImpact.make_left_panel(
ctx,
app,
Widget::col(vec![
"This city doesn't have travel demand model data available".text_widget(ctx),
]),
);
}
let col = vec![
col = vec![
ctx.style()
.btn_plain
.icon_text(
@ -188,6 +180,30 @@ fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
])
.section(ctx),
];
} else {
let scenario_name = crate::pregame::default_scenario_for_map(&map_name);
if scenario_name == "home_to_work" {
col =
vec!["This city doesn't have travel demand model data available".text_widget(ctx)];
} else {
let size = Manifest::load()
.get_entry(&abstio::path_scenario(&map_name, &scenario_name))
.map(|entry| prettyprint_bytes(entry.compressed_size_bytes))
.unwrap_or("???".to_string());
col = vec![
Text::from_multiline(vec![
Line("Predicting impact of your proposal may take a moment."),
Line("The application may freeze up during that time."),
Line(format!("We need to load a {} file", size)),
])
.into_widget(ctx),
ctx.style()
.btn_solid_primary
.text("Calculate")
.build_def(ctx),
];
}
}
Tab::PredictImpact.make_left_panel(ctx, app, Widget::col(col))
}

View File

@ -4,6 +4,7 @@ use std::fs::File;
use futures_channel::mpsc;
use abstio::{DataPacks, Manifest, MapName};
use abstutil::prettyprint_bytes;
use widgetry::{EventCtx, Key, Transition};
use crate::load::FutureLoader;
@ -31,18 +32,6 @@ fn size_of_city(map: &MapName) -> u64 {
bytes
}
fn prettyprint_bytes(bytes: u64) -> String {
if bytes < 1024 {
return format!("{} bytes", bytes);
}
let kb = (bytes as f64) / 1024.0;
if kb < 1024.0 {
return format!("{} KB", kb as usize);
}
let mb = kb / 1024.0;
format!("{} MB", mb as usize)
}
/// Prompt to download a missing city. On either success or failure (maybe the player choosing to
/// not download, maybe a network error), the new map isn't automatically loaded or anything; up to
/// the caller to handle that.