thrust/data/faa/
arcgis.rs1use 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}