1use std::collections::BTreeSet;
2
3use abstio::MapName;
4use abstutil::Timer;
5use geom::{Duration, Time};
6use map_gui::tools::compare_counts::CompareCounts;
7use map_model::{PathConstraints, PathRequest, PathV2, Pathfinder, RoadID};
8use synthpop::{Scenario, TrafficCounts, TripEndpoint, TripMode};
9use widgetry::EventCtx;
10
11use crate::App;
12
13pub struct Impact {
23 pub map: MapName,
24 pub filters: Filters,
25
26 pathfinder_before_changes: Pathfinder,
29
30 all_trips: Vec<PathRequest>,
31 filtered_trips: Vec<(PathRequest, usize)>,
33
34 pub compare_counts: CompareCounts,
35 pub map_edit_key: usize,
36}
37
38#[derive(PartialEq)]
39pub struct Filters {
40 pub modes: BTreeSet<TripMode>,
41 pub include_borders: bool,
44 pub departure_time: (Time, Time),
45}
46
47impl Impact {
48 pub fn empty(ctx: &EventCtx) -> Self {
49 Self {
50 map: MapName::blank(),
51 filters: Filters {
52 modes: vec![TripMode::Drive].into_iter().collect(),
53 include_borders: true,
54 departure_time: (Time::START_OF_DAY, end_of_day()),
55 },
56
57 pathfinder_before_changes: Pathfinder::empty(),
58
59 all_trips: Vec::new(),
60 filtered_trips: Vec::new(),
61
62 compare_counts: CompareCounts::empty(ctx),
63 map_edit_key: usize::MAX,
64 }
65 }
66
67 pub fn from_scenario(
68 ctx: &mut EventCtx,
69 app: &App,
70 scenario: Scenario,
71 timer: &mut Timer,
72 ) -> Impact {
73 let mut impact = Impact::empty(ctx);
74 let map = &app.per_map.map;
75
76 impact.pathfinder_before_changes = Pathfinder::new_ch(
77 map,
78 app.per_map.routing_params_before_changes.clone(),
79 PathConstraints::all(),
80 timer,
81 );
82
83 impact.map = app.per_map.map.get_name().clone();
84 impact.map_edit_key = app.per_map.map.get_edits_change_key();
85 impact.all_trips = timer
86 .parallelize("analyze trips", scenario.all_trips().collect(), |trip| {
87 TripEndpoint::path_req(trip.origin, trip.destination, trip.mode, map)
88 })
89 .into_iter()
90 .flatten()
91 .collect();
92 impact.trips_changed(ctx, app, timer);
93 impact.compare_counts.autoselect_layer();
94 impact
95 }
96
97 fn pathfinder_after(&self, app: &App, timer: &mut Timer) -> Pathfinder {
99 let constraints: BTreeSet<PathConstraints> = self
100 .filters
101 .modes
102 .iter()
103 .map(|m| m.to_constraints())
104 .collect();
105 Pathfinder::new_ch(
106 &app.per_map.map,
107 app.per_map.map.routing_params_respecting_modal_filters(),
108 constraints.into_iter().collect(),
109 timer,
110 )
111 }
112
113 pub fn trips_changed(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
114 let map = &app.per_map.map;
115 let constraints: BTreeSet<PathConstraints> = self
116 .filters
117 .modes
118 .iter()
119 .map(|m| m.to_constraints())
120 .collect();
121 self.filtered_trips = PathRequest::deduplicate(
122 map,
123 self.all_trips
124 .iter()
125 .filter(|req| constraints.contains(&req.constraints))
126 .cloned()
127 .collect(),
128 );
129
130 let counts_a = TrafficCounts::from_path_requests(
131 map,
132 "before filters".to_string(),
134 &self.filtered_trips,
135 &self.pathfinder_before_changes,
136 timer,
137 );
138
139 let counts_b = self.counts_b(app, timer);
140
141 let clickable_roads = true;
142 self.compare_counts = CompareCounts::new(
143 ctx,
144 app,
145 counts_a,
146 counts_b,
147 self.compare_counts.layer,
148 clickable_roads,
149 );
150 }
151
152 pub fn map_edits_changed(&mut self, ctx: &mut EventCtx, app: &App, timer: &mut Timer) {
153 self.map_edit_key = app.per_map.map.get_edits_change_key();
154 let counts_b = self.counts_b(app, timer);
155 self.compare_counts.recalculate_b(ctx, app, counts_b);
156 }
157
158 fn counts_b(&self, app: &App, timer: &mut Timer) -> TrafficCounts {
159 let map = &app.per_map.map;
160 let pathfinder_after = self.pathfinder_after(app, timer);
161
162 let mut counts = TrafficCounts::from_path_requests(
166 map,
167 "after filters".to_string(),
169 &vec![],
170 &pathfinder_after,
171 timer,
172 );
173
174 timer.start_iter("calculate routes", self.filtered_trips.len());
175 for (req, count) in &self.filtered_trips {
176 timer.next();
177 if let (Some(path1), Some(path2)) = (
178 self.pathfinder_before_changes.pathfind_v2(req.clone(), map),
179 pathfinder_after.pathfind_v2(req.clone(), map),
180 ) {
181 if path1.get_cost() == path2.get_cost() {
182 counts.update_with_path(path1, *count, map);
185 } else {
186 counts.update_with_path(path2, *count, map);
187 }
188 }
189 }
190
191 counts
192 }
193
194 pub fn find_changed_routes(
197 &self,
198 app: &App,
199 r: RoadID,
200 timer: &mut Timer,
201 ) -> Vec<(PathV2, PathV2)> {
202 let map = &app.per_map.map;
203 let pathfinder_after = self.pathfinder_after(app, timer);
204
205 let mut changed = Vec::new();
206 timer.start_iter("find changed routes", self.filtered_trips.len());
207 for (req, _) in &self.filtered_trips {
208 timer.next();
209 if let (Some(path1), Some(path2)) = (
210 self.pathfinder_before_changes.pathfind_v2(req.clone(), map),
211 pathfinder_after.pathfind_v2(req.clone(), map),
212 ) {
213 if path1.get_cost() == path2.get_cost() {
215 continue;
216 }
217
218 if path1.crosses_road(r) != path2.crosses_road(r) {
219 changed.push((path1, path2));
220 }
221 }
222 }
223 changed
224 }
225}
226
227pub fn end_of_day() -> Time {
229 Time::START_OF_DAY + Duration::hours(24)
230}