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)]
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}