mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 15:02:59 +03:00
This commit is contained in:
parent
112848f23b
commit
915b902c9a
@ -136,6 +136,12 @@ impl Manifest {
|
|||||||
}
|
}
|
||||||
None
|
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
|
/// Player-chosen groups of files to opt into downloading
|
||||||
|
@ -35,6 +35,18 @@ pub fn prettyprint_usize(x: usize) -> String {
|
|||||||
result
|
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 {
|
pub fn abbreviated_format(x: usize) -> String {
|
||||||
if x >= 1000 {
|
if x >= 1000 {
|
||||||
let ks = x as f32 / 1000.0;
|
let ks = x as f32 / 1000.0;
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
use std::collections::HashSet;
|
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 geom::{Distance, Duration, Polygon, UnitFmt};
|
||||||
use map_gui::load::FileLoader;
|
use map_gui::load::FileLoader;
|
||||||
use map_gui::tools::{open_browser, ColorNetwork};
|
use map_gui::tools::{open_browser, ColorNetwork};
|
||||||
@ -29,39 +30,11 @@ impl TakeLayers for ShowGaps {
|
|||||||
|
|
||||||
impl ShowGaps {
|
impl ShowGaps {
|
||||||
pub fn new_state(ctx: &mut EventCtx, app: &mut App, layers: Layers) -> Box<dyn State<App>> {
|
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();
|
Box::new(ShowGaps {
|
||||||
let change_key = app.primary.map.get_edits_change_key();
|
top_panel: make_top_panel(ctx, app),
|
||||||
if app.session.mode_shift.key().as_ref() == Some(&(map_name.clone(), change_key)) {
|
layers,
|
||||||
return Box::new(ShowGaps {
|
tooltip: None,
|
||||||
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,19 +43,20 @@ impl State<App> for ShowGaps {
|
|||||||
ctx.canvas_movement();
|
ctx.canvas_movement();
|
||||||
if ctx.redo_mouseover() {
|
if ctx.redo_mouseover() {
|
||||||
self.tooltip = None;
|
self.tooltip = None;
|
||||||
if let Some(r) = match app.mouseover_unzoomed_roads_and_intersections(ctx) {
|
if let Some(data) = app.session.mode_shift.value() {
|
||||||
Some(ID::Road(r)) => Some(r),
|
if let Some(r) = match app.mouseover_unzoomed_roads_and_intersections(ctx) {
|
||||||
Some(ID::Lane(l)) => Some(l.road),
|
Some(ID::Road(r)) => Some(r),
|
||||||
_ => None,
|
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);
|
let count = data.gaps.count_per_road.get(r);
|
||||||
if count > 0 {
|
if count > 0 {
|
||||||
// TODO Word more precisely... or less verbosely.
|
// TODO Word more precisely... or less verbosely.
|
||||||
self.tooltip = Some(Text::from(Line(format!(
|
self.tooltip = Some(Text::from(Line(format!(
|
||||||
"{} trips might cross this high-stress road",
|
"{} trips might cross this high-stress road",
|
||||||
prettyprint_usize(count)
|
prettyprint_usize(count)
|
||||||
))));
|
))));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -92,6 +66,28 @@ impl State<App> for ShowGaps {
|
|||||||
if x == "read about how this prediction works" {
|
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");
|
open_browser("https://a-b-street.github.io/docs/software/bike_network/tech_details.html#predict-impact");
|
||||||
return Transition::Keep;
|
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
|
return Tab::PredictImpact
|
||||||
@ -122,8 +118,9 @@ impl State<App> for ShowGaps {
|
|||||||
self.top_panel.draw(g);
|
self.top_panel.draw(g);
|
||||||
self.layers.draw(g, app);
|
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);
|
data.gaps.draw.draw(g);
|
||||||
|
}
|
||||||
if let Some(ref txt) = self.tooltip {
|
if let Some(ref txt) = self.tooltip {
|
||||||
g.draw_mouse_tooltip(txt.clone());
|
g.draw_mouse_tooltip(txt.clone());
|
||||||
}
|
}
|
||||||
@ -131,64 +128,83 @@ impl State<App> for ShowGaps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
|
fn make_top_panel(ctx: &mut EventCtx, app: &App) -> Panel {
|
||||||
let data = app.session.mode_shift.value().unwrap();
|
let map_name = app.primary.map.get_name().clone();
|
||||||
|
let change_key = app.primary.map.get_edits_change_key();
|
||||||
|
let col;
|
||||||
|
|
||||||
if data.all_candidate_trips.is_empty() {
|
if app.session.mode_shift.key().as_ref() == Some(&(map_name.clone(), change_key)) {
|
||||||
return Tab::PredictImpact.make_left_panel(
|
let data = app.session.mode_shift.value().unwrap();
|
||||||
ctx,
|
|
||||||
app,
|
col = vec![
|
||||||
|
ctx.style()
|
||||||
|
.btn_plain
|
||||||
|
.icon_text(
|
||||||
|
"system/assets/tools/info.svg",
|
||||||
|
"How many drivers might switch to biking?",
|
||||||
|
)
|
||||||
|
.build_widget(ctx, "read about how this prediction works"),
|
||||||
|
percentage_bar(
|
||||||
|
ctx,
|
||||||
|
Text::from(Line(format!(
|
||||||
|
"{} total driving trips in this area",
|
||||||
|
prettyprint_usize(data.all_candidate_trips.len())
|
||||||
|
))),
|
||||||
|
0.0,
|
||||||
|
),
|
||||||
Widget::col(vec![
|
Widget::col(vec![
|
||||||
"This city doesn't have travel demand model data available".text_widget(ctx),
|
"Who might cycle if it was safer?".text_widget(ctx),
|
||||||
]),
|
data.filters.to_controls(ctx),
|
||||||
);
|
percentage_bar(
|
||||||
|
ctx,
|
||||||
|
Text::from(Line(format!(
|
||||||
|
"{} / {} trips, based on these thresholds",
|
||||||
|
data.filtered_trips.len(),
|
||||||
|
data.all_candidate_trips.len()
|
||||||
|
))),
|
||||||
|
pct(data.filtered_trips.len(), data.all_candidate_trips.len()),
|
||||||
|
),
|
||||||
|
])
|
||||||
|
.section(ctx),
|
||||||
|
Widget::col(vec![
|
||||||
|
"How many would switch based on your proposal?".text_widget(ctx),
|
||||||
|
percentage_bar(
|
||||||
|
ctx,
|
||||||
|
Text::from(Line(format!(
|
||||||
|
"{} / {} trips would switch",
|
||||||
|
data.results.num_trips,
|
||||||
|
data.all_candidate_trips.len()
|
||||||
|
))),
|
||||||
|
pct(data.results.num_trips, data.all_candidate_trips.len()),
|
||||||
|
),
|
||||||
|
data.results.describe().into_widget(ctx),
|
||||||
|
])
|
||||||
|
.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),
|
||||||
|
];
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let col = vec![
|
|
||||||
ctx.style()
|
|
||||||
.btn_plain
|
|
||||||
.icon_text(
|
|
||||||
"system/assets/tools/info.svg",
|
|
||||||
"How many drivers might switch to biking?",
|
|
||||||
)
|
|
||||||
.build_widget(ctx, "read about how this prediction works"),
|
|
||||||
percentage_bar(
|
|
||||||
ctx,
|
|
||||||
Text::from(Line(format!(
|
|
||||||
"{} total driving trips in this area",
|
|
||||||
prettyprint_usize(data.all_candidate_trips.len())
|
|
||||||
))),
|
|
||||||
0.0,
|
|
||||||
),
|
|
||||||
Widget::col(vec![
|
|
||||||
"Who might cycle if it was safer?".text_widget(ctx),
|
|
||||||
data.filters.to_controls(ctx),
|
|
||||||
percentage_bar(
|
|
||||||
ctx,
|
|
||||||
Text::from(Line(format!(
|
|
||||||
"{} / {} trips, based on these thresholds",
|
|
||||||
data.filtered_trips.len(),
|
|
||||||
data.all_candidate_trips.len()
|
|
||||||
))),
|
|
||||||
pct(data.filtered_trips.len(), data.all_candidate_trips.len()),
|
|
||||||
),
|
|
||||||
])
|
|
||||||
.section(ctx),
|
|
||||||
Widget::col(vec![
|
|
||||||
"How many would switch based on your proposal?".text_widget(ctx),
|
|
||||||
percentage_bar(
|
|
||||||
ctx,
|
|
||||||
Text::from(Line(format!(
|
|
||||||
"{} / {} trips would switch",
|
|
||||||
data.results.num_trips,
|
|
||||||
data.all_candidate_trips.len()
|
|
||||||
))),
|
|
||||||
pct(data.results.num_trips, data.all_candidate_trips.len()),
|
|
||||||
),
|
|
||||||
data.results.describe().into_widget(ctx),
|
|
||||||
])
|
|
||||||
.section(ctx),
|
|
||||||
];
|
|
||||||
|
|
||||||
Tab::PredictImpact.make_left_panel(ctx, app, Widget::col(col))
|
Tab::PredictImpact.make_left_panel(ctx, app, Widget::col(col))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,6 +4,7 @@ use std::fs::File;
|
|||||||
use futures_channel::mpsc;
|
use futures_channel::mpsc;
|
||||||
|
|
||||||
use abstio::{DataPacks, Manifest, MapName};
|
use abstio::{DataPacks, Manifest, MapName};
|
||||||
|
use abstutil::prettyprint_bytes;
|
||||||
use widgetry::{EventCtx, Key, Transition};
|
use widgetry::{EventCtx, Key, Transition};
|
||||||
|
|
||||||
use crate::load::FutureLoader;
|
use crate::load::FutureLoader;
|
||||||
@ -31,18 +32,6 @@ fn size_of_city(map: &MapName) -> u64 {
|
|||||||
bytes
|
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
|
/// 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
|
/// not download, maybe a network error), the new map isn't automatically loaded or anything; up to
|
||||||
/// the caller to handle that.
|
/// the caller to handle that.
|
||||||
|
Loading…
Reference in New Issue
Block a user