use derive_more::{AsRef, Display, From};
use uom::si::f64::{Length, Time, Velocity};
use uom::si::length::meter;
#[must_use]
pub fn default_lane_width() -> Length {
Length::new::<meter>(3.2)
}
#[must_use]
pub fn default_lane_end_offset() -> Length {
Length::new::<meter>(0.0)
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
pub struct EdgeId(#[as_ref(forward)] pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
pub struct LaneId(#[as_ref(forward)] pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
pub struct JunctionId(#[as_ref(forward)] pub String);
#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
pub struct TrafficLightId(#[as_ref(forward)] pub String);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From)]
pub struct LaneIndex(pub usize);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From)]
pub struct Priority(pub i32);
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From)]
pub struct LinkIndex(pub i32);
pub use crate::sumo::Point;
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Shape(pub Vec<Point>);
#[derive(Debug, Clone, Copy, Default, PartialEq)]
pub struct Boundary {
pub min: Point,
pub max: Point,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub enum Projection {
#[default]
None,
Proj4(String),
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Location {
pub net_offset: Point,
pub converted_boundary: Boundary,
pub original_boundary: Boundary,
pub projection: Projection,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum EdgeFunction {
Normal,
Internal,
Connector,
Crossing,
Walkingarea,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SpreadType {
Center,
RoadCenter,
Right,
}
pub use crate::sumo::{UnknownVClass, VClass};
#[derive(Debug, Clone, PartialEq)]
pub struct Lane {
pub id: LaneId,
pub index: LaneIndex,
pub speed: Velocity,
pub length: Length,
pub width: Length,
pub end_offset: Length,
pub shape: Shape,
pub allow: Vec<String>,
pub disallow: Vec<String>,
}
impl Lane {
#[must_use]
pub fn permits(&self, class: VClass) -> bool {
class.permitted_on(&self.allow, &self.disallow)
}
#[must_use]
pub fn is_pedestrian_only(&self) -> bool {
!self.allow.is_empty() && self.allow.iter().all(|vclass| vclass == "pedestrian")
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Edge {
pub id: EdgeId,
pub function: EdgeFunction,
pub from: Option<JunctionId>,
pub to: Option<JunctionId>,
pub name: Option<String>,
pub priority: Option<Priority>,
pub length: Option<Length>,
pub shape: Option<Shape>,
pub spread_type: Option<SpreadType>,
pub lanes: Vec<Lane>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum JunctionKind {
TrafficLight,
TrafficLightUnregulated,
TrafficLightRightOnRed,
RailSignal,
RailCrossing,
Priority,
PriorityStop,
LeftBeforeRight,
RightBeforeLeft,
AllWayStop,
Zipper,
District,
Unregulated,
Internal,
DeadEnd,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Junction {
pub id: JunctionId,
pub position: Point,
pub kind: JunctionKind,
pub incoming_lanes: Vec<LaneId>,
pub internal_lanes: Vec<LaneId>,
pub shape: Option<Shape>,
pub name: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
pub enum ConnectionDirection {
Straight,
Turn,
TurnLeftHand,
Left,
Right,
PartialLeft,
PartialRight,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LinkState {
Major,
Minor,
TlsOffNoSignal,
TlsOffBlinking,
Equal,
Stop,
AllWayStop,
Zipper,
DeadEnd,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Connection {
pub from_edge: EdgeId,
pub to_edge: EdgeId,
pub from_lane: LaneIndex,
pub to_lane: LaneIndex,
pub direction: ConnectionDirection,
pub state: LinkState,
pub via: Option<LaneId>,
pub traffic_light: Option<TrafficLightId>,
pub link_index: Option<LinkIndex>,
pub pass: bool,
pub keep_clear: bool,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Roundabout {
pub nodes: Vec<JunctionId>,
pub edges: Vec<EdgeId>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TrafficLightKind {
Actuated,
DelayBased,
Static,
Nema,
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum TrafficLightOffset {
Fixed(Time),
Begin,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Phase {
pub duration: Time,
pub state: String,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TrafficLightProgram {
pub id: TrafficLightId,
pub program_id: String,
pub kind: Option<TrafficLightKind>,
pub offset: Option<TrafficLightOffset>,
pub phases: Vec<Phase>,
}
#[derive(Debug, Clone, Default, PartialEq)]
pub struct Network {
pub location: Location,
pub edges: Vec<Edge>,
pub junctions: Vec<Junction>,
pub connections: Vec<Connection>,
pub roundabouts: Vec<Roundabout>,
pub traffic_light_programs: Vec<TrafficLightProgram>,
}
#[cfg(test)]
mod vclass_tests {
use super::*;
fn lane_with(allow: &[&str], disallow: &[&str]) -> Lane {
Lane {
id: LaneId("e0_0".into()),
index: LaneIndex(0),
speed: Velocity::new::<uom::si::velocity::meter_per_second>(10.0),
length: Length::new::<meter>(20.0),
width: default_lane_width(),
end_offset: default_lane_end_offset(),
shape: Shape::default(),
allow: allow.iter().map(|s| (*s).to_string()).collect(),
disallow: disallow.iter().map(|s| (*s).to_string()).collect(),
}
}
#[test]
fn a_lane_with_neither_attribute_permits_every_class() {
let lane = lane_with(&[], &[]);
assert!(lane.permits(VClass::Passenger));
assert!(lane.permits(VClass::Bicycle));
assert!(lane.permits(VClass::Pedestrian));
}
#[test]
fn disallow_excludes_only_the_classes_it_names() {
let lane = lane_with(&[], &["pedestrian", "tram"]);
assert!(!lane.permits(VClass::Pedestrian));
assert!(!lane.permits(VClass::Tram));
assert!(lane.permits(VClass::Passenger));
assert!(lane.permits(VClass::Bicycle));
}
#[test]
fn a_non_empty_allow_is_exhaustive_disallow_is_not_consulted() {
let lane = lane_with(&["pedestrian"], &["pedestrian"]);
assert!(lane.permits(VClass::Pedestrian));
assert!(!lane.permits(VClass::Passenger));
}
#[test]
fn disallow_all_permits_nothing() {
let lane = lane_with(&[], &["all"]);
assert!(!lane.permits(VClass::Passenger));
assert!(!lane.permits(VClass::Pedestrian));
assert!(!lane.permits(VClass::Bicycle));
}
#[test]
fn allow_all_permits_everything_the_same_as_no_allow_at_all() {
let lane = lane_with(&["all"], &[]);
assert!(lane.permits(VClass::Passenger));
assert!(lane.permits(VClass::Pedestrian));
}
#[test]
fn an_unknown_token_in_disallow_does_not_affect_any_known_class() {
let lane = lane_with(&[], &["some_future_vclass"]);
assert!(lane.permits(VClass::Passenger));
assert!(lane.permits(VClass::Bicycle));
}
#[test]
fn an_unknown_token_in_a_non_empty_allow_grants_no_known_class() {
let lane = lane_with(&["some_future_vclass"], &[]);
assert!(!lane.permits(VClass::Passenger));
}
#[test]
fn a_real_barcelona_style_vehicle_lane_permits_car_moped_and_bicycle() {
let lane = lane_with(
&[],
&[
"pedestrian",
"tram",
"rail_urban",
"rail",
"rail_electric",
"rail_fast",
"ship",
"container",
"cable_car",
"subway",
"aircraft",
"wheelchair",
"scooter",
"drone",
],
);
assert!(lane.permits(VClass::Passenger));
assert!(lane.permits(VClass::Motorcycle));
assert!(lane.permits(VClass::Moped));
assert!(lane.permits(VClass::Bicycle));
assert!(!lane.permits(VClass::Pedestrian));
}
#[test]
fn a_pedestrian_only_lane_permits_only_pedestrians() {
let lane = lane_with(&["pedestrian"], &[]);
assert!(lane.permits(VClass::Pedestrian));
assert!(!lane.permits(VClass::Passenger));
assert!(!lane.permits(VClass::Bicycle));
}
#[test]
fn from_str_rejects_an_unknown_token() {
let err = "spaceship".parse::<VClass>().unwrap_err();
assert_eq!(err, UnknownVClass("spaceship".to_string()));
}
}