1use std::collections::HashSet;
2
3use abstio::{CityName, MapName};
4use abstutil::Timer;
5use geom::{Polygon, QuadTree, Ring};
6use kml::ExtraShapes;
7use map_model::{BuildingID, BuildingType, Map};
8use sim::count_parked_cars_per_bldg;
9use synthpop::Scenario;
10
11use crate::configuration::ImporterConfiguration;
12use crate::utils::{download, download_kml};
13
14pub async fn input(config: &ImporterConfiguration, timer: &mut Timer<'_>) {
15 let city = CityName::seattle();
16
17 download(
24 config,
25 city.input_path("parcels_urbansim.txt"),
26 "http://abstreet.s3-website.us-east-2.amazonaws.com/dev/data/input/us/seattle/parcels_urbansim.txt.gz",
27 )
28 .await;
29 download(
30 config,
31 city.input_path("trips_2014.csv"),
32 "http://abstreet.s3-website.us-east-2.amazonaws.com/dev/data/input/us/seattle/trips_2014.csv.gz",
33 )
34 .await;
35
36 let bounds = geom::GPSBounds::from(
37 geom::LonLat::read_geojson_polygon("importer/config/us/seattle/huge_seattle.geojson")
38 .unwrap(),
39 );
40 download_kml(
42 city.input_path("blockface.bin"),
43 "https://opendata.arcgis.com/datasets/a1458ad1abca41869b81f7c0db0cd777_0.kml",
44 &bounds,
45 true,
46 timer,
47 )
48 .await;
49 download_kml(
51 city.input_path("offstreet_parking.bin"),
52 "http://data-seattlecitygis.opendata.arcgis.com/datasets/8e52dfde6d5d45948f7a90654c8d50cd_0.kml",
53 &bounds,
54 true,
55 timer
56 ).await;
57
58 download(
61 config,
62 city.input_path("collisions.kml"),
63 "https://opendata.arcgis.com/datasets/5b5c745e0f1f48e7a53acec63a0022ab_0.kml",
64 )
65 .await;
66
67 if !abstio::file_exists(city.input_path("collisions.bin")) {
70 let shapes = kml::load(city.input_path("collisions.kml"), &bounds, true, timer).unwrap();
71 let collisions = collisions::import_seattle(
72 shapes,
73 "https://data-seattlecitygis.opendata.arcgis.com/datasets/5b5c745e0f1f48e7a53acec63a0022ab_0");
74 abstio::write_binary(city.input_path("collisions.bin"), &collisions);
75 }
76
77 download_kml(
79 city.input_path("zoning_parcels.bin"),
80 "https://opendata.arcgis.com/datasets/42863f1debdc47488a1c2b9edd38053e_2.kml",
81 &bounds,
82 true,
83 timer,
84 )
85 .await;
86
87 download_kml(
90 city.input_path("land_use.bin"),
91 "https://opendata.arcgis.com/datasets/dd29065b5d01420e9686570c2b77502b_0.kml",
92 &bounds,
93 false,
94 timer,
95 )
96 .await;
97}
98
99pub async fn ensure_popdat_exists(
101 timer: &mut Timer<'_>,
102 config: &ImporterConfiguration,
103 built_raw_huge_seattle: &mut bool,
104 built_map_huge_seattle: &mut bool,
105) -> (crate::soundcast::PopDat, map_model::Map) {
106 let huge_name = MapName::seattle("huge_seattle");
107
108 if abstio::file_exists(abstio::path_popdat()) {
109 println!("- {} exists, not regenerating it", abstio::path_popdat());
110 return (
111 abstio::read_binary(abstio::path_popdat(), timer),
112 map_model::Map::load_synchronously(huge_name.path(), timer),
113 );
114 }
115
116 if !abstio::file_exists(abstio::path_raw_map(&huge_name)) {
117 crate::utils::osm_to_raw(MapName::seattle("huge_seattle"), timer, config).await;
118 *built_raw_huge_seattle = true;
119 }
120 let huge_map = if abstio::file_exists(huge_name.path()) {
121 map_model::Map::load_synchronously(huge_name.path(), timer)
122 } else {
123 *built_map_huge_seattle = true;
124 crate::utils::raw_to_map(&huge_name, map_model::RawToMapOptions::default(), timer)
125 };
126
127 (crate::soundcast::import_data(&huge_map, timer), huge_map)
128}
129
130pub fn adjust_private_parking(map: &mut Map, scenario: &Scenario) {
131 for (b, count) in count_parked_cars_per_bldg(scenario).consume() {
132 map.hack_override_offstreet_spots_individ(b, count);
133 }
134 map.save();
135}
136
137pub fn match_parcels_to_buildings(map: &mut Map, shapes: &ExtraShapes, timer: &mut Timer) {
140 let mut parcels_with_housing: Vec<(Polygon, usize)> = Vec::new();
141 let mut quadtree = QuadTree::builder();
144 timer.start_iter("index all parcels", shapes.shapes.len());
145 for shape in &shapes.shapes {
146 timer.next();
147 if let Some(units) = shape
148 .attributes
149 .get("EXIST_UNITS")
150 .and_then(|x| x.parse::<usize>().ok())
151 {
152 if let Some(ring) = map
153 .get_gps_bounds()
154 .try_convert(&shape.points)
155 .and_then(|pts| Ring::new(pts).ok())
156 {
157 let polygon = ring.into_polygon();
158 quadtree.add_with_box(parcels_with_housing.len(), polygon.get_bounds());
159 parcels_with_housing.push((polygon, units));
160 }
161 }
162 }
163 let quadtree = quadtree.build();
164
165 let mut used_parcels: HashSet<usize> = HashSet::new();
166 let mut units_per_bldg: Vec<(BuildingID, usize)> = Vec::new();
167 timer.start_iter("match buildings to parcels", map.all_buildings().len());
168 for b in map.all_buildings() {
169 timer.next();
170 for idx in quadtree.query_bbox(b.polygon.get_bounds()) {
172 if used_parcels.contains(&idx)
173 || !parcels_with_housing[idx].0.contains_pt(b.label_center)
174 {
175 continue;
176 }
177 used_parcels.insert(idx);
178 units_per_bldg.push((b.id, parcels_with_housing[idx].1));
179 }
180 }
181
182 for (b, num_housing_units) in units_per_bldg {
183 let bldg_type = match map.get_b(b).bldg_type.clone() {
184 BuildingType::Residential { num_residents, .. } => BuildingType::Residential {
185 num_housing_units,
186 num_residents,
187 },
188 x => x,
189 };
190 map.hack_override_bldg_type(b, bldg_type);
191 }
192
193 map.save();
194}