map_model/objects/
transit.rs

1//! Public transit stops and routes.
2
3use std::fmt;
4
5use anyhow::Result;
6use serde::{Deserialize, Serialize};
7
8use abstutil::{deserialize_usize, serialize_usize};
9use geom::Time;
10
11use crate::{LaneID, Map, Path, PathConstraints, PathRequest, Position, RoadID};
12
13#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
14pub struct TransitStopID {
15    pub road: RoadID,
16    /// As long as this is unique per road, this value is otherwise meaningless. Not contiguous or
17    /// ordered in any way.
18    pub(crate) idx: usize,
19}
20
21impl fmt::Display for TransitStopID {
22    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
23        write!(f, "TransitStopID({0}, {1})", self.road, self.idx)
24    }
25}
26
27#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord, Serialize, Deserialize)]
28pub struct TransitRouteID(
29    #[serde(
30        serialize_with = "serialize_usize",
31        deserialize_with = "deserialize_usize"
32    )]
33    pub usize,
34);
35
36impl fmt::Display for TransitRouteID {
37    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
38        write!(f, "TransitRoute #{}", self.0)
39    }
40}
41
42#[derive(Serialize, Deserialize, Clone, Debug, PartialEq)]
43pub struct TransitStop {
44    pub id: TransitStopID,
45    pub name: String,
46    pub gtfs_id: String,
47    /// These may be on different roads entirely, like for light rail platforms.
48    pub driving_pos: Position,
49    pub sidewalk_pos: Position,
50    /// If false, only buses serve this stop
51    pub is_train_stop: bool,
52}
53
54#[derive(Serialize, Deserialize, Clone, Debug)]
55pub struct TransitRoute {
56    pub id: TransitRouteID,
57    pub long_name: String,
58    pub short_name: String,
59    pub gtfs_id: String,
60    pub stops: Vec<TransitStopID>,
61    /// A transit vehicle spawns at the beginning of this lane. This lane may be at a border or the
62    /// first stop. For the non-border case, the lane must be long enough for the vehicle to spawn.
63    pub start: LaneID,
64    /// A transit vehicle either vanishes at its last stop or exits the map through this border.
65    pub end_border: Option<LaneID>,
66    pub route_type: PathConstraints,
67    /// Non-empty, times in order for one day when a vehicle should begin at start.
68    pub spawn_times: Vec<Time>,
69    /// Explicitly store whatever the original was, since this can't be reconstructed without side
70    /// input.
71    pub orig_spawn_times: Vec<Time>,
72}
73
74impl TransitRoute {
75    fn all_path_requests(&self, map: &Map) -> Vec<PathRequest> {
76        let mut steps = vec![PathRequest::vehicle(
77            Position::start(self.start),
78            map.get_ts(self.stops[0]).driving_pos,
79            self.route_type,
80        )];
81        for pair in self.stops.windows(2) {
82            steps.push(PathRequest::vehicle(
83                map.get_ts(pair[0]).driving_pos,
84                map.get_ts(pair[1]).driving_pos,
85                self.route_type,
86            ));
87        }
88
89        let last_stop_pos = map.get_ts(*self.stops.last().unwrap()).driving_pos;
90        if let Some(end) = self.end_border {
91            steps.push(PathRequest::vehicle(
92                last_stop_pos,
93                Position::end(end, map),
94                self.route_type,
95            ));
96        } else {
97            // Drive to the end of the lane with the last stop
98            steps.push(PathRequest::vehicle(
99                last_stop_pos,
100                Position::end(last_stop_pos.lane(), map),
101                self.route_type,
102            ));
103        }
104        steps
105    }
106
107    /// Entry i is the path to drive to stop i. The very last entry is to drive from the last step
108    /// to the place where the vehicle vanishes.
109    pub fn all_paths(&self, map: &Map) -> Result<Vec<Path>> {
110        let mut paths = Vec::new();
111        for req in self.all_path_requests(map) {
112            if req.start.lane().road == req.end.lane().road
113                && req.start.dist_along() > req.end.dist_along()
114            {
115                bail!(
116                    "Two consecutive stops are on the same road, but they travel backwards: {}",
117                    req
118                );
119            }
120
121            let path = map.pathfind(req)?;
122            if path.is_empty() {
123                bail!("Empty path between stops: {}", path.get_req());
124            }
125            paths.push(path);
126        }
127
128        for pair in paths.windows(2) {
129            if pair[0].get_req().end != pair[1].get_req().start {
130                bail!(
131                    "Transit route will warp from {} to {}",
132                    pair[0].get_req().end,
133                    pair[1].get_req().start
134                );
135            }
136        }
137
138        Ok(paths)
139    }
140
141    pub fn plural_noun(&self) -> &'static str {
142        if self.route_type == PathConstraints::Bus {
143            "buses"
144        } else {
145            "trains"
146        }
147    }
148}