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}