Skip to main content

sumo_types/net/
domain.rs

1//! Layer 2: this crate's own domain types.
2//!
3//! These types are independent of SUMO/XSD (unlike the private `schema`
4//! module, layer
5//! 1, which is an almost literal mirror of the `.xsd` files) and are
6//! produced from it by [`crate::schema_mapper`]. Consumers of this crate
7//! should work on these types, not on the ones generated by xsd-parser.
8
9use derive_more::{AsRef, Display, From};
10use uom::si::f64::{Length, Time, Velocity};
11use uom::si::length::meter;
12
13/// SUMO's default lane width (`SUMO_const_laneWidth`), used by netconvert
14/// whenever a lane's `width` attribute is omitted from `.net.xml`.
15///
16/// This is the value SUMO itself has used across recent versions; it isn't
17/// derived from anything in this repo, so double-check it against the SUMO
18/// version producing your input if precision matters.
19#[must_use]
20pub fn default_lane_width() -> Length {
21    Length::new::<meter>(3.2)
22}
23
24/// SUMO's default lane end offset: `0`, meaning the lane extends the full
25/// length of its edge. Used whenever a lane's `endOffset` attribute is
26/// omitted from `.net.xml`.
27#[must_use]
28pub fn default_lane_end_offset() -> Length {
29    Length::new::<meter>(0.0)
30}
31
32/// Identifier of an [`Edge`]. A distinct type from a bare `String` so it
33/// can't be passed where a [`LaneId`] is expected — a mistake that would
34/// silently compile (and silently misbehave, e.g. as a wrong `HashMap`
35/// lookup) if every id were just a `String`.
36#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
37pub struct EdgeId(#[as_ref(forward)] pub String);
38
39/// Identifier of a [`Lane`]. See [`EdgeId`] for why this isn't a bare `String`.
40#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
41pub struct LaneId(#[as_ref(forward)] pub String);
42
43/// Identifier of a [`Junction`]. See [`EdgeId`] for why this isn't a bare `String`.
44#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
45pub struct JunctionId(#[as_ref(forward)] pub String);
46
47/// Identifier of a traffic light program (`tlLogic`). See [`EdgeId`] for why
48/// this isn't a bare `String`.
49#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From, AsRef)]
50pub struct TrafficLightId(#[as_ref(forward)] pub String);
51
52/// Index of a [`Lane`] within its parent [`Edge`] (`0` is the rightmost lane
53/// in SUMO's convention). A distinct type from a bare `usize` so it can't be
54/// confused with unrelated counts (e.g. a traffic light link index).
55#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From)]
56pub struct LaneIndex(pub usize);
57
58/// Relative right-of-way ranking of an [`Edge`] (higher wins), derived by
59/// netconvert from the road classification. Can be negative (e.g. `-1` for
60/// internal/connector edges). A distinct type from a bare `i32` so it can't
61/// be confused with unrelated integers (e.g. a traffic light link index).
62#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From)]
63pub struct Priority(pub i32);
64
65/// Index of a [`Connection`] within its traffic light's signal group
66/// (`tlLogic/@state` / `phase/@state` character position). A distinct type
67/// from a bare `i32` so it can't be confused with unrelated integers (e.g.
68/// an edge's [`Priority`]).
69#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Display, From)]
70pub struct LinkIndex(pub i32);
71
72/// A point in the network's coordinate system, re-exported at this format's
73/// own path. Defined once for the whole crate — see the type's own docs for
74/// why this one is shared while the id newtypes above are not.
75pub use crate::sumo::Point;
76
77/// Polyline that defines the geometry of an edge, lane, or junction.
78#[derive(Debug, Clone, Default, PartialEq)]
79pub struct Shape(pub Vec<Point>);
80
81/// Bounding box (bottom-left corner, top-right corner).
82///
83/// [`Default`] is the degenerate box at the origin.
84#[derive(Debug, Clone, Copy, Default, PartialEq)]
85pub struct Boundary {
86    pub min: (f64, f64),
87    pub max: (f64, f64),
88}
89
90/// How a network's coordinates relate to real-world geographic coordinates,
91/// as declared by SUMO's `location/@projParameter`.
92#[derive(Debug, Clone, Default, PartialEq)]
93pub enum Projection {
94    /// SUMO's `"!"` sentinel: the network isn't georeferenced, its
95    /// coordinates are already local/Cartesian. Also the [`Default`]:
96    /// assuming plain Cartesian coordinates is the assumption that can't
97    /// silently misplace a network on the globe.
98    #[default]
99    None,
100    /// A PROJ definition string, e.g. `"+proj=utm +zone=32 +ellps=WGS84"`.
101    Proj4(String),
102}
103
104/// Geographic metadata of the network, as exported by netconvert. Unlike
105/// the XSD (which marks these attributes optional), a real `netconvert`
106/// output always populates all of them, so a missing one here is treated as
107/// a malformed input rather than modeled with `Option`.
108///
109/// [`Default`] is a non-georeferenced network at the origin with an empty
110/// bounding box — what an empty `.net.xml` would describe. It exists so
111/// [`Network`] can keep its own [`Default`] for test fixtures
112/// (`Network { edges, ..Default::default() }`) now that `location` is no
113/// longer an `Option`; it is not something [`crate::read_network`] ever
114/// produces.
115#[derive(Debug, Clone, Default, PartialEq)]
116pub struct Location {
117    pub net_offset: Point,
118    pub converted_boundary: Boundary,
119    pub original_boundary: Boundary,
120    pub projection: Projection,
121}
122
123/// The role an edge plays within the network.
124#[derive(Debug, Clone, Copy, PartialEq, Eq)]
125pub enum EdgeFunction {
126    Normal,
127    Internal,
128    Connector,
129    Crossing,
130    Walkingarea,
131}
132
133/// How the edge's width is distributed relative to its axis.
134#[derive(Debug, Clone, Copy, PartialEq, Eq)]
135pub enum SpreadType {
136    Center,
137    RoadCenter,
138    Right,
139}
140
141/// A lane within an edge.
142#[derive(Debug, Clone, PartialEq)]
143pub struct Lane {
144    pub id: LaneId,
145    pub index: LaneIndex,
146    pub speed: Velocity,
147    pub length: Length,
148    pub width: Length,
149    pub end_offset: Length,
150    pub shape: Shape,
151    pub allow: Vec<String>,
152    pub disallow: Vec<String>,
153}
154
155/// A directed edge (arc) of the network.
156#[derive(Debug, Clone, PartialEq)]
157pub struct Edge {
158    pub id: EdgeId,
159    pub function: EdgeFunction,
160    pub from: Option<JunctionId>,
161    pub to: Option<JunctionId>,
162    pub name: Option<String>,
163    pub priority: Option<Priority>,
164    pub length: Option<Length>,
165    pub shape: Option<Shape>,
166    pub spread_type: Option<SpreadType>,
167    pub lanes: Vec<Lane>,
168}
169
170/// The kind of right-of-way control a junction has (traffic light, priority,
171/// yield, ...).
172#[derive(Debug, Clone, Copy, PartialEq, Eq)]
173pub enum JunctionKind {
174    TrafficLight,
175    TrafficLightUnregulated,
176    TrafficLightRightOnRed,
177    RailSignal,
178    RailCrossing,
179    Priority,
180    PriorityStop,
181    LeftBeforeRight,
182    RightBeforeLeft,
183    AllWayStop,
184    Zipper,
185    District,
186    Unregulated,
187    Internal,
188    DeadEnd,
189}
190
191/// A network node where one or more edges meet.
192#[derive(Debug, Clone, PartialEq)]
193pub struct Junction {
194    pub id: JunctionId,
195    pub position: Point,
196    pub kind: JunctionKind,
197    pub incoming_lanes: Vec<LaneId>,
198    pub internal_lanes: Vec<LaneId>,
199    pub shape: Option<Shape>,
200    pub name: Option<String>,
201}
202
203/// Turning direction of a connection, following SUMO's convention
204/// (`SUMOXMLDefinitions::LinkDirection`).
205#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
206pub enum ConnectionDirection {
207    Straight,
208    Turn,
209    TurnLeftHand,
210    Left,
211    Right,
212    PartialLeft,
213    PartialRight,
214}
215
216/// Right-of-way state of a connection, following SUMO's convention
217/// (`SUMOXMLDefinitions::LinkState`) for the `state` attribute in `.net.xml`.
218#[derive(Debug, Clone, Copy, PartialEq, Eq)]
219pub enum LinkState {
220    /// Has the right of way ("major").
221    Major,
222    /// Does not have the right of way ("minor"), must yield.
223    Minor,
224    /// Controlled by a traffic light that is off, with no visible signal.
225    TlsOffNoSignal,
226    /// Controlled by a traffic light that is off, in blinking mode.
227    TlsOffBlinking,
228    /// Shared right of way (everyone yields equally).
229    Equal,
230    /// Must come to a full stop.
231    Stop,
232    /// "All-way stop" style intersection.
233    AllWayStop,
234    /// "Zipper" merge.
235    Zipper,
236    /// No real connection (dead end).
237    DeadEnd,
238}
239
240/// A connection between two lanes (possibly from different edges).
241#[derive(Debug, Clone, PartialEq)]
242pub struct Connection {
243    pub from_edge: EdgeId,
244    pub to_edge: EdgeId,
245    pub from_lane: LaneIndex,
246    pub to_lane: LaneIndex,
247    pub direction: ConnectionDirection,
248    pub state: LinkState,
249    pub via: Option<LaneId>,
250    pub traffic_light: Option<TrafficLightId>,
251    pub link_index: Option<LinkIndex>,
252    pub pass: bool,
253    pub keep_clear: bool,
254}
255
256/// A roundabout in the network.
257#[derive(Debug, Clone, PartialEq)]
258pub struct Roundabout {
259    pub nodes: Vec<JunctionId>,
260    pub edges: Vec<EdgeId>,
261}
262
263/// A traffic light program's control strategy (`tlLogic/@type`).
264#[derive(Debug, Clone, Copy, PartialEq, Eq)]
265pub enum TrafficLightKind {
266    Actuated,
267    DelayBased,
268    Static,
269    Nema,
270}
271
272/// When a traffic light program's cycle starts, relative to the simulation
273/// clock (`tlLogic/@offset`).
274#[derive(Debug, Clone, Copy, PartialEq)]
275pub enum TrafficLightOffset {
276    /// A fixed offset into the program's cycle.
277    Fixed(Time),
278    /// SUMO's `"begin"` sentinel: the cycle starts at the simulation's own
279    /// begin time rather than at a fixed offset.
280    Begin,
281}
282
283/// One phase of a [`TrafficLightProgram`] (`phaseType`): how long it lasts
284/// and the `state` string every [`Connection`] it controls reads from at
285/// that phase — see the program's own docs for how the two connect.
286///
287/// Only `duration` and `state` are modelled. `phaseType` also has a group of
288/// actuated/NEMA-controller attributes this crate doesn't read yet —
289/// `minDur`, `maxDur`, `earliestEnd`, `latestEnd`, `earlyTarget`,
290/// `finalTarget`, `yellow`, `red`, `vehext`, `next`, `name` — meaningful
291/// only for a [`TrafficLightProgram`] whose `kind` is
292/// [`TrafficLightKind::Actuated`] or [`TrafficLightKind::Nema`], and absent
293/// or ignored for the plain [`TrafficLightKind::Static`] case this crate was
294/// first built to round-trip.
295#[derive(Debug, Clone, PartialEq)]
296pub struct Phase {
297    pub duration: Time,
298    pub state: String,
299}
300
301/// A traffic light program (`tlLogic`): an ordered list of [`Phase`]s. The
302/// character at index `i` of every phase's `state` governs the
303/// [`Connection`] whose `link_index == i` — two links that always share the
304/// same character, in every phase, are controlled by the same physical
305/// signal head and should be treated as one group, even if SUMO assigns
306/// them different `link_index`es.
307///
308/// A `tlLogic` id can appear more than once in `.net.xml` (multiple
309/// programs sharing one junction, e.g. a normal one and a night-time one) —
310/// [`Self::program_id`] is what tells them apart; which one is "the" active
311/// program for a junction is left to the caller. This type only models a
312/// single program as parsed.
313#[derive(Debug, Clone, PartialEq)]
314pub struct TrafficLightProgram {
315    pub id: TrafficLightId,
316    /// `programID`: SUMO requires this, so unlike [`Self::kind`] and
317    /// [`Self::offset`] it can't be `None` — a `.net.xml` omitting it fails
318    /// to parse rather than yielding a program with no `program_id`.
319    pub program_id: String,
320    pub kind: Option<TrafficLightKind>,
321    pub offset: Option<TrafficLightOffset>,
322    pub phases: Vec<Phase>,
323}
324
325/// Complete model of a SUMO network (`.net.xml`), already converted to the
326/// project's own types and decoupled from the original XSD schema.
327///
328/// [`Default`] is an empty network with a default [`Location`], which is
329/// useful for building test fixtures but is never what
330/// [`crate::read_network`] returns: `net_file.xsd` requires `<location>`
331/// (`minOccurs="1"`), so a parsed `Network` always carries the real one.
332#[derive(Debug, Clone, Default, PartialEq)]
333pub struct Network {
334    /// Always present: `net_file.xsd` declares `<location>` as
335    /// `minOccurs="1"`, so a `.net.xml` without one fails to parse rather
336    /// than yielding a `Network` with no location.
337    pub location: Location,
338    pub edges: Vec<Edge>,
339    pub junctions: Vec<Junction>,
340    pub connections: Vec<Connection>,
341    pub roundabouts: Vec<Roundabout>,
342    pub traffic_light_programs: Vec<TrafficLightProgram>,
343}