Skip to main content

thrust/data/faa/
arcgis.rs

1use serde::{Deserialize, Serialize};
2use serde_json::Value;
3
4#[cfg(feature = "net")]
5const OPENDATA_BASE: &str = "https://opendata.arcgis.com/datasets";
6
7const ATS_ROUTE_DATASET: &str = "acf64966af5f48a1a40fdbcb31238ba7_0";
8const DESIGNATED_POINTS_DATASET: &str = "861043a88ff4486c97c3789e7dcdccc6_0";
9const NAVAID_COMPONENTS_DATASET: &str = "c9254c171b6741d3a5e494860761443a_0";
10const AIRSPACE_BOUNDARY_DATASET: &str = "67885972e4e940b2aa6d74024901c561_0";
11const CLASS_AIRSPACE_DATASET: &str = "c6a62360338e408cb1512366ad61559e_0";
12const SPECIAL_USE_AIRSPACE_DATASET: &str = "dd0d1b726e504137ab3c41b21835d05b_0";
13const ROUTE_AIRSPACE_DATASET: &str = "8bf861bb9b414f4ea9f0ff2ca0f1a851_0";
14const PROHIBITED_AIRSPACE_DATASET: &str = "354ee0c77484461198ebf728a2fca50c_0";
15
16#[derive(Debug, Clone, Serialize, Deserialize, Default)]
17pub struct FaaFeature {
18    pub properties: Value,
19    pub geometry: Value,
20}
21
22#[derive(Debug, Clone, Serialize, Deserialize, Default)]
23pub struct FaaOpenData {
24    pub ats_routes: Vec<FaaFeature>,
25    pub designated_points: Vec<FaaFeature>,
26    pub navaid_components: Vec<FaaFeature>,
27    pub airspace_boundary: Vec<FaaFeature>,
28    pub class_airspace: Vec<FaaFeature>,
29    pub special_use_airspace: Vec<FaaFeature>,
30    pub route_airspace: Vec<FaaFeature>,
31    pub prohibited_airspace: Vec<FaaFeature>,
32}
33
34fn fetch_geojson(dataset_id: &str) -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
35    #[cfg(not(feature = "net"))]
36    {
37        let _ = dataset_id;
38        Err("FAA ArcGIS network fetch is disabled; enable feature 'net'".into())
39    }
40
41    #[cfg(feature = "net")]
42    {
43        let url = format!("{OPENDATA_BASE}/{dataset_id}.geojson");
44        let payload = reqwest::blocking::get(url)?.error_for_status()?.json::<Value>()?;
45
46        let features = payload
47            .get("features")
48            .and_then(|x| x.as_array())
49            .map(|arr| {
50                arr.iter()
51                    .map(|feature| FaaFeature {
52                        properties: feature.get("properties").cloned().unwrap_or(Value::Null),
53                        geometry: feature.get("geometry").cloned().unwrap_or(Value::Null),
54                    })
55                    .collect::<Vec<_>>()
56            })
57            .unwrap_or_default();
58
59        Ok(features)
60    }
61}
62
63pub fn parse_faa_ats_routes() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
64    fetch_geojson(ATS_ROUTE_DATASET)
65}
66
67pub fn parse_faa_designated_points() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
68    fetch_geojson(DESIGNATED_POINTS_DATASET)
69}
70
71pub fn parse_faa_navaid_components() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
72    fetch_geojson(NAVAID_COMPONENTS_DATASET)
73}
74
75pub fn parse_faa_airspace_boundary() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
76    fetch_geojson(AIRSPACE_BOUNDARY_DATASET)
77}
78
79pub fn parse_faa_class_airspace() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
80    fetch_geojson(CLASS_AIRSPACE_DATASET)
81}
82
83pub fn parse_faa_special_use_airspace() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
84    fetch_geojson(SPECIAL_USE_AIRSPACE_DATASET)
85}
86
87pub fn parse_faa_route_airspace() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
88    fetch_geojson(ROUTE_AIRSPACE_DATASET)
89}
90
91pub fn parse_faa_prohibited_airspace() -> Result<Vec<FaaFeature>, Box<dyn std::error::Error>> {
92    fetch_geojson(PROHIBITED_AIRSPACE_DATASET)
93}
94
95pub fn parse_all_faa_open_data() -> Result<FaaOpenData, Box<dyn std::error::Error>> {
96    Ok(FaaOpenData {
97        ats_routes: parse_faa_ats_routes()?,
98        designated_points: parse_faa_designated_points()?,
99        navaid_components: parse_faa_navaid_components()?,
100        airspace_boundary: parse_faa_airspace_boundary()?,
101        class_airspace: parse_faa_class_airspace()?,
102        special_use_airspace: parse_faa_special_use_airspace()?,
103        route_airspace: parse_faa_route_airspace()?,
104        prohibited_airspace: parse_faa_prohibited_airspace()?,
105    })
106}