1use std::collections::{HashSet, VecDeque};
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5
6use abstutil::{deserialize_usize, serialize_usize, Tags};
7use geom::{Distance, PolyLine, Polygon, Pt2D};
8
9use crate::{osm, Amenity, AmenityType, LaneID, Map, NamePerLanguage, PathConstraints, Position};
10
11#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
12pub struct BuildingID(
13 #[serde(
14 serialize_with = "serialize_usize",
15 deserialize_with = "deserialize_usize"
16 )]
17 pub usize,
18);
19
20impl fmt::Display for BuildingID {
21 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
22 write!(f, "Building #{}", self.0)
23 }
24}
25
26#[derive(Serialize, Deserialize, Clone, Debug)]
29pub struct Building {
30 pub id: BuildingID,
31 pub polygon: Polygon,
32 pub levels: f64,
33 pub address: String,
34 pub name: Option<NamePerLanguage>,
35 pub orig_id: osm::OsmID,
36 pub label_center: Pt2D,
39 pub amenities: Vec<Amenity>,
40 pub bldg_type: BuildingType,
41 pub parking: OffstreetParking,
42 pub osm_tags: Tags,
44
45 pub sidewalk_pos: Position,
48 pub driveway_geom: PolyLine,
50}
51
52#[derive(Serialize, Deserialize, Debug, PartialEq, Clone)]
54pub enum OffstreetParking {
55 PublicGarage(String, usize),
57 Private(usize, bool),
59}
60
61#[derive(Serialize, Deserialize, Clone, Debug)]
62pub enum BuildingType {
63 Residential {
64 num_residents: usize,
65 num_housing_units: usize,
66 },
67 ResidentialCommercial(usize, usize),
69 Commercial(usize),
71 Empty,
72}
73
74impl BuildingType {
75 pub fn has_residents(&self) -> bool {
76 match self {
77 BuildingType::Residential { .. } | BuildingType::ResidentialCommercial(_, _) => true,
78 BuildingType::Commercial(_) | BuildingType::Empty => false,
79 }
80 }
81}
82
83impl Building {
84 pub fn sidewalk(&self) -> LaneID {
85 self.sidewalk_pos.lane()
86 }
87
88 pub fn driving_connection(&self, map: &Map) -> Option<(Position, PolyLine)> {
91 let lane = map
92 .get_parent(self.sidewalk())
93 .find_closest_lane(self.sidewalk(), |l| PathConstraints::Car.can_use(l, map))?;
94 let pos = self
96 .sidewalk_pos
97 .equiv_pos(lane, map)
98 .buffer_dist(Distance::meters(7.0), map)?;
99 Some((pos, self.driveway_geom.clone().optionally_push(pos.pt(map))))
100 }
101
102 pub fn biking_connection(&self, map: &Map) -> Option<(Position, Position)> {
105 if let Some(pair) = sidewalk_to_bike(self.sidewalk_pos, map) {
107 return Some(pair);
108 }
109
110 let mut queue: VecDeque<LaneID> = VecDeque::new();
112 let mut visited: HashSet<LaneID> = HashSet::new();
113 queue.push_back(self.sidewalk());
114
115 loop {
116 if queue.is_empty() {
117 return None;
118 }
119 let l = queue.pop_front().unwrap();
120 if visited.contains(&l) {
121 continue;
122 }
123 visited.insert(l);
124 if let Some(pair) = sidewalk_to_bike(Position::new(l, map.get_l(l).length() / 2.0), map)
126 {
127 return Some(pair);
128 }
129 for (_, next) in map.get_next_turns_and_lanes(l) {
130 if next.is_walkable() && !visited.contains(&next.id) {
131 queue.push_back(next.id);
132 }
133 }
134 }
135 }
136
137 pub fn num_parking_spots(&self) -> usize {
138 match self.parking {
139 OffstreetParking::PublicGarage(_, n) => n,
140 OffstreetParking::Private(n, _) => n,
141 }
142 }
143
144 pub fn has_amenity(&self, category: AmenityType) -> bool {
146 for amenity in &self.amenities {
147 if AmenityType::categorize(&amenity.amenity_type) == Some(category) {
148 return true;
149 }
150 }
151 false
152 }
153}
154
155fn sidewalk_to_bike(sidewalk_pos: Position, map: &Map) -> Option<(Position, Position)> {
156 let lane = map
157 .get_parent(sidewalk_pos.lane())
158 .find_closest_lane(sidewalk_pos.lane(), |l| {
159 !l.biking_blackhole && PathConstraints::Bike.can_use(l, map)
160 })?;
161 Some((sidewalk_pos.equiv_pos(lane, map), sidewalk_pos))
163}