mirror of
https://github.com/a-b-street/abstreet.git
synced 2024-11-24 01:15:12 +03:00
Simplify traffic count code -- directly deal in Counters
This commit is contained in:
parent
921228b7bf
commit
6c2a581be5
@ -83,8 +83,9 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// A counter per key
|
||||
// Be careful with PartialEq -- some entries may have an explicit 0, others not
|
||||
#[derive(Clone, PartialEq)]
|
||||
#[derive(Serialize, Deserialize, Clone, PartialEq)]
|
||||
pub struct Counter<T: Ord + PartialEq + Clone> {
|
||||
map: BTreeMap<T, usize>,
|
||||
sum: usize,
|
||||
@ -114,6 +115,7 @@ impl<T: Ord + PartialEq + Clone> Counter<T> {
|
||||
self.add(val, 1)
|
||||
}
|
||||
|
||||
/// If the key is missing, returns 0
|
||||
pub fn get(&self, val: T) -> usize {
|
||||
self.map.get(&val).cloned().unwrap_or(0)
|
||||
}
|
||||
|
@ -162,16 +162,20 @@ fn count_throughput(
|
||||
cache_custom: PathfinderCaching,
|
||||
timer: &mut Timer,
|
||||
) -> Counts {
|
||||
let mut road_counts = Counter::new();
|
||||
let mut intersection_counts = Counter::new();
|
||||
let mut counts = Counts {
|
||||
map: map.get_name().clone(),
|
||||
description,
|
||||
per_road: Counter::new(),
|
||||
per_intersection: Counter::new(),
|
||||
};
|
||||
|
||||
// Statistic::Min will be wrong later for roads that're 0. So explicitly start with 0 for every
|
||||
// road/intersection.
|
||||
for r in map.all_roads() {
|
||||
road_counts.add(r.id, 0);
|
||||
counts.per_road.add(r.id, 0);
|
||||
}
|
||||
for i in map.all_intersections() {
|
||||
intersection_counts.add(i.id, 0);
|
||||
counts.per_intersection.add(i.id, 0);
|
||||
}
|
||||
|
||||
// It's very memory intensive to calculate all of the paths in one chunk, then process them to
|
||||
@ -189,22 +193,16 @@ fn count_throughput(
|
||||
for step in path.get_steps() {
|
||||
match step {
|
||||
PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
|
||||
road_counts.add(dr.road, count);
|
||||
counts.per_road.add(dr.road, count);
|
||||
}
|
||||
PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
|
||||
intersection_counts.add(m.parent, count);
|
||||
counts.per_intersection.add(m.parent, count);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Counts {
|
||||
map: map.get_name().clone(),
|
||||
description,
|
||||
per_road: road_counts.consume().into_iter().collect(),
|
||||
per_intersection: intersection_counts.consume().into_iter().collect(),
|
||||
}
|
||||
counts
|
||||
}
|
||||
|
||||
// TODO Fixed, and sadly not const
|
||||
|
@ -95,11 +95,11 @@ impl SimpleState<App> for ShowResults {
|
||||
let path2 = "counts_b.json";
|
||||
abstio::write_json(
|
||||
path1.to_string(),
|
||||
&app.session.impact.compare_counts.counts_a.to_counts(),
|
||||
&app.session.impact.compare_counts.counts_a,
|
||||
);
|
||||
abstio::write_json(
|
||||
path2.to_string(),
|
||||
&app.session.impact.compare_counts.counts_b.to_counts(),
|
||||
&app.session.impact.compare_counts.counts_b,
|
||||
);
|
||||
Transition::Push(PopupMsg::new_state(
|
||||
ctx,
|
||||
|
@ -10,22 +10,35 @@ use widgetry::{Color, EventCtx, GeomBatch, GfxCtx, Key, Line, Text, TextExt, Wid
|
||||
use crate::tools::{cmp_count, ColorNetwork, DivergingScale};
|
||||
use crate::AppLike;
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
#[derive(Clone, Serialize, Deserialize)]
|
||||
pub struct Counts {
|
||||
pub map: MapName,
|
||||
// TODO For now, squeeze everything into this -- mode, weekday/weekend, time of day, data
|
||||
// source, etc
|
||||
pub description: String,
|
||||
// TODO Maybe per direction, movement
|
||||
pub per_road: Vec<(RoadID, usize)>,
|
||||
pub per_intersection: Vec<(IntersectionID, usize)>,
|
||||
pub per_road: Counter<RoadID>,
|
||||
pub per_intersection: Counter<IntersectionID>,
|
||||
}
|
||||
|
||||
impl Default for Counts {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
map: MapName::new("zz", "place", "holder"),
|
||||
description: String::new(),
|
||||
per_road: Counter::new(),
|
||||
per_intersection: Counter::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct CompareCounts {
|
||||
pub layer: Layer,
|
||||
pub counts_a: CountsUI,
|
||||
pub counts_b: CountsUI,
|
||||
world: World<Obj>,
|
||||
pub counts_a: Counts,
|
||||
heatmap_a: ToggleZoomed,
|
||||
pub counts_b: Counts,
|
||||
heatmap_b: ToggleZoomed,
|
||||
relative_heatmap: ToggleZoomed,
|
||||
}
|
||||
|
||||
@ -43,63 +56,6 @@ pub enum Layer {
|
||||
Compare,
|
||||
}
|
||||
|
||||
pub struct CountsUI {
|
||||
// TODO Just embed Counts directly, and make that serialize a Counter?
|
||||
map: MapName,
|
||||
description: String,
|
||||
heatmap: ToggleZoomed,
|
||||
per_road: Counter<RoadID>,
|
||||
per_intersection: Counter<IntersectionID>,
|
||||
}
|
||||
|
||||
impl CountsUI {
|
||||
fn new(ctx: &EventCtx, app: &dyn AppLike, counts: Counts) -> CountsUI {
|
||||
let mut per_road = Counter::new();
|
||||
for (r, count) in counts.per_road {
|
||||
per_road.add(r, count);
|
||||
}
|
||||
let mut per_intersection = Counter::new();
|
||||
for (i, count) in counts.per_intersection {
|
||||
per_intersection.add(i, count);
|
||||
}
|
||||
|
||||
let mut colorer = ColorNetwork::no_fading(app);
|
||||
colorer.ranked_roads(per_road.clone(), &app.cs().good_to_bad_red);
|
||||
colorer.ranked_intersections(per_intersection.clone(), &app.cs().good_to_bad_red);
|
||||
CountsUI {
|
||||
map: counts.map,
|
||||
description: counts.description,
|
||||
heatmap: colorer.build(ctx),
|
||||
per_road,
|
||||
per_intersection,
|
||||
}
|
||||
}
|
||||
|
||||
fn empty(ctx: &EventCtx) -> Self {
|
||||
Self {
|
||||
map: MapName::new("zz", "place", "holder"),
|
||||
description: String::new(),
|
||||
heatmap: ToggleZoomed::empty(ctx),
|
||||
per_road: Counter::new(),
|
||||
per_intersection: Counter::new(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn to_counts(&self) -> Counts {
|
||||
Counts {
|
||||
map: self.map.clone(),
|
||||
description: self.description.clone(),
|
||||
per_road: self.per_road.clone().consume().into_iter().collect(),
|
||||
per_intersection: self
|
||||
.per_intersection
|
||||
.clone()
|
||||
.consume()
|
||||
.into_iter()
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl CompareCounts {
|
||||
pub fn new(
|
||||
ctx: &mut EventCtx,
|
||||
@ -108,16 +64,17 @@ impl CompareCounts {
|
||||
counts_b: Counts,
|
||||
layer: Layer,
|
||||
) -> CompareCounts {
|
||||
let counts_a = CountsUI::new(ctx, app, counts_a);
|
||||
let counts_b = CountsUI::new(ctx, app, counts_b);
|
||||
|
||||
let heatmap_a = calculate_heatmap(ctx, app, counts_a.clone());
|
||||
let heatmap_b = calculate_heatmap(ctx, app, counts_b.clone());
|
||||
let relative_heatmap = calculate_relative_heatmap(ctx, app, &counts_a, &counts_b);
|
||||
|
||||
CompareCounts {
|
||||
layer,
|
||||
counts_a,
|
||||
counts_b,
|
||||
world: make_world(ctx, app),
|
||||
counts_a,
|
||||
heatmap_a,
|
||||
counts_b,
|
||||
heatmap_b,
|
||||
relative_heatmap,
|
||||
}
|
||||
}
|
||||
@ -134,7 +91,8 @@ impl CompareCounts {
|
||||
}
|
||||
|
||||
pub fn recalculate_b(&mut self, ctx: &EventCtx, app: &dyn AppLike, counts_b: Counts) {
|
||||
self.counts_b = CountsUI::new(ctx, app, counts_b);
|
||||
self.counts_b = counts_b;
|
||||
self.heatmap_b = calculate_heatmap(ctx, app, self.counts_b.clone());
|
||||
self.relative_heatmap =
|
||||
calculate_relative_heatmap(ctx, app, &self.counts_a, &self.counts_b);
|
||||
if self.layer == Layer::A {
|
||||
@ -145,9 +103,11 @@ impl CompareCounts {
|
||||
pub fn empty(ctx: &EventCtx) -> CompareCounts {
|
||||
CompareCounts {
|
||||
layer: Layer::A,
|
||||
counts_a: CountsUI::empty(ctx),
|
||||
counts_b: CountsUI::empty(ctx),
|
||||
world: World::unbounded(),
|
||||
counts_a: Counts::default(),
|
||||
heatmap_a: ToggleZoomed::empty(ctx),
|
||||
counts_b: Counts::default(),
|
||||
heatmap_b: ToggleZoomed::empty(ctx),
|
||||
relative_heatmap: ToggleZoomed::empty(ctx),
|
||||
}
|
||||
}
|
||||
@ -184,10 +144,10 @@ impl CompareCounts {
|
||||
pub fn draw(&self, g: &mut GfxCtx) {
|
||||
match self.layer {
|
||||
Layer::A => {
|
||||
self.counts_a.heatmap.draw(g);
|
||||
self.heatmap_a.draw(g);
|
||||
}
|
||||
Layer::B => {
|
||||
self.counts_b.heatmap.draw(g);
|
||||
self.heatmap_b.draw(g);
|
||||
}
|
||||
Layer::Compare => {
|
||||
self.relative_heatmap.draw(g);
|
||||
@ -267,11 +227,19 @@ impl CompareCounts {
|
||||
}
|
||||
}
|
||||
|
||||
fn calculate_heatmap(ctx: &EventCtx, app: &dyn AppLike, counts: Counts) -> ToggleZoomed {
|
||||
let mut colorer = ColorNetwork::no_fading(app);
|
||||
// TODO The scale will be different for roads and intersections
|
||||
colorer.ranked_roads(counts.per_road, &app.cs().good_to_bad_red);
|
||||
colorer.ranked_intersections(counts.per_intersection, &app.cs().good_to_bad_red);
|
||||
colorer.build(ctx)
|
||||
}
|
||||
|
||||
fn calculate_relative_heatmap(
|
||||
ctx: &EventCtx,
|
||||
app: &dyn AppLike,
|
||||
counts_a: &CountsUI,
|
||||
counts_b: &CountsUI,
|
||||
counts_a: &Counts,
|
||||
counts_b: &Counts,
|
||||
) -> ToggleZoomed {
|
||||
// First just understand the counts...
|
||||
let mut hgram_before = Histogram::new();
|
||||
|
Loading…
Reference in New Issue
Block a user