use std::collections::BTreeSet;
use abstio::MapName;
use abstutil::Timer;
use geom::{Duration, Time};
use map_gui::tools::compare_counts::CompareCounts;
use map_model::{PathConstraints, PathRequest, PathV2, Pathfinder, RoadID};
use synthpop::{Scenario, TrafficCounts, TripEndpoint, TripMode};
use widgetry::EventCtx;
use crate::App;
pub struct Impact {
pub map: MapName,
pub filters: Filters,
pathfinder_before_changes: Pathfinder,
all_trips: Vec<PathRequest>,
filtered_trips: Vec<(PathRequest, usize)>,
pub compare_counts: CompareCounts,
pub map_edit_key: usize,
}
#[derive(PartialEq)]
pub struct Filters {
pub modes: BTreeSet<TripMode>,
pub include_borders: bool,
pub departure_time: (Time, Time),
}
impl Impact {
pub fn empty(ctx: &EventCtx) -> Self {
Self {
map: MapName::blank(),
filters: Filters {
modes: vec![TripMode::Drive].into_iter().collect(),
include_borders: true,
departure_time: (Time::START_OF_DAY, end_of_day()),
},
pathfinder_before_changes: Pathfinder::empty(),
all_trips: Vec::new(),
filtered_trips: Vec::new(),
compare_counts: CompareCounts::empty(ctx),
map_edit_key: usize::MAX,
}
}
pub fn from_scenario(
ctx: &mut EventCtx,
app: &App,
scenario: Scenario,
timer: &mut Timer,
) -> Impact {
let mut impact = Impact::empty(ctx);
let map = &app.per_map.map;
impact.pathfinder_before_changes = Pathfinder::new_ch(
map,
app.per_map.routing_params_before_changes.clone(),
PathConstraints::all(),
timer,
);
impact.map = app.per_map.map.get_name().clone();
impact.map_edit_key = app.per_map.map.get_edits_change_key();
impact.all_trips = timer
.parallelize("analyze trips", scenario.all_trips().collect(), |trip| {
TripEndpoint::path_req(trip.origin, trip.destination, trip.mode, map)
})
.into_iter()
.flatten()
.collect();
impact.trips_changed(ctx, app, timer);
impact.compare_counts.autoselect_layer();
impact
}
fn pathfinder_after(&self, app: &App, timer: &mut Timer) -> Pathfinder {
let constraints: BTreeSet<PathConstraints> = self
.filters
.modes
.iter()
.map(|m| m.to_constraints())
.collect();
Pathfinder::new_ch(
&app.per_map.map,
app.per_map.map.routing_params_respecting_modal_filters(),
constraints.into_iter().collect(),
timer,
)
}
pub fn trips_changed(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
let map = &app.per_map.map;
let constraints: BTreeSet<PathConstraints> = self
.filters
.modes
.iter()
.map(|m| m.to_constraints())
.collect();
self.filtered_trips = PathRequest::deduplicate(
map,
self.all_trips
.iter()
.filter(|req| constraints.contains(&req.constraints))
.cloned()
.collect(),
);
let counts_a = TrafficCounts::from_path_requests(
map,
"before filters".to_string(),
&self.filtered_trips,
&self.pathfinder_before_changes,
timer,
);
let counts_b = self.counts_b(app, timer);
let clickable_roads = true;
self.compare_counts = CompareCounts::new(
ctx,
app,
counts_a,
counts_b,
self.compare_counts.layer,
clickable_roads,
);
}
pub fn map_edits_changed(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
self.map_edit_key = app.per_map.map.get_edits_change_key();
let counts_b = self.counts_b(app, timer);
self.compare_counts.recalculate_b(ctx, app, counts_b);
}
fn counts_b(&self, app: &App, timer: &mut Timer) -> TrafficCounts {
let map = &app.per_map.map;
let pathfinder_after = self.pathfinder_after(app, timer);
let mut counts = TrafficCounts::from_path_requests(
map,
"after filters".to_string(),
&vec![],
&pathfinder_after,
timer,
);
timer.start_iter("calculate routes", self.filtered_trips.len());
for (req, count) in &self.filtered_trips {
timer.next();
if let (Some(path1), Some(path2)) = (
self.pathfinder_before_changes.pathfind_v2(req.clone(), map),
pathfinder_after.pathfind_v2(req.clone(), map),
) {
if path1.get_cost() == path2.get_cost() {
counts.update_with_path(path1, *count, map);
} else {
counts.update_with_path(path2, *count, map);
}
}
}
counts
}
pub fn find_changed_routes(
&self,
app: &App,
r: RoadID,
timer: &mut Timer,
) -> Vec<(PathV2, PathV2)> {
let map = &app.per_map.map;
let pathfinder_after = self.pathfinder_after(app, timer);
let mut changed = Vec::new();
timer.start_iter("find changed routes", self.filtered_trips.len());
for (req, _) in &self.filtered_trips {
timer.next();
if let (Some(path1), Some(path2)) = (
self.pathfinder_before_changes.pathfind_v2(req.clone(), map),
pathfinder_after.pathfind_v2(req.clone(), map),
) {
if path1.get_cost() == path2.get_cost() {
continue;
}
if path1.crosses_road(r) != path2.crosses_road(r) {
changed.push((path1, path2));
}
}
}
changed
}
}
pub fn end_of_day() -> Time {
Time::START_OF_DAY + Duration::hours(24)
}