mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-12-24 23:15:24 +03:00
Show two LTN routes, to show effects of the new filters
This commit is contained in:
parent
08223efed5
commit
29fbf74c2d
@ -1,6 +1,7 @@
|
||||
use std::cmp::Ordering;
|
||||
use std::collections::BTreeSet;
|
||||
|
||||
use geom::{Duration, Polygon, Time};
|
||||
use geom::{Distance, Duration, Polygon, Time};
|
||||
use map_gui::ID;
|
||||
use map_model::{IntersectionID, Map, RoadID};
|
||||
use sim::{AgentType, TripMode, TripPhaseType};
|
||||
@ -369,6 +370,55 @@ pub fn cmp_duration_shorter(app: &App, after: Duration, before: Duration) -> Vec
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorter is better
|
||||
pub fn cmp_dist(txt: &mut Text, app: &App, dist: Distance, shorter: &str, longer: &str) {
|
||||
match dist.cmp(&Distance::ZERO) {
|
||||
Ordering::Less => {
|
||||
txt.add_line(
|
||||
Line(format!(
|
||||
"{} {}",
|
||||
(-dist).to_string(&app.opts.units),
|
||||
shorter
|
||||
))
|
||||
.fg(Color::GREEN),
|
||||
);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
txt.add_line(
|
||||
Line(format!("{} {}", dist.to_string(&app.opts.units), longer)).fg(Color::RED),
|
||||
);
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
}
|
||||
|
||||
/// Shorter is better
|
||||
pub fn cmp_duration(txt: &mut Text, app: &App, duration: Duration, shorter: &str, longer: &str) {
|
||||
match duration.cmp(&Duration::ZERO) {
|
||||
Ordering::Less => {
|
||||
txt.add_line(
|
||||
Line(format!(
|
||||
"{} {}",
|
||||
(-duration).to_string(&app.opts.units),
|
||||
shorter
|
||||
))
|
||||
.fg(Color::GREEN),
|
||||
);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
txt.add_line(
|
||||
Line(format!(
|
||||
"{} {}",
|
||||
duration.to_string(&app.opts.units),
|
||||
longer
|
||||
))
|
||||
.fg(Color::RED),
|
||||
);
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
}
|
||||
|
||||
pub fn color_for_mode(app: &App, m: TripMode) -> Color {
|
||||
match m {
|
||||
TripMode::Walk => app.cs.unzoomed_pedestrian,
|
||||
|
@ -1,15 +1,15 @@
|
||||
use geom::{Distance, Polygon};
|
||||
use geom::{Distance, Duration, Polygon};
|
||||
use map_model::NORMAL_LANE_THICKNESS;
|
||||
use sim::{TripEndpoint, TripMode};
|
||||
use widgetry::mapspace::{ObjectID, ToggleZoomed, World};
|
||||
use widgetry::{
|
||||
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Outcome, Panel, State, VerticalAlignment,
|
||||
Widget,
|
||||
Color, EventCtx, GfxCtx, HorizontalAlignment, Key, Line, Outcome, Panel, State, Text,
|
||||
VerticalAlignment, Widget,
|
||||
};
|
||||
|
||||
use super::Neighborhood;
|
||||
use crate::app::{App, Transition};
|
||||
use crate::common::{InputWaypoints, WaypointID};
|
||||
use crate::common::{cmp_dist, cmp_duration, InputWaypoints, WaypointID};
|
||||
|
||||
pub struct RoutePlanner {
|
||||
panel: Panel,
|
||||
@ -21,7 +21,8 @@ pub struct RoutePlanner {
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
|
||||
enum ID {
|
||||
MainRoute,
|
||||
RouteAfterFilters,
|
||||
RouteBeforeFilters,
|
||||
Waypoint(WaypointID),
|
||||
}
|
||||
impl ObjectID for ID {}
|
||||
@ -49,6 +50,9 @@ impl RoutePlanner {
|
||||
.text("Back to editing modal filters")
|
||||
.hotkey(Key::Escape)
|
||||
.build_def(ctx),
|
||||
Line("Warning: Time estimates assume freeflow conditions (no traffic)")
|
||||
.fg(Color::RED)
|
||||
.into_widget(ctx),
|
||||
self.waypoints.get_panel_widget(ctx),
|
||||
]))
|
||||
.aligned(HorizontalAlignment::Left, VerticalAlignment::Top)
|
||||
@ -58,7 +62,7 @@ impl RoutePlanner {
|
||||
|
||||
let mut world = self.calculate_paths(ctx, app);
|
||||
self.waypoints
|
||||
.rebuild_world(ctx, &mut world, ID::Waypoint, 1);
|
||||
.rebuild_world(ctx, &mut world, ID::Waypoint, 2);
|
||||
world.initialize_hover(ctx);
|
||||
world.rebuilt_during_drag(&self.world);
|
||||
self.world = world;
|
||||
@ -66,37 +70,124 @@ impl RoutePlanner {
|
||||
|
||||
fn calculate_paths(&self, ctx: &mut EventCtx, app: &App) -> World<ID> {
|
||||
let map = &app.primary.map;
|
||||
|
||||
let mut world = World::bounded(map.get_bounds());
|
||||
|
||||
let mut params = map.routing_params().clone();
|
||||
params
|
||||
.avoid_roads
|
||||
.extend(app.session.modal_filters.roads.keys().cloned());
|
||||
let cache_custom = true;
|
||||
// First the route respecting the filters
|
||||
let (total_time_after, total_dist_after) = {
|
||||
let mut params = map.routing_params().clone();
|
||||
params
|
||||
.avoid_roads
|
||||
.extend(app.session.modal_filters.roads.keys().cloned());
|
||||
let cache_custom = true;
|
||||
|
||||
let mut draw_route = ToggleZoomed::builder();
|
||||
let mut hitbox_pieces = Vec::new();
|
||||
for pair in self.waypoints.get_waypoints().windows(2) {
|
||||
if let Some(pl) = TripEndpoint::path_req(pair[0], pair[1], TripMode::Drive, map)
|
||||
.and_then(|req| map.pathfind_with_params(req, ¶ms, cache_custom).ok())
|
||||
.and_then(|path| path.trace(map))
|
||||
{
|
||||
let shape = pl.make_polygons(5.0 * NORMAL_LANE_THICKNESS);
|
||||
draw_route
|
||||
.unzoomed
|
||||
.push(Color::RED.alpha(0.8), shape.clone());
|
||||
draw_route.zoomed.push(Color::RED.alpha(0.5), shape.clone());
|
||||
hitbox_pieces.push(shape);
|
||||
let mut draw_route = ToggleZoomed::builder();
|
||||
let mut hitbox_pieces = Vec::new();
|
||||
let mut total_time = Duration::ZERO;
|
||||
let mut total_dist = Distance::ZERO;
|
||||
for pair in self.waypoints.get_waypoints().windows(2) {
|
||||
if let Some((path, pl)) =
|
||||
TripEndpoint::path_req(pair[0], pair[1], TripMode::Drive, map)
|
||||
.and_then(|req| map.pathfind_with_params(req, ¶ms, cache_custom).ok())
|
||||
.and_then(|path| path.trace(map).map(|pl| (path, pl)))
|
||||
{
|
||||
let shape = pl.make_polygons(5.0 * NORMAL_LANE_THICKNESS);
|
||||
draw_route
|
||||
.unzoomed
|
||||
.push(Color::RED.alpha(0.8), shape.clone());
|
||||
draw_route.zoomed.push(Color::RED.alpha(0.5), shape.clone());
|
||||
hitbox_pieces.push(shape);
|
||||
|
||||
// Use estimate_duration and not the original cost from pathfinding, since that
|
||||
// includes huge penalties when the route is forced to cross a filter
|
||||
total_time += path.estimate_duration(map, None);
|
||||
total_dist += path.total_length();
|
||||
}
|
||||
}
|
||||
if !hitbox_pieces.is_empty() {
|
||||
let mut txt = Text::new();
|
||||
txt.add_line(Line("Route respecting the new modal filters"));
|
||||
txt.add_line(Line(format!("Time: {}", total_time)));
|
||||
txt.add_line(Line(format!("Distance: {}", total_dist)));
|
||||
|
||||
world
|
||||
.add(ID::RouteAfterFilters)
|
||||
.hitbox(Polygon::union_all(hitbox_pieces))
|
||||
.zorder(0)
|
||||
.draw(draw_route)
|
||||
.hover_outline(Color::BLACK, Distance::meters(2.0))
|
||||
.tooltip(txt)
|
||||
.build(ctx);
|
||||
}
|
||||
|
||||
(total_time, total_dist)
|
||||
};
|
||||
|
||||
// Then the one ignoring filters
|
||||
{
|
||||
let mut draw_route = ToggleZoomed::builder();
|
||||
let mut hitbox_pieces = Vec::new();
|
||||
let mut total_time = Duration::ZERO;
|
||||
let mut total_dist = Distance::ZERO;
|
||||
for pair in self.waypoints.get_waypoints().windows(2) {
|
||||
if let Some((path, pl)) =
|
||||
TripEndpoint::path_req(pair[0], pair[1], TripMode::Drive, map)
|
||||
.and_then(|req| map.pathfind(req).ok())
|
||||
.and_then(|path| path.trace(map).map(|pl| (path, pl)))
|
||||
{
|
||||
let shape = pl.make_polygons(5.0 * NORMAL_LANE_THICKNESS);
|
||||
draw_route
|
||||
.unzoomed
|
||||
.push(Color::BLUE.alpha(0.8), shape.clone());
|
||||
draw_route
|
||||
.zoomed
|
||||
.push(Color::BLUE.alpha(0.5), shape.clone());
|
||||
hitbox_pieces.push(shape);
|
||||
|
||||
total_time += path.estimate_duration(map, None);
|
||||
total_dist += path.total_length();
|
||||
}
|
||||
}
|
||||
if !hitbox_pieces.is_empty() {
|
||||
let mut txt = Text::new();
|
||||
// If these two stats are the same, assume the two paths are equivalent
|
||||
if total_time == total_time_after && total_dist == total_dist_after {
|
||||
world.delete(ID::RouteAfterFilters);
|
||||
txt.add_line(Line(
|
||||
"The route is the same before/after the new modal filters",
|
||||
));
|
||||
txt.add_line(Line(format!("Time: {}", total_time)));
|
||||
txt.add_line(Line(format!("Distance: {}", total_dist)));
|
||||
} else {
|
||||
txt.add_line(Line("Route before the new modal filters"));
|
||||
txt.add_line(Line(format!("Time: {}", total_time)));
|
||||
txt.add_line(Line(format!("Distance: {}", total_dist)));
|
||||
cmp_duration(
|
||||
&mut txt,
|
||||
app,
|
||||
total_time - total_time_after,
|
||||
"shorter",
|
||||
"longer",
|
||||
);
|
||||
cmp_dist(
|
||||
&mut txt,
|
||||
app,
|
||||
total_dist - total_dist_after,
|
||||
"shorter",
|
||||
"longer",
|
||||
);
|
||||
}
|
||||
|
||||
world
|
||||
.add(ID::RouteBeforeFilters)
|
||||
.hitbox(Polygon::union_all(hitbox_pieces))
|
||||
// If the two routes partly overlap, put the "before" on top, since it has
|
||||
// the comparison stats.
|
||||
.zorder(1)
|
||||
.draw(draw_route)
|
||||
.hover_outline(Color::BLACK, Distance::meters(2.0))
|
||||
.tooltip(txt)
|
||||
.build(ctx);
|
||||
}
|
||||
}
|
||||
if !hitbox_pieces.is_empty() {
|
||||
world
|
||||
.add(ID::MainRoute)
|
||||
.hitbox(Polygon::union_all(hitbox_pieces))
|
||||
.draw(draw_route)
|
||||
.hover_outline(Color::BLACK, Distance::meters(2.0))
|
||||
.build(ctx);
|
||||
}
|
||||
|
||||
world
|
||||
|
@ -12,6 +12,7 @@ use widgetry::{
|
||||
|
||||
use super::{before_after_button, RoutingPreferences};
|
||||
use crate::app::{App, Transition};
|
||||
use crate::common::{cmp_dist, cmp_duration};
|
||||
|
||||
/// A temporary structure that the caller should unpack and use as needed.
|
||||
pub struct BuiltRoute {
|
||||
@ -507,50 +508,3 @@ fn compare_routes(
|
||||
|
||||
txt
|
||||
}
|
||||
|
||||
fn cmp_dist(txt: &mut Text, app: &App, dist: Distance, shorter: &str, longer: &str) {
|
||||
match dist.cmp(&Distance::ZERO) {
|
||||
Ordering::Less => {
|
||||
txt.add_line(
|
||||
Line(format!(
|
||||
"{} {}",
|
||||
(-dist).to_string(&app.opts.units),
|
||||
shorter
|
||||
))
|
||||
.fg(Color::GREEN),
|
||||
);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
txt.add_line(
|
||||
Line(format!("{} {}", dist.to_string(&app.opts.units), longer)).fg(Color::RED),
|
||||
);
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn cmp_duration(txt: &mut Text, app: &App, duration: Duration, shorter: &str, longer: &str) {
|
||||
match duration.cmp(&Duration::ZERO) {
|
||||
Ordering::Less => {
|
||||
txt.add_line(
|
||||
Line(format!(
|
||||
"{} {}",
|
||||
(-duration).to_string(&app.opts.units),
|
||||
shorter
|
||||
))
|
||||
.fg(Color::GREEN),
|
||||
);
|
||||
}
|
||||
Ordering::Greater => {
|
||||
txt.add_line(
|
||||
Line(format!(
|
||||
"{} {}",
|
||||
duration.to_string(&app.opts.units),
|
||||
longer
|
||||
))
|
||||
.fg(Color::RED),
|
||||
);
|
||||
}
|
||||
Ordering::Equal => {}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user