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