ifc_lite_landxml/alignment/
model.rs

1// This Source Code Form is subject to the terms of the Mozilla Public
2// License, v. 2.0. If a copy of the MPL was not distributed with this
3// file, You can obtain one at https://mozilla.org/MPL/2.0/.
4
5//! Durable LandXML alignment records, deliberately independent of rendering.
6
7use serde::{Deserialize, Serialize};
8
9use crate::{LandXmlSourceId, LandXmlUnits};
10
11/// A LandXML plan coordinate in its declared `northing easting [elevation]`
12/// order. It is never silently swapped into a renderer axis convention.
13#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
14pub struct LandXmlPlanPoint {
15    pub northing: f64,
16    pub easting: f64,
17    pub elevation: Option<f64>,
18}
19
20/// A coordinate leaf as authored. Resolving `pntRef` belongs to the later COGO
21/// source adapter; numerical geometry refuses unresolved references explicitly.
22#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
23#[serde(tag = "kind", rename_all = "snake_case")]
24pub enum LandXmlPointLocation {
25    Coordinates { point: LandXmlPlanPoint },
26    PointReference { pnt_ref: String },
27}
28
29/// One authored `AlignPIs/AlignPI` location, preserved independently from
30/// primitive PI leaves and with a stable source identity.
31#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
32pub struct LandXmlAlignmentPi {
33    pub source_id: LandXmlSourceId,
34    pub location: LandXmlPointLocation,
35}
36
37#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
38#[serde(rename_all = "snake_case")]
39pub enum LandXmlRotation {
40    Clockwise,
41    CounterClockwise,
42}
43
44impl LandXmlRotation {
45    pub const fn sign(self) -> f64 {
46        match self {
47            Self::Clockwise => -1.0,
48            Self::CounterClockwise => 1.0,
49        }
50    }
51}
52
53/// LandXML uses `INF` for a transition's infinite radius.
54#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Serialize)]
55#[serde(rename_all = "snake_case")]
56pub enum LandXmlRadius {
57    Finite(f64),
58    Infinite,
59}
60
61#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
62pub struct LandXmlLine {
63    pub start: LandXmlPointLocation,
64    pub end: LandXmlPointLocation,
65    pub declared_length: Option<f64>,
66}
67
68#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
69pub struct LandXmlIrregularLine {
70    pub start: LandXmlPointLocation,
71    pub end: LandXmlPointLocation,
72    /// The source's intermediate polyline, retained rather than reduced to a
73    /// single straight segment.
74    pub points: Vec<LandXmlPlanPoint>,
75    pub declared_length: Option<f64>,
76}
77
78#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
79pub struct LandXmlCurve {
80    pub start: LandXmlPointLocation,
81    pub center: LandXmlPointLocation,
82    pub end: LandXmlPointLocation,
83    pub pi: Option<LandXmlPointLocation>,
84    pub rotation: LandXmlRotation,
85    pub radius: Option<f64>,
86    pub declared_length: Option<f64>,
87}
88
89#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
90pub struct LandXmlSpiral {
91    pub start: LandXmlPointLocation,
92    pub pi: LandXmlPointLocation,
93    pub end: LandXmlPointLocation,
94    /// Exact LandXML `spiType`, retained even when this numeric foundation does
95    /// not implement it.
96    pub spi_type: String,
97    pub radius_start: LandXmlRadius,
98    pub radius_end: LandXmlRadius,
99    pub rotation: LandXmlRotation,
100    pub declared_length: f64,
101}
102
103/// A source primitive with a durable source id independent of chunks or meshes.
104#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
105#[serde(tag = "kind", rename_all = "snake_case")]
106pub enum LandXmlAlignmentPrimitive {
107    Line(LandXmlLine),
108    IrregularLine(LandXmlIrregularLine),
109    Curve(LandXmlCurve),
110    Spiral(LandXmlSpiral),
111    /// Preserved in geometric order but deliberately unavailable to evaluation.
112    UnsupportedSpiral(LandXmlSpiral),
113}
114
115#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
116pub struct LandXmlAlignmentSegment {
117    pub source_id: LandXmlSourceId,
118    /// One-based source order within this `CoordGeom`.
119    pub ordinal: usize,
120    pub primitive: LandXmlAlignmentPrimitive,
121}
122
123#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
124pub struct LandXmlStationEquation {
125    pub source_id: LandXmlSourceId,
126    pub sta_internal: f64,
127    pub sta_ahead: f64,
128    pub sta_back: Option<f64>,
129    pub sta_increment: Option<String>,
130}
131
132#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
133pub struct LandXmlCantStation {
134    pub source_id: LandXmlSourceId,
135    pub station: f64,
136    pub applied_cant: f64,
137    pub equilibrium_cant: Option<f64>,
138    pub curvature: LandXmlRotation,
139    pub cant_deficiency: Option<f64>,
140    pub cant_excess: Option<f64>,
141    pub rate_of_change_of_applied_cant_over_time: Option<f64>,
142    pub rate_of_change_of_applied_cant_over_length: Option<f64>,
143    pub rate_of_change_of_cant_deficiency_over_time: Option<f64>,
144    pub cant_gradient: Option<f64>,
145    pub speed: Option<f64>,
146    pub transition_type: Option<String>,
147    pub adverse: Option<bool>,
148}
149#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
150pub struct LandXmlSpeedStation {
151    pub source_id: LandXmlSourceId,
152    pub station: f64,
153    pub speed: f64,
154}
155
156#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
157pub struct LandXmlCant {
158    pub source_id: LandXmlSourceId,
159    pub name: String,
160    pub gauge: f64,
161    pub rotation_point: Option<String>,
162    pub equilibrium_constant: Option<f64>,
163    pub applied_cant_constant: Option<f64>,
164    pub stations: Vec<LandXmlCantStation>,
165    pub speed_stations: Vec<LandXmlSpeedStation>,
166}
167
168/// The exact named events in LandXML `Superelevation`.
169#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
170#[serde(rename_all = "snake_case")]
171pub enum LandXmlSuperelevationEventKind {
172    BeginRunoutSta,
173    BeginRunoffSta,
174    FullSuperSta,
175    FullSuperelev,
176    RunoffSta,
177    StartofRunoutSta,
178    EndofRunoutSta,
179    AdverseSE,
180}
181
182#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
183pub struct LandXmlSuperelevationEvent {
184    pub source_id: LandXmlSourceId,
185    pub kind: LandXmlSuperelevationEventKind,
186    /// `None` retains an explicitly nil/empty schema event.
187    pub value: Option<String>,
188}
189
190#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
191pub struct LandXmlSuperelevation {
192    pub source_id: LandXmlSourceId,
193    pub sta_start: Option<f64>,
194    pub sta_end: Option<f64>,
195    pub events: Vec<LandXmlSuperelevationEvent>,
196}
197
198/// A transition retained but intentionally unavailable to numeric probing.
199#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
200pub struct LandXmlUnsupportedTransition {
201    pub source_id: LandXmlSourceId,
202    pub spi_type: String,
203    pub spiral: LandXmlSpiral,
204    pub reason: String,
205}
206
207#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
208pub struct LandXmlAlignment {
209    pub source_id: LandXmlSourceId,
210    pub ordinal: usize,
211    pub name: String,
212    pub length: f64,
213    pub sta_start: f64,
214    pub start: Option<LandXmlPointLocation>,
215    /// Authored `AlignPIs` leaves. They are an explicit source capability,
216    /// not inferred from primitive `PI` leaves or reconstructed geometry.
217    pub align_pis: Vec<LandXmlAlignmentPi>,
218    pub segments: Vec<LandXmlAlignmentSegment>,
219    pub station_equations: Vec<LandXmlStationEquation>,
220    pub cant: Option<LandXmlCant>,
221    pub superelevations: Vec<LandXmlSuperelevation>,
222    pub unsupported_transitions: Vec<LandXmlUnsupportedTransition>,
223}
224
225#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
226pub struct LandXmlAlignmentDocument {
227    /// Namespace-selected grammar used to parse this source, independent from
228    /// the producer's declared version string.
229    pub schema: String,
230    /// The producer-authored root version, retained verbatim as provenance.
231    pub version: String,
232    /// Compatibility observations retained for direct alignment-parser consumers.
233    pub capability_diagnostics: Vec<crate::LandXmlCapabilityDiagnostic>,
234    /// LandXML's declared linear/elevation units. Alignment geometry remains
235    /// authored-value data; a format-neutral georeferencing adapter owns any
236    /// coordinate conversion.
237    pub units: Option<LandXmlUnits>,
238    pub alignments: Vec<LandXmlAlignment>,
239    pub warnings: Vec<String>,
240}
241
242impl LandXmlAlignmentDocument {}