1use serde::{Deserialize, Serialize};
2
3use abstio::{CityName, MapName};
4use abstutil::Timer;
5use geom::{GPSBounds, LonLat, Polygon, Ring};
6
7use crate::{AreaType, Map};
8
9const POLYGON_EPSILON: f64 = 1000.0;
11
12#[derive(Serialize, Deserialize)]
15pub struct City {
16 pub name: CityName,
17 pub boundary: Polygon,
18 pub areas: Vec<(AreaType, Polygon)>,
19 pub districts: Vec<(MapName, Polygon)>,
21 }
23
24impl City {
25 pub fn from_huge_map(huge_map: &Map) -> City {
27 let city_name = huge_map.get_city_name().clone();
28 let mut districts = abstio::list_dir(format!(
29 "importer/config/{}/{}",
30 city_name.country, city_name.city
31 ))
32 .into_iter()
33 .filter(|path| path.ends_with(".geojson"))
34 .map(|path| {
35 let pts = LonLat::read_geojson_polygon(&path).unwrap();
36 (
37 MapName::from_city(&city_name, &abstutil::basename(path)),
38 Ring::must_new(huge_map.get_gps_bounds().convert(&pts)).into_polygon(),
39 )
40 })
41 .collect::<Vec<_>>();
42 districts.sort_by_key(|(_, poly)| poly.get_bounds().width() as usize);
45
46 City {
47 name: city_name,
48 boundary: huge_map.get_boundary_polygon().clone(),
49 areas: huge_map
50 .all_areas()
51 .iter()
52 .map(|a| (a.area_type, a.polygon.simplify(POLYGON_EPSILON)))
53 .collect(),
54 districts,
55 }
56 }
57
58 pub fn from_individual_maps(city_name: &CityName, timer: &mut Timer) -> City {
61 let boundary_per_district: Vec<(MapName, Vec<LonLat>)> = abstio::list_dir(format!(
62 "importer/config/{}/{}",
63 city_name.country, city_name.city
64 ))
65 .into_iter()
66 .filter(|path| path.ends_with(".geojson"))
67 .map(|path| {
68 (
69 MapName::from_city(city_name, &abstutil::basename(&path)),
70 LonLat::read_geojson_polygon(&path).unwrap(),
71 )
72 })
73 .collect();
74 let mut gps_bounds = GPSBounds::new();
76 for (_, pts) in &boundary_per_district {
77 for pt in pts {
78 gps_bounds.update(*pt);
79 }
80 }
81 let boundary = gps_bounds.to_bounds().get_rectangle();
82
83 let mut districts = Vec::new();
84 for (name, pts) in boundary_per_district {
85 districts.push((
86 name,
87 Ring::must_new(gps_bounds.convert(&pts)).into_polygon(),
88 ));
89 }
90 districts.sort_by_key(|(_, poly)| poly.get_bounds().width() as usize);
93
94 let mut areas = Vec::new();
96 for path in abstio::list_dir(abstio::path(format!(
97 "system/{}/{}/maps",
98 city_name.country, city_name.city
99 ))) {
100 let map = Map::load_synchronously(path, timer);
101 for area in map.all_areas() {
102 let pts = map
103 .gps_bounds
104 .convert_back(area.polygon.get_outer_ring().points());
105 if let Ok(ring) = Ring::new(gps_bounds.convert(&pts)) {
107 areas.push((
108 area.area_type,
109 ring.into_polygon().simplify(POLYGON_EPSILON),
110 ));
111 }
112 }
113 }
114
115 City {
116 name: city_name.clone(),
117 boundary,
118 areas,
119 districts,
120 }
121 }
122}