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