1 use crate::{osm, LaneID, Map, PathConstraints, Position};
2 use abstutil::{
3 deserialize_btreemap, deserialize_usize, serialize_btreemap, serialize_usize, Tags,
4 };
5 use geom::{Distance, PolyLine, Polygon, Pt2D};
6 use serde::{Deserialize, Serialize};
7 use std::collections::{BTreeMap, BTreeSet, HashSet, VecDeque};
8 use std::fmt;
9
10 #[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
11 pub struct BuildingID(
12 #[serde(
13 serialize_with = "serialize_usize",
14 deserialize_with = "deserialize_usize"
15 )]
16 pub usize,
17 );
18
19 impl fmt::Display for BuildingID {
fmt(&self, f: &mut fmt::Formatter) -> fmt::Result20 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
21 write!(f, "Building #{}", self.0)
22 }
23 }
24
25 #[derive(Serialize, Deserialize, Debug)]
26 pub struct Building {
27 pub id: BuildingID,
28 pub polygon: Polygon,
29 pub address: String,
30 pub name: Option<NamePerLanguage>,
31 pub orig_id: osm::OsmID,
32 // Where a text label should be centered to have the best chances of being contained within the
33 // polygon.
34 pub label_center: Pt2D,
35 // TODO Might fold these into BuildingType::Commercial
36 // (Name, amenity)
37 pub amenities: BTreeSet<(NamePerLanguage, String)>,
38 pub bldg_type: BuildingType,
39 pub parking: OffstreetParking,
40
41 // The building's connection for pedestrians is immutable. For cars and bikes, it can change
42 // based on map edits, so don't cache it.
43 pub sidewalk_pos: Position,
44 // Goes from building to sidewalk
45 pub driveway_geom: PolyLine,
46 }
47
48 // Represent None as Private(0, false).
49 #[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
50 pub enum OffstreetParking {
51 // (Name, spots)
52 PublicGarage(String, usize),
53 // (Spots, explicitly tagged as a garage)
54 Private(usize, bool),
55 }
56
57 #[derive(Serialize, Deserialize, Debug)]
58 pub enum BuildingType {
59 // An estimated number of residents
60 Residential(usize),
61 ResidentialCommercial(usize),
62 Commercial,
63 Empty,
64 }
65
66 impl BuildingType {
has_residents(&self) -> bool67 pub fn has_residents(&self) -> bool {
68 match self {
69 BuildingType::Residential(_) | BuildingType::ResidentialCommercial(_) => true,
70 BuildingType::Commercial | BuildingType::Empty => false,
71 }
72 }
73 }
74
75 // None corresponds to the native name
76 #[derive(Serialize, Deserialize, Debug, PartialEq, Eq, PartialOrd, Ord, Clone)]
77 pub struct NamePerLanguage(
78 #[serde(
79 serialize_with = "serialize_btreemap",
80 deserialize_with = "deserialize_btreemap"
81 )]
82 pub(crate) BTreeMap<Option<String>, String>,
83 );
84
85 impl NamePerLanguage {
get(&self, lang: Option<&String>) -> &String86 pub fn get(&self, lang: Option<&String>) -> &String {
87 // TODO Can we avoid this clone?
88 let lang = lang.cloned();
89 if let Some(name) = self.0.get(&lang) {
90 return name;
91 }
92 &self.0[&None]
93 }
94
new(tags: &Tags) -> Option<NamePerLanguage>95 pub fn new(tags: &Tags) -> Option<NamePerLanguage> {
96 let native_name = tags.get(osm::NAME)?;
97 let mut map = BTreeMap::new();
98 map.insert(None, native_name.to_string());
99 for (k, v) in tags.inner() {
100 if let Some(lang) = k.strip_prefix("name:") {
101 map.insert(Some(lang.to_string()), v.to_string());
102 }
103 }
104 Some(NamePerLanguage(map))
105 }
106
unnamed() -> NamePerLanguage107 pub fn unnamed() -> NamePerLanguage {
108 let mut map = BTreeMap::new();
109 map.insert(None, "unnamed".to_string());
110 NamePerLanguage(map)
111 }
112 }
113
114 impl Building {
sidewalk(&self) -> LaneID115 pub fn sidewalk(&self) -> LaneID {
116 self.sidewalk_pos.lane()
117 }
118
house_number(&self) -> Option<String>119 pub fn house_number(&self) -> Option<String> {
120 let num = self.address.split(" ").next().unwrap();
121 if num != "???" {
122 Some(num.to_string())
123 } else {
124 None
125 }
126 }
127
128 // The polyline goes from the building to the driving position
129 // TODO Make this handle parking_blackhole
driving_connection(&self, map: &Map) -> Option<(Position, PolyLine)>130 pub fn driving_connection(&self, map: &Map) -> Option<(Position, PolyLine)> {
131 let lane = map.get_parent(self.sidewalk()).find_closest_lane(
132 self.sidewalk(),
133 |l| PathConstraints::Car.can_use(l, map),
134 map,
135 )?;
136 // TODO Do we need to insist on this buffer, now that we can make cars gradually appear?
137 let pos = self
138 .sidewalk_pos
139 .equiv_pos(lane, map)
140 .buffer_dist(Distance::meters(7.0), map)?;
141 Some((pos, self.driveway_geom.clone().must_push(pos.pt(map))))
142 }
143
144 // Returns (biking position, sidewalk position). Could fail if the biking graph is
145 // disconnected.
biking_connection(&self, map: &Map) -> Option<(Position, Position)>146 pub fn biking_connection(&self, map: &Map) -> Option<(Position, Position)> {
147 // Easy case: the building is directly next to a usable lane
148 if let Some(pair) = sidewalk_to_bike(self.sidewalk_pos, map) {
149 return Some(pair);
150 }
151
152 // Floodfill the sidewalk graph until we find a sidewalk<->bike connection.
153 let mut queue: VecDeque<LaneID> = VecDeque::new();
154 let mut visited: HashSet<LaneID> = HashSet::new();
155 queue.push_back(self.sidewalk());
156
157 loop {
158 if queue.is_empty() {
159 return None;
160 }
161 let l = queue.pop_front().unwrap();
162 if visited.contains(&l) {
163 continue;
164 }
165 visited.insert(l);
166 // TODO Could search by sidewalk endpoint
167 if let Some(pair) = sidewalk_to_bike(Position::new(l, map.get_l(l).length() / 2.0), map)
168 {
169 return Some(pair);
170 }
171 for t in map.get_turns_from_lane(l) {
172 if !visited.contains(&t.id.dst) {
173 queue.push_back(t.id.dst);
174 }
175 }
176 }
177 }
178
num_parking_spots(&self) -> usize179 pub fn num_parking_spots(&self) -> usize {
180 match self.parking {
181 OffstreetParking::PublicGarage(_, n) => n,
182 OffstreetParking::Private(n, _) => n,
183 }
184 }
185 }
186
sidewalk_to_bike(sidewalk_pos: Position, map: &Map) -> Option<(Position, Position)>187 fn sidewalk_to_bike(sidewalk_pos: Position, map: &Map) -> Option<(Position, Position)> {
188 let lane = map.get_parent(sidewalk_pos.lane()).find_closest_lane(
189 sidewalk_pos.lane(),
190 |l| !l.biking_blackhole && PathConstraints::Bike.can_use(l, map),
191 map,
192 )?;
193 // No buffer needed
194 Some((sidewalk_pos.equiv_pos(lane, map), sidewalk_pos))
195 }
196