Ignore spurious changes in pathfinding for the LTN impact tool. #868

This commit is contained in:
Dustin Carlino 2022-03-25 13:15:33 +00:00
parent 1126972fdd
commit 272408002f
2 changed files with 64 additions and 31 deletions

View File

@ -143,15 +143,39 @@ impl Impact {
app.session.modal_filters.update_routing_params(&mut params); app.session.modal_filters.update_routing_params(&mut params);
// Since we're making so many requests, it's worth it to rebuild a contraction hierarchy. // Since we're making so many requests, it's worth it to rebuild a contraction hierarchy.
// This depends on the current map edits, so no need to cache // This depends on the current map edits, so no need to cache
let pathfinder = Pathfinder::new_ch(map, params, constraints.into_iter().collect(), timer); let pathfinder_after =
TrafficCounts::from_path_requests( Pathfinder::new_ch(map, params, constraints.into_iter().collect(), timer);
// We can't simply use TrafficCounts::from_path_requests. Due to spurious diffs with paths,
// we need to skip cases where the path before and after have the same cost. It's easiest
// (code-wise) to just repeat some calculation here.
let mut counts = TrafficCounts::from_path_requests(
map, map,
// Don't bother describing all the trip filtering // Don't bother describing all the trip filtering
"after filters".to_string(), "after filters".to_string(),
&self.filtered_trips, &vec![],
&pathfinder, &pathfinder_after,
timer, 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)) = (
map.get_pathfinder().pathfind_v2(req.clone(), map),
pathfinder_after.pathfind_v2(req.clone(), map),
) {
if path1.get_cost() == path2.get_cost() {
// When the path maybe changed but the cost is the same, just count it the same
// as the original path
counts.update_with_path(path1, *count, map);
} else {
counts.update_with_path(path2, *count, map);
}
}
}
counts
} }
/// Returns routes that start or stop crossing the given road. Returns paths (before filters, /// Returns routes that start or stop crossing the given road. Returns paths (before filters,
@ -185,6 +209,11 @@ impl Impact {
map.get_pathfinder().pathfind_v2(req.clone(), map), map.get_pathfinder().pathfind_v2(req.clone(), map),
pathfinder_after.pathfind_v2(req.clone(), map), pathfinder_after.pathfind_v2(req.clone(), map),
) { ) {
// Skip spurious changes where the cost matches.
if path1.get_cost() == path2.get_cost() {
continue;
}
if path1.crosses_road(r) != path2.crosses_road(r) { if path1.crosses_road(r) != path2.crosses_road(r) {
if let (Ok(path1), Ok(path2)) = (path1.into_v1(map), path2.into_v1(map)) { if let (Ok(path1), Ok(path2)) = (path1.into_v1(map), path2.into_v1(map)) {
changed.push((path1, path2)); changed.push((path1, path2));

View File

@ -3,7 +3,7 @@ use serde::{Deserialize, Serialize};
use abstio::MapName; use abstio::MapName;
use abstutil::{prettyprint_usize, Counter, Timer}; use abstutil::{prettyprint_usize, Counter, Timer};
use geom::Distance; use geom::Distance;
use map_model::{IntersectionID, Map, PathRequest, PathStepV2, Pathfinder, RoadID}; use map_model::{IntersectionID, Map, PathRequest, PathStepV2, PathV2, Pathfinder, RoadID};
/// This represents the number of vehicles (or trips, or something else) crossing roads and /// This represents the number of vehicles (or trips, or something else) crossing roads and
/// intersections over some span of time. The data could represent real observations or something /// intersections over some span of time. The data could represent real observations or something
@ -70,36 +70,40 @@ impl TrafficCounts {
for (req, count) in requests { for (req, count) in requests {
timer.next(); timer.next();
if let Some(path) = pathfinder.pathfind_v2(req.clone(), map) { if let Some(path) = pathfinder.pathfind_v2(req.clone(), map) {
let count = *count; counts.update_with_path(path, *count, map);
for step in path.get_steps() {
match step {
PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
counts.per_road.add(dr.road, count);
}
PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
counts.per_intersection.add(m.parent, count);
}
}
}
// If we're starting or ending at a border, count it
if req.start.dist_along() == Distance::ZERO {
// TODO src_i and dst_i may not work for pedestrians on contraflow sidewalks
let i = map.get_l(req.start.lane()).src_i;
if map.get_i(i).is_border() {
counts.per_intersection.add(i, count);
}
} else {
let i = map.get_l(req.end.lane()).dst_i;
if map.get_i(i).is_border() {
counts.per_intersection.add(i, count);
}
}
} }
} }
counts counts
} }
pub fn update_with_path(&mut self, path: PathV2, count: usize, map: &Map) {
for step in path.get_steps() {
match step {
PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
self.per_road.add(dr.road, count);
}
PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
self.per_intersection.add(m.parent, count);
}
}
}
// If we're starting or ending at a border, count it
let req = path.get_req();
if req.start.dist_along() == Distance::ZERO {
// TODO src_i and dst_i may not work for pedestrians on contraflow sidewalks
let i = map.get_l(req.start.lane()).src_i;
if map.get_i(i).is_border() {
self.per_intersection.add(i, count);
}
} else {
let i = map.get_l(req.end.lane()).dst_i;
if map.get_i(i).is_border() {
self.per_intersection.add(i, count);
}
}
}
/// Print a comparison of counts. Only look at roads/intersections in `self`. /// Print a comparison of counts. Only look at roads/intersections in `self`.
pub fn quickly_compare(&self, other: &TrafficCounts) { pub fn quickly_compare(&self, other: &TrafficCounts) {
// TODO Easy ASCII art table without huge dependencies? // TODO Easy ASCII art table without huge dependencies?