popdat/lib.rs
1//! popdat ("population data") generates `Scenarios` given a map and some external census data.
2//! Some of this functionality should maybe be reorganized or incorporated into the importer crate,
3//! but for now, it's convenient to organize it here.
4//!
5//! All of the types and methods here are tied to a single `Map`. Even if a city is chopped up into
6//! multiple pieces, for now, let's assume we're just dealing with one map at a time. That lets us
7//! use the map's coordinate system, building IDs, etc.
8//!
9//! These types form a pipeline:
10//!
11//! 1) For a given map, find some census data that describes how many people live in different
12//! areas of the city. (CensusArea)
13//! 2) Take the CensusAreas and turn them into individual CensusPersons, by randomly choosing a
14//! specific building on the map as their home, and assigning specific attributes based on the
15//! census data's distribution.
16//! 3) For each CensusPerson, classify them into a PersonType, then generate a Schedule of
17//! different Activities throughout the day.
18//! 4) Pick specific buildings to visit to satisfy the Schedule.
19
20#[macro_use]
21extern crate anyhow;
22#[macro_use]
23extern crate log;
24
25use rand_xorshift::XorShiftRng;
26
27use abstutil::Timer;
28use geom::{Distance, Time};
29use map_model::{BuildingID, Map};
30use synthpop::Scenario;
31
32pub use self::distribute_people::distribute_population_to_homes;
33
34mod activities;
35mod distribute_people;
36mod import_census;
37mod make_person;
38pub mod od;
39
40/// Represents aggregate demographic data for some part of a city. These could be census tracts or
41/// blocks, depending what data we find. All of the areas should roughly partition the map -- we
42/// probably don't need to guarantee we cover every single building, but we definitely shouldn't
43/// have two overlapping areas.
44#[derive(Debug, PartialEq)]
45pub struct CensusArea {
46 pub polygon: geo::Polygon,
47 pub population: usize,
48 // TODO Not sure what goes here, whatever census data actually has that could be useful
49}
50
51/// Demographic information for a single person
52pub struct CensusPerson {
53 pub home: BuildingID,
54 pub age: usize,
55 pub employed: bool,
56 pub owns_car: bool,
57}
58
59/// It might be useful to classify a CensusPerson into different categories to figure out their
60/// Schedule.
61pub enum PersonType {
62 Student,
63 Worker,
64}
65
66/// A single person's daily schedule. It's assumed that someone always starts at home. And for most
67/// people, the last entry should probably be Activity::Home.
68pub struct Schedule {
69 pub activities: Vec<(Time, Activity)>,
70}
71
72/// Different things people might do in the day. Maybe it's more clear to call this a
73/// DestinationType or similar.
74#[derive(Hash, PartialEq, Eq, Clone, Copy, Debug)]
75pub enum Activity {
76 Breakfast,
77 Lunch,
78 Dinner,
79 School,
80 Entertainment,
81 Errands,
82 Financial,
83 Healthcare,
84 Home,
85 Work,
86}
87
88/// Any arbitrarily chosen parameters needed should be put here, so they can be controlled from the
89/// UI or tuned for different cities.
90pub struct Config {
91 pub walk_for_distances_shorter_than: Distance,
92 pub walk_or_bike_for_distances_shorter_than: Distance,
93}
94
95impl Config {
96 pub fn default() -> Config {
97 Config {
98 walk_for_distances_shorter_than: Distance::miles(0.5),
99 walk_or_bike_for_distances_shorter_than: Distance::miles(3.0),
100 }
101 }
102}
103
104/// Wires together all the pieces, so you can just hand this any map, and it'll automatically find
105/// appropriate census data, and use it to produce a Scenario.
106pub fn generate_scenario(
107 scenario_name: &str,
108 areas: Vec<CensusArea>,
109 config: Config,
110 map: &Map,
111 rng: &mut XorShiftRng,
112) -> Scenario {
113 let mut timer = Timer::new("building scenario");
114
115 // find_data_for_map may return an error. If so, just plumb it back to the caller using the ?
116 // operator
117 timer.start("assigning people to houses");
118 let people = distribute_people::assign_people_to_houses(areas, map, rng, &config);
119 timer.stop("assigning people to houses");
120
121 let mut scenario = Scenario::empty(map, scenario_name);
122 timer.start("building people");
123 scenario.people.extend(make_person::make_people(
124 people, map, &mut timer, rng, &config,
125 ));
126 timer.stop("building people");
127
128 timer.start("removing weird schedules");
129 scenario = scenario.remove_weird_schedules(true);
130 timer.stop("removing weird schedules");
131
132 scenario
133}