diff --git a/src/ntfs/mod.rs b/src/ntfs/mod.rs index 4dda6fe11..8de7f723c 100644 --- a/src/ntfs/mod.rs +++ b/src/ntfs/mod.rs @@ -76,20 +76,33 @@ enum StopLocationType { BoardingArea, } -impl Into> for StopLocationType { - fn into(self) -> Option { +impl Into for StopLocationType { + fn into(self) -> StopType { let stop_type = match self { - StopLocationType::StopPoint => Some(StopType::Point), - StopLocationType::StopArea => Some(StopType::Zone), - StopLocationType::EntranceExit => Some(StopType::StopEntrance), - StopLocationType::PathwayInterconnectionNode => Some(StopType::GenericNode), - StopLocationType::BoardingArea => Some(StopType::BoardingArea), - _ => None, + StopLocationType::StopPoint => StopType::Point, + StopLocationType::StopArea => StopType::Zone, + StopLocationType::GeographicArea => StopType::Zone, + StopLocationType::EntranceExit => StopType::StopEntrance, + StopLocationType::PathwayInterconnectionNode => StopType::GenericNode, + StopLocationType::BoardingArea => StopType::BoardingArea, }; stop_type } } +impl From for StopLocationType { + fn from(stop_type: StopType) -> StopLocationType { + let stop_location_type = match stop_type { + StopType::Point => StopLocationType::StopPoint, + StopType::Zone => StopLocationType::StopArea, + StopType::StopEntrance => StopLocationType::EntranceExit, + StopType::GenericNode => StopLocationType::PathwayInterconnectionNode, + StopType::BoardingArea => StopLocationType::BoardingArea, + }; + stop_location_type + } +} + #[derive(Serialize, Deserialize, Debug, Clone)] struct Stop { #[serde(rename = "stop_id")] @@ -105,9 +118,9 @@ struct Stop { fare_zone_id: Option, zone_id: Option, #[serde(rename = "stop_lon")] - lon: f64, + lon: String, #[serde(rename = "stop_lat")] - lat: f64, + lat: String, #[serde(default, deserialize_with = "de_with_empty_default")] location_type: StopLocationType, parent_station: Option, diff --git a/src/ntfs/read.rs b/src/ntfs/read.rs index e340fe718..06b68ef12 100644 --- a/src/ntfs/read.rs +++ b/src/ntfs/read.rs @@ -31,6 +31,13 @@ use transit_model_collection::*; impl From for StopArea { fn from(stop: Stop) -> StopArea { + if stop.name.is_empty() { + warn!("stop_id: {}: for station stop_name is required", stop.id); + } + let coord = Coord::from((stop.lon, stop.lat)); + if coord == Coord::default() { + warn!("stop_id: {}: for station coordinates are required", stop.id); + } StopArea { id: stop.id, name: stop.name, @@ -38,10 +45,7 @@ impl From for StopArea { object_properties: KeysValues::default(), comment_links: CommentLinksT::default(), visible: stop.visible, - coord: Coord { - lon: stop.lon, - lat: stop.lat, - }, + coord: coord, timezone: stop.timezone, geometry_id: stop.geometry_id, equipment_id: stop.equipment_id, @@ -49,24 +53,59 @@ impl From for StopArea { } } } -impl StopPoint { - fn from_with_type(stop: Stop, stop_type: StopType) -> StopPoint { + +impl Into> for Stop { + fn into(self) -> Result { + if self.name.is_empty() { + warn!("stop_id: {}: for platform stop_name is required", self.id); + } + + let coord = Coord::from((self.lon, self.lat)); + if coord == Coord::default() { + warn!( + "stop_id: {}: for platform coordinates are required", + self.id + ); + } + let stop_area = StopArea { + id: self.id, + name: self.name, + codes: KeysValues::default(), + object_properties: KeysValues::default(), + comment_links: CommentLinksT::default(), + visible: self.visible, + coord: coord, + timezone: self.timezone, + geometry_id: self.geometry_id, + equipment_id: self.equipment_id, + level_id: self.level_id, + }; + Ok(stop_area) + } +} + +impl From for StopPoint { + fn from(stop: Stop) -> StopPoint { let id = stop.id; + if stop.name.is_empty() { + warn!("stop_id: {}: for plateform stop_name is required", id); + } + let coord = Coord::from((stop.lon, stop.lat)); + if coord == Coord::default() { + warn!("stop_id: {}: for plateform coordinates are required", id); + } StopPoint { id, name: stop.name, visible: stop.visible, - coord: Coord { - lon: stop.lon, - lat: stop.lat, - }, + coord: coord, stop_area_id: stop.parent_station.unwrap(), timezone: stop.timezone, geometry_id: stop.geometry_id, equipment_id: stop.equipment_id, fare_zone_id: stop.fare_zone_id, zone_id: stop.zone_id, - stop_type, + stop_type: stop.location_type.into(), platform_code: stop.platform_code, level_id: stop.level_id, ..Default::default() @@ -74,24 +113,95 @@ impl StopPoint { } } +impl Into> for Stop { + fn into(self) -> Result { + if self.name.is_empty() { + warn!("stop_id: {}: for platform name is required", self.id); + }; + + let coord: Coord = Coord::from((self.lon, self.lat)); + if coord == Coord::default() { + warn!( + "stop_id: {}: for platform coordinates are required", + self.id + ); + } + let stop_point = StopPoint { + id: self.id, + name: self.name, + visible: self.visible, + coord: coord, + stop_area_id: self + .parent_station + .unwrap_or_else(|| String::from("default_id")), + timezone: self.timezone, + geometry_id: self.geometry_id, + equipment_id: self.equipment_id, + fare_zone_id: self.fare_zone_id, + zone_id: self.zone_id, + stop_type: self.location_type.into(), + platform_code: self.platform_code, + level_id: self.level_id, + ..Default::default() + }; + Ok(stop_point) + } +} + impl StopLocation { - fn from_ntfs_stop(stop: Stop) -> StopLocation { - StopLocation { + fn from_ntfs_stop(stop: Stop) -> Result { + let coord: Coord = Coord::from((stop.lon, stop.lat)); + + if stop.location_type == StopLocationType::EntranceExit { + if coord == Coord::default() { + return Err(format_err!( + "stop_id: {}: for entrances/exits coordinates is required", + stop.id + )); + } + if stop.parent_station.is_none() { + return Err(format_err!( + "stop_id: {}: for entrances/exits parent_station is required", + stop.id + )); + } + if stop.name.is_empty() { + return Err(format_err!( + "stop_id: {}: for entrances/exits stop_name is required", + stop.id + )); + } + } + if stop.location_type == StopLocationType::PathwayInterconnectionNode { + if stop.parent_station.is_none() { + return Err(format_err!( + "stop_id: {}: for generic node parent_station is required", + stop.id + )); + } + } + if stop.location_type == StopLocationType::BoardingArea { + if stop.parent_station.is_none() { + return Err(format_err!( + "stop_id: {}: for boarding area parent_station is required", + stop.id + )); + } + } + let stop_location = StopLocation { id: stop.id, name: stop.name, comment_links: CommentLinksT::default(), visible: false, - coord: Coord { - lon: stop.lon, - lat: stop.lat, - }, + coord: coord, parent_id: stop.parent_station, timezone: stop.timezone, geometry_id: stop.geometry_id, equipment_id: stop.equipment_id, stop_type: stop.location_type.clone().into(), level_id: stop.level_id, - } + }; + Ok(stop_location) } } @@ -112,30 +222,25 @@ pub fn manage_stops(collections: &mut Collections, path: &path::Path) -> Result< let mut stop_areas = vec![]; let mut stop_points = vec![]; let mut stop_locations = vec![]; - for mut stop in ntfs_stops { + for stop in ntfs_stops { match stop.location_type { StopLocationType::StopPoint | StopLocationType::GeographicArea => { - let (stop_type, area_visibility) = - if stop.location_type == StopLocationType::GeographicArea { - (StopType::Zone, false) - } else { - (StopType::Point, true) - }; - let stop_point = if stop.parent_station.is_none() { - stop.parent_station = Some(String::from("default_id")); - let mut stop_point = StopPoint::from_with_type(stop, stop_type); + let mut stop_point: StopPoint = skip_fail!(stop.clone().into()); + if stop.parent_station.is_none() { let mut stop_area = StopArea::from(stop_point.clone()); stop_point.stop_area_id = stop_area.id.clone(); - stop_area.visible = area_visibility; + stop_area.visible = stop.location_type.clone() == StopLocationType::StopPoint; stop_areas.push(stop_area); - stop_point + stop_point.clone() } else { - StopPoint::from_with_type(stop, stop_type) + skip_fail!(stop.into()) }; stop_points.push(stop_point); } StopLocationType::StopArea => stop_areas.push(StopArea::from(stop)), - _ => stop_locations.push(StopLocation::from_ntfs_stop(stop)), + _ => { + stop_locations.push(skip_fail!(StopLocation::from_ntfs_stop(stop))); + } } } collections.stop_areas = CollectionWithId::new(stop_areas)?; diff --git a/src/ntfs/write.rs b/src/ntfs/write.rs index 07ff5df5a..6d9dd638e 100644 --- a/src/ntfs/write.rs +++ b/src/ntfs/write.rs @@ -422,46 +422,6 @@ pub fn write_fares_v1(base_path: &path::Path, collections: &Collections) -> Resu Ok(()) } -pub fn write_collection_with_id( - path: &path::Path, - file: &str, - collection: &CollectionWithId, -) -> Result<()> -where - T: Id + serde::Serialize, -{ - if collection.is_empty() { - return Ok(()); - } - info!("Writing {}", file); - let path = path.join(file); - let mut wtr = csv::Writer::from_path(&path).with_context(ctx_from_path!(path))?; - for obj in collection.values() { - wtr.serialize(obj).with_context(ctx_from_path!(path))?; - } - wtr.flush().with_context(ctx_from_path!(path))?; - - Ok(()) -} - -pub fn write_collection(path: &path::Path, file: &str, collection: &Collection) -> Result<()> -where - T: serde::Serialize, -{ - if collection.is_empty() { - return Ok(()); - } - info!("Writing {}", file); - let path = path.join(file); - let mut wtr = csv::Writer::from_path(&path).with_context(ctx_from_path!(path))?; - for obj in collection.values() { - wtr.serialize(obj).with_context(ctx_from_path!(path))?; - } - wtr.flush().with_context(ctx_from_path!(path))?; - - Ok(()) -} - pub fn write_stops( path: &path::Path, stop_points: &CollectionWithId, @@ -473,22 +433,21 @@ pub fn write_stops( let path = path.join(file); let mut wtr = csv::Writer::from_path(&path).with_context(ctx_from_path!(path))?; for st in stop_points.values() { - let location_type = match st.stop_type { - StopType::Point => StopLocationType::StopPoint, - StopType::Zone => StopLocationType::GeographicArea, - StopType::StopEntrance => StopLocationType::EntranceExit, - StopType::GenericNode => StopLocationType::PathwayInterconnectionNode, - StopType::BoardingArea => StopLocationType::BoardingArea, - }; + let location_type: StopLocationType; + if st.stop_type == StopType::Zone { + location_type = StopLocationType::GeographicArea; + } else { + location_type = StopLocationType::from(st.stop_type.clone()); + } wtr.serialize(Stop { id: st.id.clone(), visible: st.visible, name: st.name.clone(), - lat: st.coord.lat, - lon: st.coord.lon, + lat: st.coord.lat.to_string(), + lon: st.coord.lon.to_string(), fare_zone_id: st.fare_zone_id.clone(), zone_id: st.zone_id.clone(), - location_type, + location_type: location_type, parent_station: stop_areas.get(&st.stop_area_id).map(|sa| sa.id.clone()), timezone: st.timezone.clone(), equipment_id: st.equipment_id.clone(), @@ -504,8 +463,8 @@ pub fn write_stops( id: sa.id.clone(), visible: sa.visible, name: sa.name.clone(), - lat: sa.coord.lat, - lon: sa.coord.lon, + lat: sa.coord.lat.to_string(), + lon: sa.coord.lon.to_string(), fare_zone_id: None, zone_id: None, location_type: StopLocationType::StopArea, @@ -520,23 +479,16 @@ pub fn write_stops( } for sl in stop_locations.values() { - let location_type = match sl.stop_type { - Some(StopType::Point) => StopLocationType::StopPoint, - Some(StopType::Zone) => StopLocationType::GeographicArea, - Some(StopType::StopEntrance) => StopLocationType::EntranceExit, - Some(StopType::GenericNode) => StopLocationType::PathwayInterconnectionNode, - Some(StopType::BoardingArea) => StopLocationType::BoardingArea, - _ => StopLocationType::StopPoint, - }; + let (lon, lat) = sl.coord.into(); wtr.serialize(Stop { id: sl.id.clone(), visible: sl.visible, name: sl.name.clone(), - lat: sl.coord.lat, - lon: sl.coord.lon, + lat: lat, + lon: lon, fare_zone_id: None, zone_id: None, - location_type, + location_type: StopLocationType::from(sl.stop_type.clone()), parent_station: sl.parent_id.clone(), timezone: sl.timezone.clone(), equipment_id: sl.equipment_id.clone(), diff --git a/tests/fixtures/netexidf2ntfs/output/stops.txt b/tests/fixtures/netexidf2ntfs/output/stops.txt index 42ff2dc07..0821f8d66 100644 --- a/tests/fixtures/netexidf2ntfs/output/stops.txt +++ b/tests/fixtures/netexidf2ntfs/output/stops.txt @@ -6,6 +6,5 @@ prefix:FR:94033:ZDE:36493:STIF,Général de Gaulle,1,,,2.4847859314617384,48.852 prefix:FR:75115:ZDE:50121556:STIF,VIOLET,1,,,2.2899928475299345,48.84477786353687,0,prefix:Navitia:FR:75115:ZDE:50121556:STIF,Europe/Paris,,prefix:3,, prefix:FR:94033:LDA:415674:STIF,Place du Général de Gaulle,1,,,2.4850504232164936,48.85247660153115,1,,,,,, prefix:FR:91272:ZDL:50076060:STIF,Lycée RER,1,,,2.099077315019164,48.7005008034973,1,,,,,, -prefix:will_be_removed_by_sanitize,LES ROSES,1,,,0.0,0.0,1,,,,,, +prefix:will_be_removed_by_sanitize,LES ROSES,1,,,0,0,1,,,,,, prefix:Navitia:FR:75115:ZDE:50121556:STIF,VIOLET,1,,,2.2899928475299345,48.84477786353687,1,,Europe/Paris,,,, - diff --git a/tests/fixtures/restrict-validity-period/output/stops.txt b/tests/fixtures/restrict-validity-period/output/stops.txt index 5712f5166..ead6776b4 100644 --- a/tests/fixtures/restrict-validity-period/output/stops.txt +++ b/tests/fixtures/restrict-validity-period/output/stops.txt @@ -1,7 +1,7 @@ stop_id,stop_name,visible,fare_zone_id,zone_id,stop_lon,stop_lat,location_type,parent_station,stop_timezone,geometry_id,equipment_id,level_id,platform_code GDLM,Gare de Lyon (Metro),1,,,2.372987,48.844746,0,GDL,,,eq:kept,, GDLB,Gare de Lyon (Bus),1,,,2.372987,48.844746,0,GDL,,,,, -NATM,Nation (Metro),1,,,2.396497,48.84849,0,NAT,,,,, +NATM,Nation (Metro),1,,,2.396497,48.84849,2,NAT,,geo:7:kept,,, CDGM,Charles de Gaulle (Metro),1,,,2.795354,48.973965,0,CDG,,,,, CHAM,Châtelet (Metro),1,,,2.348145,48.858137,0,CHA,,,,, MTPB,Montparnasse (Bus),1,,,2.321783,48.842481,0,MTP,,,,,