Skip to content

Commit

Permalink
Merge pull request #120 from meszarosdezso/pathways
Browse files Browse the repository at this point in the history
Add support for pathways.txt
  • Loading branch information
antoine-de authored Mar 17, 2022
2 parents b826cca + e498ab8 commit 069fcca
Show file tree
Hide file tree
Showing 7 changed files with 169 additions and 3 deletions.
2 changes: 2 additions & 0 deletions fixtures/basic/pathways.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
pathway_id,from_stop_id,to_stop_id,mode,is_bidirectional,length,traversal_time,stair_count,max_slope,min_width,signposted_as,reversed_signposted_as
pathway1,stop1,stop3,1,0,,,,,,,
43 changes: 43 additions & 0 deletions src/enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -523,3 +523,46 @@ pub enum TransferType {
#[serde(rename = "3")]
Impossible,
}

/// Type of pathway between [from_stop] and [to_stop]
#[derive(Debug, Serialize, Deserialize, Derivative, Copy, Clone, PartialEq)]
#[derivative(Default)]
pub enum PathwayMode {
/// A walkway
#[serde(rename = "1")]
#[derivative(Default)]
Walkway,
/// Stairs
#[serde(rename = "2")]
Stairs,
/// Moving sidewalk / travelator
#[serde(rename = "3")]
MovingSidewalk,
/// Escalator
#[serde(rename = "4")]
Escalator,
/// Elevator
#[serde(rename = "5")]
Elevator,
/// A pathway that crosses into an area of the station where a
/// proof of payment is required (usually via a physical payment gate)
#[serde(rename = "6")]
FareGate,
/// Indicates a pathway exiting an area where proof-of-payment is required
/// into an area where proof-of-payment is no longer required.
#[serde(rename = "7")]
ExitGate,
}

/// Indicates in which direction the pathway can be used
#[derive(Debug, Serialize, Deserialize, Derivative, Copy, Clone, PartialEq)]
#[derivative(Default)]
pub enum PathwayDirectionType {
/// Unidirectional pathway, it can only be used from [from_stop_id] to [to_stop_id].
#[serde(rename = "0")]
#[derivative(Default)]
Unidirectional,
/// Bidirectional pathway, it can be used in the two directions.
#[serde(rename = "1")]
Bidirectional,
}
16 changes: 15 additions & 1 deletion src/gtfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,11 @@ impl TryFrom<RawGtfs> for Gtfs {
///
/// It might fail if some mandatory files couldn’t be read or if there are references to other objects that are invalid.
fn try_from(raw: RawGtfs) -> Result<Gtfs, Error> {
let stops = to_stop_map(raw.stops?, raw.transfers.unwrap_or_else(|| Ok(Vec::new()))?)?;
let stops = to_stop_map(
raw.stops?,
raw.transfers.unwrap_or_else(|| Ok(Vec::new()))?,
raw.pathways.unwrap_or(Ok(Vec::new()))?,
)?;
let frequencies = raw.frequencies.unwrap_or_else(|| Ok(Vec::new()))?;
let trips = create_trips(raw.trips?, raw.stop_times?, frequencies, &stops)?;

Expand Down Expand Up @@ -231,6 +235,7 @@ fn to_map<O: Id>(elements: impl IntoIterator<Item = O>) -> HashMap<String, O> {
fn to_stop_map(
stops: Vec<Stop>,
raw_transfers: Vec<RawTransfer>,
raw_pathways: Vec<RawPathway>,
) -> Result<HashMap<String, Arc<Stop>>, Error> {
let mut stop_map: HashMap<String, Stop> =
stops.into_iter().map(|s| (s.id.clone(), s)).collect();
Expand All @@ -244,6 +249,15 @@ fn to_stop_map(
.and_modify(|stop| stop.transfers.push(StopTransfer::from(transfer)));
}

for pathway in raw_pathways {
stop_map
.get(&pathway.to_stop_id)
.ok_or_else(|| Error::ReferenceError(pathway.to_stop_id.to_string()))?;
stop_map
.entry(pathway.from_stop_id.clone())
.and_modify(|stop| stop.pathways.push(Pathway::from(pathway)));
}

let res = stop_map
.into_iter()
.map(|(i, s)| (i, Arc::new(s)))
Expand Down
3 changes: 3 additions & 0 deletions src/gtfs_reader.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,7 @@ impl RawGtfsReader {
fare_attributes: self.read_objs_from_optional_path(p, "fare_attributes.txt"),
frequencies: self.read_objs_from_optional_path(p, "frequencies.txt"),
transfers: self.read_objs_from_optional_path(p, "transfers.txt"),
pathways: self.read_objs_from_optional_path(p, "pathways.txt"),
feed_info: self.read_objs_from_optional_path(p, "feed_info.txt"),
read_duration: Utc::now().signed_duration_since(now).num_milliseconds(),
files,
Expand Down Expand Up @@ -244,6 +245,7 @@ impl RawGtfsReader {
"fare_attributes.txt",
"frequencies.txt",
"transfers.txt",
"pathways.txt",
"feed_info.txt",
"shapes.txt",
] {
Expand Down Expand Up @@ -278,6 +280,7 @@ impl RawGtfsReader {
),
frequencies: self.read_optional_file(&file_mapping, &mut archive, "frequencies.txt"),
transfers: self.read_optional_file(&file_mapping, &mut archive, "transfers.txt"),
pathways: self.read_optional_file(&file_mapping, &mut archive, "pathways.txt"),
feed_info: self.read_optional_file(&file_mapping, &mut archive, "feed_info.txt"),
shapes: self.read_optional_file(&file_mapping, &mut archive, "shapes.txt"),
read_duration: Utc::now().signed_duration_since(now).num_milliseconds(),
Expand Down
85 changes: 85 additions & 0 deletions src/objects.rs
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,9 @@ pub struct Stop {
/// Transfers from this Stop
#[serde(skip)]
pub transfers: Vec<StopTransfer>,
/// Pathways from this stop
#[serde(skip)]
pub pathways: Vec<Pathway>,
}

impl Type for Stop {
Expand Down Expand Up @@ -720,3 +723,85 @@ impl fmt::Display for FeedInfo {
write!(f, "{}", self.name)
}
}

/// A graph representation to describe subway or train, with nodes (the locations) and edges (the pathways).
#[derive(Debug, Serialize, Deserialize, Default)]
pub struct RawPathway {
/// Uniquely identifies the pathway
#[serde(rename = "pathway_id")]
pub id: String,
/// Location at which the pathway begins
pub from_stop_id: String,
/// Location at which the pathway ends
pub to_stop_id: String,
/// Type of pathway between the specified (from_stop_id, to_stop_id) pair
pub mode: PathwayMode,
/// Indicates in which direction the pathway can be used
pub is_bidirectional: PathwayDirectionType,
/// Horizontal length in meters of the pathway from the origin location to the destination location
pub length: Option<f32>,
/// Average time in seconds needed to walk through the pathway from the origin location to the destination location
pub traversal_time: Option<u32>,
/// Number of stairs of the pathway
pub stair_count: Option<u32>,
/// Maximum slope ratio of the pathway
pub max_slope: Option<f32>,
/// Minimum width of the pathway in meters
pub min_width: Option<f32>,
/// String of text from physical signage visible to transit riders
pub signposted_as: Option<String>,
/// Same than the signposted_as field, but when the pathways is used backward
pub reversed_signposted_as: Option<String>,
}

/// Pathway going from a stop to another.
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Pathway {
/// Uniquely identifies the pathway
pub id: String,
/// Location at which the pathway ends
pub to_stop_id: String,
/// Type of pathway between the specified (from_stop_id, to_stop_id) pair
pub mode: PathwayMode,
/// Indicates in which direction the pathway can be used
pub is_bidirectional: PathwayDirectionType,
/// Horizontal length in meters of the pathway from the origin location to the destination location
pub length: Option<f32>,
/// Average time in seconds needed to walk through the pathway from the origin location to the destination location
pub traversal_time: Option<u32>,
/// Number of stairs of the pathway
pub stair_count: Option<u32>,
/// Maximum slope ratio of the pathway
pub max_slope: Option<f32>,
/// Minimum width of the pathway in meters
pub min_width: Option<f32>,
/// String of text from physical signage visible to transit riders
pub signposted_as: Option<String>,
/// Same than the signposted_as field, but when the pathways is used backward
pub reversed_signposted_as: Option<String>,
}

impl Id for Pathway {
fn id(&self) -> &str {
&self.id
}
}

impl From<RawPathway> for Pathway {
/// Converts from a [RawPathway] to a [Pathway]
fn from(raw: RawPathway) -> Self {
Self {
id: raw.id,
to_stop_id: raw.to_stop_id,
mode: raw.mode,
is_bidirectional: raw.is_bidirectional,
length: raw.length,
max_slope: raw.max_slope,
min_width: raw.min_width,
reversed_signposted_as: raw.reversed_signposted_as,
signposted_as: raw.signposted_as,
stair_count: raw.stair_count,
traversal_time: raw.traversal_time,
}
}
}
5 changes: 4 additions & 1 deletion src/raw_gtfs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@ pub struct RawGtfs {
pub frequencies: Option<Result<Vec<RawFrequency>, Error>>,
/// All Transfers, None if the file was absent as it is not mandatory
pub transfers: Option<Result<Vec<RawTransfer>, Error>>,
/// All Pathways, None if the file was absent as it is not mandatory
pub pathways: Option<Result<Vec<RawPathway>, Error>>,
/// All FeedInfo, None if the file was absent as it is not mandatory
pub feed_info: Option<Result<Vec<FeedInfo>, Error>>,
/// All StopTimes
Expand All @@ -57,7 +59,8 @@ impl RawGtfs {
" Frequencies: {}",
optional_file_summary(&self.frequencies)
);
println!(" Transfers {}", optional_file_summary(&self.transfers));
println!(" Transfers: {}", optional_file_summary(&self.transfers));
println!(" Pathways: {}", optional_file_summary(&self.pathways));
println!(" Feed info: {}", optional_file_summary(&self.feed_info));
}

Expand Down
18 changes: 17 additions & 1 deletion src/tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,22 @@ fn read_transfers() {
);
}

#[test]
fn read_pathways() {
let gtfs = Gtfs::from_path("fixtures/basic").expect("impossible to read gtfs");

let pathways = &gtfs.get_stop("stop1").unwrap().pathways;

assert_eq!(1, pathways.len());
assert_eq!("stop3", pathways[0].to_stop_id);
assert_eq!(PathwayMode::Walkway, pathways[0].mode);
assert_eq!(
PathwayDirectionType::Unidirectional,
pathways[0].is_bidirectional
);
assert_eq!(None, pathways[0].min_width);
}

#[test]
fn read_feed_info() {
let gtfs = Gtfs::from_path("fixtures/basic").expect("impossible to read gtfs");
Expand Down Expand Up @@ -277,7 +293,7 @@ fn display() {
#[test]
fn path_files() {
let gtfs = RawGtfs::from_path("fixtures/basic").expect("impossible to read gtfs");
assert_eq!(gtfs.files.len(), 12);
assert_eq!(gtfs.files.len(), 13);
}

#[test]
Expand Down

0 comments on commit 069fcca

Please sign in to comment.