1use geom::Pt2D;
2use map_model::{BuildingID, IntersectionID, Map, PathConstraints, PathRequest, Position};
3use serde::{Deserialize, Serialize};
4
5use crate::TripMode;
6
7#[derive(Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Debug, Clone, Copy)]
9pub enum TripEndpoint {
10 Building(BuildingID),
11 Border(IntersectionID),
12 SuddenlyAppear(Position),
14}
15
16impl TripEndpoint {
17 pub fn pt(self, map: &Map) -> Pt2D {
19 match self {
20 TripEndpoint::Building(b) => map.get_b(b).polygon.center(),
21 TripEndpoint::Border(i) => map.get_i(i).polygon.center(),
22 TripEndpoint::SuddenlyAppear(pos) => pos.pt(map),
23 }
24 }
25
26 pub fn path_req(
31 from: TripEndpoint,
32 to: TripEndpoint,
33 mode: TripMode,
34 map: &Map,
35 ) -> Option<PathRequest> {
36 let start = from.pos(mode, true, map)?;
37 let end = to.pos(mode, false, map)?;
38 Some(match mode {
39 TripMode::Walk | TripMode::Transit => PathRequest::walking(start, end),
40 TripMode::Bike => PathRequest::vehicle(start, end, PathConstraints::Bike),
41 TripMode::Drive => {
43 if matches!(from, TripEndpoint::Building(_)) {
44 PathRequest::leave_from_driveway(start, end, PathConstraints::Car, map)
45 } else {
46 PathRequest::vehicle(start, end, PathConstraints::Car)
47 }
48 }
49 })
50 }
51
52 fn pos(self, mode: TripMode, from: bool, map: &Map) -> Option<Position> {
53 match mode {
54 TripMode::Walk | TripMode::Transit => self.sidewalk_pos(map, from),
55 TripMode::Drive | TripMode::Bike => {
56 let constraints = mode.to_constraints();
57 if from {
58 match self {
59 TripEndpoint::Building(_) => {}
61 TripEndpoint::Border(i) => {
62 return map.get_i(i).some_outgoing_road(map).and_then(|dr| {
63 dr.lanes(constraints, map)
64 .get(0)
65 .map(|l| Position::start(*l))
66 });
67 }
68 TripEndpoint::SuddenlyAppear(pos) => {
69 return Some(pos);
70 }
71 }
72 }
73
74 match self {
75 TripEndpoint::Building(b) => match constraints {
76 PathConstraints::Car => {
77 let driving_lane = map.find_driving_lane_near_building(b);
78 let sidewalk_pos = map.get_b(b).sidewalk_pos;
79 if driving_lane.road == sidewalk_pos.lane().road {
80 Some(sidewalk_pos.equiv_pos(driving_lane, map))
81 } else {
82 Some(Position::start(driving_lane))
83 }
84 }
85 PathConstraints::Bike => Some(map.get_b(b).biking_connection(map)?.0),
86 PathConstraints::Bus
87 | PathConstraints::Train
88 | PathConstraints::Pedestrian => {
89 unreachable!()
90 }
91 },
92 TripEndpoint::Border(i) => {
93 map.get_i(i).some_incoming_road(map).and_then(|dr| {
94 let lanes = dr.lanes(constraints, map);
95 if lanes.is_empty() {
96 None
97 } else {
98 Some(Position::end(lanes[0], map))
100 }
101 })
102 }
103 TripEndpoint::SuddenlyAppear(pos) => Some(pos),
105 }
106 }
107 }
108 }
109
110 fn sidewalk_pos(self, map: &Map, from: bool) -> Option<Position> {
111 match self {
112 TripEndpoint::Building(b) => Some(map.get_b(b).sidewalk_pos),
113 TripEndpoint::Border(i) => {
114 if from {
115 TripEndpoint::start_walking_at_border(i, map)
116 } else {
117 TripEndpoint::end_walking_at_border(i, map)
118 }
119 }
120 TripEndpoint::SuddenlyAppear(pos) => Some(pos),
121 }
122 }
123
124 pub fn start_walking_at_border(i: IntersectionID, map: &Map) -> Option<Position> {
126 let lanes = map
127 .get_i(i)
128 .get_outgoing_lanes(map, PathConstraints::Pedestrian);
129 if !lanes.is_empty() {
130 return Some(Position::start(lanes[0]));
131 }
132 map.get_i(i)
133 .get_incoming_lanes(map, PathConstraints::Pedestrian)
134 .get(0)
135 .map(|l| Position::end(*l, map))
136 }
137
138 pub fn end_walking_at_border(i: IntersectionID, map: &Map) -> Option<Position> {
139 if let Some(l) = map
140 .get_i(i)
141 .get_incoming_lanes(map, PathConstraints::Pedestrian)
142 .get(0)
143 {
144 return Some(Position::end(*l, map));
145 }
146
147 let lanes = map
148 .get_i(i)
149 .get_outgoing_lanes(map, PathConstraints::Pedestrian);
150 if lanes.is_empty() {
151 return None;
152 }
153 Some(Position::start(lanes[0]))
154 }
155}