1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97
//! popdat ("population data") generates `Scenarios` given a map and some external census data. //! Some of this functionality should maybe be reorganized or incorporated into the importer crate, //! but for now, it's convenient to organize it here. //! //! All of the types and methods here are tied to a single `Map`. Even if a city is chopped up into //! multiple pieces, for now, let's assume we're just dealing with one map at a time. That lets us //! use the map's coordinate system, building IDs, etc. //! //! These types form a pipeline: //! //! 1) For a given map, find some census data that describes how many people live in different //! areas of the city. (CensusArea) //! 2) Take the CensusAreas and turn them into individual CensusPersons, by randomly choosing a //! specific building on the map as their home, and assigning specific attributes based on the //! census data's distribution. //! 3) For each CensusPerson, classify them into a PersonType, then generate a Schedule of //! different Activities throughout the day. //! 4) Pick specific buildings to visit to satisfy the Schedule. use rand_xorshift::XorShiftRng; use geom::Polygon; use geom::Time; use map_model::{BuildingID, Map}; use sim::Scenario; mod activities; mod distribute_people; mod import_census; mod make_person; /// Represents aggregate demographic data for some part of a city. These could be census tracts or /// blocks, depending what data we find. All of the areas should roughly partition the map -- we /// probably don't need to guarantee we cover every single building, but we definitely shouldn't /// have two overlapping areas. pub struct CensusArea { pub polygon: Polygon, pub total_population: usize, // TODO Not sure what goes here, whatever census data actually has that could be useful } /// Demographic information for a single person pub struct CensusPerson { pub home: BuildingID, pub age: usize, pub employeed: bool, pub owns_car: bool, } /// It might be useful to classify a CensusPerson into different categories to figure out their /// Schedule. pub enum PersonType { Student, Worker, } /// A single person's daily schedule. It's assumed that someone always starts at home. And for most /// people, the last entry should probably be Activity::Home. pub struct Schedule { pub activities: Vec<(Time, Activity)>, } /// Different things people might do in the day. Maybe it's more clear to call this a /// DestinationType or similar. pub enum Activity { Home, School, Work, Mall, Movies, Coffee, Restaurant, Library, } /// Wires together all the pieces, so you can just hand this any map, and it'll automatically find /// appropriate census data, and use it to produce a Scenario. pub fn generate_scenario( scenario_name: &str, map: &Map, rng: &mut XorShiftRng, ) -> Result<Scenario, String> { /// find_data_for_map may return an error. If so, just plumb it back to the caller using the ? /// operator let areas = CensusArea::find_data_for_map(map)?; let people = distribute_people::assign_people_to_houses(areas, map, rng); let mut scenario = Scenario::empty(map, scenario_name); for person in people { // TODO If we need to parallelize because make_person is slow, the sim crate has a fork_rng // method that could be useful scenario .people .push(make_person::make_person(person, map, rng)); } Ok(scenario) }