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
use std::cmp::Ordering;
use std::collections::{BinaryHeap, HashMap};
use geom::{Distance, Duration, Speed};
use crate::pathfind::WalkingNode;
use crate::{BuildingID, LaneType, Map, PathConstraints};
#[derive(Clone)]
pub struct WalkingOptions {
pub allow_shoulders: bool,
pub walking_speed: Speed,
}
impl WalkingOptions {
pub fn default() -> WalkingOptions {
WalkingOptions {
allow_shoulders: true,
walking_speed: Speed::meters_per_second(1.34),
}
}
fn cost(&self, dist: Distance) -> Duration {
dist / self.walking_speed
}
}
#[derive(PartialEq, Eq)]
struct Item {
cost: Duration,
node: WalkingNode,
}
impl PartialOrd for Item {
fn partial_cmp(&self, other: &Item) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for Item {
fn cmp(&self, other: &Item) -> Ordering {
let ord = other.cost.cmp(&self.cost);
if ord != Ordering::Equal {
return ord;
}
self.node.cmp(&other.node)
}
}
pub fn all_walking_costs_from(
map: &Map,
start: BuildingID,
time_limit: Duration,
opts: WalkingOptions,
) -> HashMap<BuildingID, Duration> {
let start_lane = map.get_l(map.get_b(start).sidewalk_pos.lane());
if start_lane.lane_type == LaneType::Shoulder && !opts.allow_shoulders {
return HashMap::new();
}
let mut queue: BinaryHeap<Item> = BinaryHeap::new();
queue.push(Item {
cost: Duration::ZERO,
node: WalkingNode::closest(map.get_b(start).sidewalk_pos, map),
});
let mut cost_per_node: HashMap<WalkingNode, Duration> = HashMap::new();
while let Some(current) = queue.pop() {
if cost_per_node.contains_key(¤t.node) {
continue;
}
if current.cost > time_limit {
continue;
}
cost_per_node.insert(current.node, current.cost);
let (l, is_dst_i) = match current.node {
WalkingNode::SidewalkEndpoint(l, is_dst_i) => (l, is_dst_i),
_ => unreachable!(),
};
let lane = map.get_l(l);
if opts.allow_shoulders || lane.lane_type != LaneType::Shoulder {
queue.push(Item {
cost: current.cost + opts.cost(lane.length()),
node: WalkingNode::SidewalkEndpoint(lane.id, !is_dst_i),
});
}
for turn in map.get_turns_for(lane.id, PathConstraints::Pedestrian) {
if (turn.id.parent == lane.dst_i) != is_dst_i {
continue;
}
queue.push(Item {
cost: current.cost + opts.cost(turn.geom.length()),
node: WalkingNode::SidewalkEndpoint(
turn.id.dst,
map.get_l(turn.id.dst).dst_i == turn.id.parent,
),
});
}
}
let mut results = HashMap::new();
for b in map.all_buildings() {
if let Some(cost) = cost_per_node.get(&WalkingNode::closest(b.sidewalk_pos, map)) {
results.insert(b.id, *cost);
}
}
results
}