Skip to main content

thrust_py/
field15.rs

1use pyo3::{exceptions::PyOSError, prelude::*, types::PyDict};
2use thrust::data::eurocontrol::database::{AirwayDatabase, ResolvedPoint, ResolvedRouteSegment};
3use thrust::data::field15::Field15Parser;
4
5#[pyclass]
6pub struct AiracDatabase {
7    database: AirwayDatabase,
8}
9
10#[pyclass(get_all)]
11#[derive(Debug, Clone)]
12pub struct Point {
13    latitude: f64,
14    longitude: f64,
15    name: Option<String>,
16}
17
18#[pymethods]
19impl Point {
20    fn __repr__(&self) -> String {
21        format!(
22            "Point(latitude={:.6}, longitude={:.6}{})",
23            self.latitude,
24            self.longitude,
25            match &self.name {
26                Some(name) => format!(", name='{}'", name),
27                None => String::new(),
28            }
29        )
30    }
31
32    fn to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
33        let d = PyDict::new(py);
34        d.set_item("latitude", self.latitude)?;
35        d.set_item("longitude", self.longitude)?;
36        if let Some(name) = &self.name {
37            d.set_item("name", name)?;
38        }
39        Ok(d.into())
40    }
41}
42
43impl From<ResolvedPoint> for Point {
44    fn from(point: ResolvedPoint) -> Self {
45        match point {
46            ResolvedPoint::AirportHeliport(airport) => Self {
47                latitude: airport.latitude,
48                longitude: airport.longitude,
49                name: Some(airport.icao),
50            },
51            ResolvedPoint::Navaid(navaid) => Self {
52                latitude: navaid.latitude,
53                longitude: navaid.longitude,
54                name: navaid.name,
55            },
56            ResolvedPoint::DesignatedPoint(designated_point) => Self {
57                latitude: designated_point.latitude,
58                longitude: designated_point.longitude,
59                name: Some(designated_point.designator),
60            },
61            ResolvedPoint::Coordinates { latitude, longitude } => Self {
62                latitude,
63                longitude,
64                name: None,
65            },
66            ResolvedPoint::None => Self {
67                latitude: 0.0,
68                longitude: 0.0,
69                name: None,
70            },
71        }
72    }
73}
74
75#[pyclass(get_all)]
76#[derive(Debug, Clone)]
77pub struct Segment {
78    start: Point,
79    end: Point,
80    name: Option<String>,
81}
82
83#[pymethods]
84impl Segment {
85    fn __repr__(&self) -> String {
86        format!(
87            "Segment(start={}, end={}{})",
88            self.start.__repr__(),
89            self.end.__repr__(),
90            match &self.name {
91                Some(name) => format!(", name='{}'", name),
92                None => String::new(),
93            }
94        )
95    }
96
97    fn to_dict(&self, py: Python<'_>) -> PyResult<Py<PyAny>> {
98        let d = PyDict::new(py);
99        d.set_item("start", self.start.to_dict(py)?)?;
100        d.set_item("end", self.end.to_dict(py)?)?;
101        if let Some(name) = &self.name {
102            d.set_item("name", name)?;
103        }
104        Ok(d.into())
105    }
106}
107
108impl From<ResolvedRouteSegment> for Segment {
109    fn from(segment: ResolvedRouteSegment) -> Self {
110        Self {
111            start: Point::from(segment.start),
112            end: Point::from(segment.end),
113            name: segment.name,
114        }
115    }
116}
117
118#[pymethods]
119impl AiracDatabase {
120    #[new]
121    fn new(path: String) -> PyResult<Self> {
122        let database =
123            AirwayDatabase::new(std::path::Path::new(&path)).map_err(|e| PyOSError::new_err(e.to_string()))?;
124        Ok(Self { database })
125    }
126
127    fn enrich_route(&self, route: String) -> Vec<Segment> {
128        let elements = Field15Parser::parse(&route);
129        let enriched = self.database.enrich_route(elements);
130        enriched.into_iter().map(Segment::from).collect()
131    }
132}
133
134pub fn init(py: Python<'_>) -> PyResult<Bound<'_, PyModule>> {
135    let m = PyModule::new(py, "field15")?;
136    m.add_class::<AiracDatabase>()?;
137    m.add_class::<Point>()?;
138    m.add_class::<Segment>()?;
139    Ok(m)
140}