From 32956544dee77af0bae391f74d5ebf22194fd559 Mon Sep 17 00:00:00 2001 From: Maximilian Zander Date: Thu, 17 Feb 2022 19:49:46 +0100 Subject: [PATCH 1/4] Add transfers to stop --- fixtures/basic/transfers.txt | 3 +++ src/enums.rs | 18 ++++++++++++++++ src/gtfs.rs | 21 +++++++++++++++---- src/gtfs_reader.rs | 3 +++ src/objects.rs | 40 ++++++++++++++++++++++++++++++++++++ src/raw_gtfs.rs | 3 +++ src/tests.rs | 34 +++++++++++++++++++++++++++++- 7 files changed, 117 insertions(+), 5 deletions(-) create mode 100644 fixtures/basic/transfers.txt diff --git a/fixtures/basic/transfers.txt b/fixtures/basic/transfers.txt new file mode 100644 index 0000000..92f0fac --- /dev/null +++ b/fixtures/basic/transfers.txt @@ -0,0 +1,3 @@ +from_stop_id,to_stop_id,transfer_type,min_transfer_time +stop3,stop5,0,60 +stop1,stop2,3, \ No newline at end of file diff --git a/src/enums.rs b/src/enums.rs index e22a4b4..1d0e28b 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -505,3 +505,21 @@ impl Serialize for Transfers { } } } +/// Defines the type of a [Transfer] +#[derive(Debug, Serialize, Deserialize, Derivative, Copy, Clone, PartialEq)] +#[derivative(Default)] +pub enum TransferType { + /// Recommended transfer point between routes + #[serde(rename = "0")] + #[derivative(Default)] + Recommended, + /// Departing vehicle waits for arriving one + #[serde(rename = "1")] + Timed, + /// Transfer requires a minimum amount of time between arrival and departure to ensure a connection. + #[serde(rename = "2")] + MinTime, + /// Transfer is not possible at this location + #[serde(rename = "3")] + Impossible +} \ No newline at end of file diff --git a/src/gtfs.rs b/src/gtfs.rs index 3dea6ba..4f16426 100644 --- a/src/gtfs.rs +++ b/src/gtfs.rs @@ -49,7 +49,7 @@ impl TryFrom 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 { - let stops = to_stop_map(raw.stops?); + let stops = to_stop_map(raw.stops?, raw.transfers.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)?; @@ -228,10 +228,23 @@ fn to_map(elements: impl IntoIterator) -> HashMap { .collect() } -fn to_stop_map(stops: Vec) -> HashMap> { - stops +fn to_stop_map(stops: Vec, raw_transfers: Vec) -> HashMap> { + let mut stop_map: HashMap = stops .into_iter() - .map(|s| (s.id.clone(), Arc::new(s))) + .map(|s| (s.id.clone(), s)) + .collect(); + + for transfer in raw_transfers { + stop_map + .entry(transfer.from_stop_id.clone()) + .and_modify(|stop| + stop.transfers.push(StopTransfer::from(transfer)) + ); + } + + stop_map + .into_iter() + .map(|(i,s)| (i, Arc::new(s))) .collect() } diff --git a/src/gtfs_reader.rs b/src/gtfs_reader.rs index f9f8e08..1211542 100644 --- a/src/gtfs_reader.rs +++ b/src/gtfs_reader.rs @@ -159,6 +159,7 @@ impl RawGtfsReader { shapes: self.read_objs_from_optional_path(p, "shapes.txt"), 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"), feed_info: self.read_objs_from_optional_path(p, "feed_info.txt"), read_duration: Utc::now().signed_duration_since(now).num_milliseconds(), files, @@ -242,6 +243,7 @@ impl RawGtfsReader { "trips.txt", "fare_attributes.txt", "frequencies.txt", + "transfers.txt", "feed_info.txt", "shapes.txt", ] { @@ -275,6 +277,7 @@ impl RawGtfsReader { "fare_attributes.txt", ), frequencies: self.read_optional_file(&file_mapping, &mut archive, "frequencies.txt"), + transfers: self.read_optional_file(&file_mapping, &mut archive, "transfers.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(), diff --git a/src/objects.rs b/src/objects.rs index 15ff87f..7e98aae 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -185,6 +185,9 @@ pub struct Stop { pub level_id: Option, /// Platform identifier for a platform stop (a stop belonging to a station) pub platform_code: Option, + /// Transfers from this Stop + #[serde(default)] + pub transfers: Vec } impl Type for Stop { @@ -634,6 +637,43 @@ impl Frequency { } } + +/// Transfer information between stops before merged into [Stop] +#[derive(Debug, Serialize, Deserialize, Default)] +pub struct RawTransfer { + /// Stop from which to leave + pub from_stop_id: String, + /// Stop which to transfer to + pub to_stop_id: String, + /// Type of the transfer + pub transfer_type: TransferType, + /// Minimum time needed to make the transfer in seconds + pub min_transfer_time: Option +} + +#[derive(Debug, Serialize, Deserialize, Default, Clone)] +/// Transfer information between stops +pub struct StopTransfer { + /// Stop which to transfer to + pub to_stop_id: String, + /// Type of the transfer + pub transfer_type: TransferType, + /// Minimum time needed to make the transfer in seconds + pub min_transfer_time: Option +} + +impl StopTransfer { + /// Converts from a [RawTransfer] to a [StopTransfer] + pub fn from(transfer: RawTransfer) -> Self { + Self { + to_stop_id: transfer.to_stop_id, + transfer_type: transfer.transfer_type, + min_transfer_time: transfer.min_transfer_time + } + } +} + + /// Meta-data about the feed. See #[derive(Debug, Serialize, Deserialize)] pub struct FeedInfo { diff --git a/src/raw_gtfs.rs b/src/raw_gtfs.rs index ccec343..9d9ca2e 100644 --- a/src/raw_gtfs.rs +++ b/src/raw_gtfs.rs @@ -29,6 +29,8 @@ pub struct RawGtfs { pub fare_attributes: Option, Error>>, /// All Frequencies, None if the file was absent as it is not mandatory pub frequencies: Option, Error>>, + /// All Transfers, None if the file was absent as it is not mandatory + pub transfers: Option, Error>>, /// All FeedInfo, None if the file was absent as it is not mandatory pub feed_info: Option, Error>>, /// All StopTimes @@ -55,6 +57,7 @@ impl RawGtfs { " Frequencies: {}", optional_file_summary(&self.frequencies) ); + println!(" Transfers {}", optional_file_summary(&self.transfers)); println!(" Feed info: {}", optional_file_summary(&self.feed_info)); } diff --git a/src/tests.rs b/src/tests.rs index 5e6d9de..14e89fb 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -143,6 +143,38 @@ fn read_fare_attributes() { ); } +#[test] +fn read_transfers() { + let gtfs = Gtfs::from_path("fixtures/basic").expect("impossible to read gtfs"); + assert_eq!(1, gtfs.get_stop("stop3").unwrap().transfers.len()); + assert_eq!(1, gtfs.get_stop("stop1").unwrap().transfers.len()); + + assert_eq!( + "stop5", + gtfs.get_stop("stop3").unwrap().transfers[0].to_stop_id + ); + assert_eq!( + "stop2", + gtfs.get_stop("stop1").unwrap().transfers[0].to_stop_id + ); + assert_eq!( + TransferType::Recommended, + gtfs.get_stop("stop3").unwrap().transfers[0].transfer_type + ); + assert_eq!( + TransferType::Impossible, + gtfs.get_stop("stop1").unwrap().transfers[0].transfer_type + ); + assert_eq!( + Some(60), + gtfs.get_stop("stop3").unwrap().transfers[0].min_transfer_time + ); + assert_eq!( + None, + gtfs.get_stop("stop1").unwrap().transfers[0].min_transfer_time + ); +} + #[test] fn read_feed_info() { let gtfs = Gtfs::from_path("fixtures/basic").expect("impossible to read gtfs"); @@ -241,7 +273,7 @@ fn display() { #[test] fn path_files() { let gtfs = RawGtfs::from_path("fixtures/basic").expect("impossible to read gtfs"); - assert_eq!(gtfs.files.len(), 11); + assert_eq!(gtfs.files.len(), 12); } #[test] From 25c7aa2ec5e902ea8deefad7faec5df913c27c88 Mon Sep 17 00:00:00 2001 From: Maximilian Zander Date: Thu, 17 Feb 2022 20:11:21 +0100 Subject: [PATCH 2/4] Fix naming in docstring --- src/enums.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/enums.rs b/src/enums.rs index 1d0e28b..b1a8679 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -505,7 +505,7 @@ impl Serialize for Transfers { } } } -/// Defines the type of a [Transfer] +/// Defines the type of a [StopTransfer] #[derive(Debug, Serialize, Deserialize, Derivative, Copy, Clone, PartialEq)] #[derivative(Default)] pub enum TransferType { From 17020d4abf7c7a3013c1225dba2616df7a7d2505 Mon Sep 17 00:00:00 2001 From: zandemax Date: Fri, 18 Feb 2022 14:00:27 +0100 Subject: [PATCH 3/4] Check for existence of to stop --- Cargo.toml | 2 +- src/enums.rs | 4 ++-- src/gtfs.rs | 29 ++++++++++++++++------------- src/objects.rs | 14 ++++++-------- 4 files changed, 25 insertions(+), 24 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 8039086..1c3acf6 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] description = "Read GTFS (public transit timetables) files" name = "gtfs-structures" -version = "0.30.0" +version = "0.31.0" authors = ["Tristram Gräbener ", "Antoine Desbordes "] repository = "https://github.com/rust-transit/gtfs-structure" license = "MIT" diff --git a/src/enums.rs b/src/enums.rs index b1a8679..48204f9 100644 --- a/src/enums.rs +++ b/src/enums.rs @@ -521,5 +521,5 @@ pub enum TransferType { MinTime, /// Transfer is not possible at this location #[serde(rename = "3")] - Impossible -} \ No newline at end of file + Impossible, +} diff --git a/src/gtfs.rs b/src/gtfs.rs index 4f16426..80d8d38 100644 --- a/src/gtfs.rs +++ b/src/gtfs.rs @@ -49,7 +49,7 @@ impl TryFrom 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 { - let stops = to_stop_map(raw.stops?, raw.transfers.unwrap_or(Ok(Vec::new()))?); + let stops = to_stop_map(raw.stops?, raw.transfers.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)?; @@ -228,24 +228,27 @@ fn to_map(elements: impl IntoIterator) -> HashMap { .collect() } -fn to_stop_map(stops: Vec, raw_transfers: Vec) -> HashMap> { - let mut stop_map: HashMap = stops - .into_iter() - .map(|s| (s.id.clone(), s)) - .collect(); - +fn to_stop_map( + stops: Vec, + raw_transfers: Vec, +) -> Result>, Error> { + let mut stop_map: HashMap = + stops.into_iter().map(|s| (s.id.clone(), s)).collect(); + for transfer in raw_transfers { + stop_map + .get(&transfer.to_stop_id) + .ok_or(Error::ReferenceError(transfer.to_stop_id.to_string()))?; stop_map .entry(transfer.from_stop_id.clone()) - .and_modify(|stop| - stop.transfers.push(StopTransfer::from(transfer)) - ); + .and_modify(|stop| stop.transfers.push(StopTransfer::from(transfer))); } - stop_map + let res = stop_map .into_iter() - .map(|(i,s)| (i, Arc::new(s))) - .collect() + .map(|(i, s)| (i, Arc::new(s))) + .collect(); + Ok(res) } fn to_shape_map(shapes: Vec) -> HashMap> { diff --git a/src/objects.rs b/src/objects.rs index 7e98aae..3c71e6a 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -186,8 +186,8 @@ pub struct Stop { /// Platform identifier for a platform stop (a stop belonging to a station) pub platform_code: Option, /// Transfers from this Stop - #[serde(default)] - pub transfers: Vec + #[serde(skip)] + pub transfers: Vec, } impl Type for Stop { @@ -637,7 +637,6 @@ impl Frequency { } } - /// Transfer information between stops before merged into [Stop] #[derive(Debug, Serialize, Deserialize, Default)] pub struct RawTransfer { @@ -648,10 +647,10 @@ pub struct RawTransfer { /// Type of the transfer pub transfer_type: TransferType, /// Minimum time needed to make the transfer in seconds - pub min_transfer_time: Option + pub min_transfer_time: Option, } -#[derive(Debug, Serialize, Deserialize, Default, Clone)] +#[derive(Debug, Default, Clone)] /// Transfer information between stops pub struct StopTransfer { /// Stop which to transfer to @@ -659,7 +658,7 @@ pub struct StopTransfer { /// Type of the transfer pub transfer_type: TransferType, /// Minimum time needed to make the transfer in seconds - pub min_transfer_time: Option + pub min_transfer_time: Option, } impl StopTransfer { @@ -668,12 +667,11 @@ impl StopTransfer { Self { to_stop_id: transfer.to_stop_id, transfer_type: transfer.transfer_type, - min_transfer_time: transfer.min_transfer_time + min_transfer_time: transfer.min_transfer_time, } } } - /// Meta-data about the feed. See #[derive(Debug, Serialize, Deserialize)] pub struct FeedInfo { From 43d2d742ee72123b0d8190496066033fed267246 Mon Sep 17 00:00:00 2001 From: zdmx Date: Wed, 23 Feb 2022 19:00:26 +0100 Subject: [PATCH 4/4] Update src/objects.rs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-authored-by: Tristram Gräbener --- src/objects.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/objects.rs b/src/objects.rs index 3c71e6a..75d76ba 100644 --- a/src/objects.rs +++ b/src/objects.rs @@ -661,9 +661,9 @@ pub struct StopTransfer { pub min_transfer_time: Option, } -impl StopTransfer { +impl From for StopTransfer { /// Converts from a [RawTransfer] to a [StopTransfer] - pub fn from(transfer: RawTransfer) -> Self { + fn from(transfer: RawTransfer) -> Self { Self { to_stop_id: transfer.to_stop_id, transfer_type: transfer.transfer_type,