synthpop/
counts.rs

1use serde::{Deserialize, Serialize};
2
3use abstio::MapName;
4use abstutil::{prettyprint_usize, Counter, Timer};
5use geom::Distance;
6use map_model::{IntersectionID, Map, PathRequest, PathStepV2, PathV2, Pathfinder, RoadID};
7
8/// This represents the number of vehicles (or trips, or something else) crossing roads and
9/// intersections over some span of time. The data could represent real observations or something
10/// from a simulation.
11///
12/// There's some nice UIs in other crates to compare counts.
13#[derive(Clone, Serialize, Deserialize)]
14pub struct TrafficCounts {
15    pub map: MapName,
16    // TODO For now, squeeze everything into this -- mode, weekday/weekend, time of day, data
17    // source, etc
18    pub description: String,
19    // TODO Maybe per direction, movement
20    pub per_road: Counter<RoadID>,
21    pub per_intersection: Counter<IntersectionID>,
22}
23
24impl Default for TrafficCounts {
25    fn default() -> Self {
26        Self {
27            map: MapName::new("zz", "place", "holder"),
28            description: String::new(),
29            per_road: Counter::new(),
30            per_intersection: Counter::new(),
31        }
32    }
33}
34
35impl TrafficCounts {
36    /// Run pathfinding on all of the requests, then count the throughput on every road and
37    /// intersection. Each request has the count it should contribute -- use
38    /// `PathRequest::deduplicate` to easily generate this.
39    pub fn from_path_requests(
40        map: &Map,
41        description: String,
42        requests: &[(PathRequest, usize)],
43        pathfinder: &Pathfinder,
44        timer: &mut Timer,
45    ) -> Self {
46        let mut counts = Self {
47            map: map.get_name().clone(),
48            description,
49            per_road: Counter::new(),
50            per_intersection: Counter::new(),
51        };
52
53        // Statistic::Min will be wrong later for roads that're 0. So explicitly start with 0 for every
54        // road/intersection.
55        for r in map.all_roads() {
56            counts.per_road.add(r.id, 0);
57        }
58        for i in map.all_intersections() {
59            counts.per_intersection.add(i.id, 0);
60        }
61
62        // It's very memory intensive to calculate all of the paths in one chunk, then process them to
63        // get counts. Increment the counters as we go.
64        //
65        // TODO But that makes it hard to use timer.parallelize for this. We could make a thread-local
66        // Counter and aggregte them at the end, but the way timer.parallelize uses scoped_threadpool
67        // right now won't let that work. Stick to single-threaded for now.
68
69        timer.start_iter("calculate routes", requests.len());
70        for (req, count) in requests {
71            timer.next();
72            if let Some(path) = pathfinder.pathfind_v2(req.clone(), map) {
73                counts.update_with_path(path, *count, map);
74            }
75        }
76        counts
77    }
78
79    pub fn update_with_path(&mut self, path: PathV2, count: usize, map: &Map) {
80        for step in path.get_steps() {
81            match step {
82                PathStepV2::Along(dr) | PathStepV2::Contraflow(dr) => {
83                    self.per_road.add(dr.road, count);
84                }
85                PathStepV2::Movement(m) | PathStepV2::ContraflowMovement(m) => {
86                    self.per_intersection.add(m.parent, count);
87                }
88            }
89        }
90
91        // If we're starting or ending at a border, count it
92        let req = path.get_req();
93        if req.start.dist_along() == Distance::ZERO {
94            // TODO src_i and dst_i may not work for pedestrians on contraflow sidewalks
95            let i = map.get_l(req.start.lane()).src_i;
96            if map.get_i(i).is_border() {
97                self.per_intersection.add(i, count);
98            }
99        } else {
100            let i = map.get_l(req.end.lane()).dst_i;
101            if map.get_i(i).is_border() {
102                self.per_intersection.add(i, count);
103            }
104        }
105    }
106
107    /// Print a comparison of counts. Only look at roads/intersections in `self`.
108    pub fn quickly_compare(&self, other: &TrafficCounts) {
109        // TODO Easy ASCII art table without huge dependencies?
110        println!("{} vs {}", self.description, other.description);
111        let mut sum = 0.0;
112        let mut n = 0;
113        for (r, cnt1) in self.per_road.borrow() {
114            let cnt1 = *cnt1;
115            let cnt2 = other.per_road.get(*r);
116            println!(
117                "{}: {} vs {}",
118                r,
119                prettyprint_usize(cnt1),
120                prettyprint_usize(cnt2)
121            );
122            sum += (cnt1 as f64 - cnt2 as f64).powi(2);
123            n += 1;
124        }
125        for (i, cnt1) in self.per_intersection.borrow() {
126            let cnt1 = *cnt1;
127            let cnt2 = other.per_intersection.get(*i);
128            println!(
129                "{}: {} vs {}",
130                i,
131                prettyprint_usize(cnt1),
132                prettyprint_usize(cnt2)
133            );
134            sum += (cnt1 as f64 - cnt2 as f64).powi(2);
135            n += 1;
136        }
137        println!("RMSE = {:.2}", (sum / n as f64).sqrt());
138    }
139}