osm-to-euroscope

A tool to generate Radar Screens from OSM Data
git clone git://archive.git.mtrnord.blog/MTRNord/osm-to-euroscope.git
Log | Files | Refs | README | LICENSE

euroscope_ground.rs (4625B)


      1 use std::fs;
      2 use std::fs::File;
      3 use std::io::{BufReader, Cursor, Write};
      4 use std::path::Path;
      5 
      6 use fnv::FnvHashMap;
      7 use osmio::obj_types::StringOSMObj;
      8 use osmio::xml::XMLReader;
      9 use osmio::{Node, OSMObjBase, OSMReader, Way};
     10 use reqwest::{Request, Response};
     11 
     12 use crate::types;
     13 use crate::types::{Coords, LatLon, Refs};
     14 use percent_encoding::{percent_encode, NON_ALPHANUMERIC};
     15 
     16 pub(crate) async fn generate_ese_ground_taxiway(
     17     airport_name: &str,
     18 ) -> Result<(), Box<dyn std::error::Error>> {
     19     let out_path_string = format!("./out/{}.ese", airport_name);
     20     let out_path = Path::new(&out_path_string);
     21     if !out_path.parent().unwrap().exists() {
     22         fs::create_dir_all(out_path.parent().unwrap()).expect("unable to generate dir");
     23     }
     24     let out_file = &mut File::create(out_path).expect("unable to open file");
     25 
     26     out_file
     27         .write_all(b"[GROUND]\n")
     28         .expect("failed to write file");
     29 
     30     let client = reqwest::Client::builder().build()?;
     31     let query0 = "data=";
     32     let query1 = "[out:xml][timeout:25];
     33 area[icao=";
     34     let query2 = "]->.searchArea;
     35 (
     36   node[\"aeroway\"=\"taxiway\"][\"ref\"](area.searchArea);
     37   way[\"aeroway\"=\"taxiway\"][\"ref\"](area.searchArea);
     38   relation[\"aeroway\"=\"taxiway\"][\"ref\"](area.searchArea);
     39 );
     40 out body;
     41 >;
     42 out skel qt;";
     43     let complete: String = [query1, airport_name, query2].concat();
     44     let query = percent_encode(complete.replace("\n", "").as_bytes(), NON_ALPHANUMERIC).to_string();
     45     let query_complete: String = [query0, query.as_str()].concat();
     46     let req: Request = client.post("https://overpass-api.de/api/interpreter")
     47         .body(query_complete).build()?;
     48 
     49     let resp: Response = client.execute(req).await?;
     50 
     51     let data = resp.text().await?;
     52 
     53     let data_buffer = Cursor::new(data);
     54     let buf_reader = BufReader::new(data_buffer);
     55 
     56     let mut osm = XMLReader::new(buf_reader);
     57 
     58     let mut nodes: Vec<types::Node> = Vec::new();
     59     let mut ways: FnvHashMap<String, Refs> = FnvHashMap::default();
     60     let mut taxiways: FnvHashMap<String, Coords> = FnvHashMap::default();
     61 
     62     osm.objects().for_each(|object| match object {
     63         StringOSMObj::Node(n) => {
     64             let lat_lon = n.lat_lon().expect("unable to get lat_lon");
     65             let new_node = types::Node {
     66                 id: n.id(),
     67                 lat: lat_lon.0 as f64,
     68                 lon: lat_lon.1 as f64,
     69             };
     70             nodes.push(new_node);
     71         }
     72         StringOSMObj::Way(w) => {
     73             let mut tags = w.tags();
     74             if tags.any(|x| x.0 == "aeroway" && x.1 == "taxiway") {
     75                 if tags.any(|x| x.0 == "ref") {
     76                     let value = w
     77                         .tags()
     78                         .find(|x| x.0 == "ref")
     79                         .expect("unable to find ref")
     80                         .1;
     81                     let mut nodes: Vec<i64> = Vec::from(w.nodes());
     82 
     83                     if ways.contains_key(value) {
     84                         ways.get_mut(value).unwrap().append(&mut nodes);
     85                     } else {
     86                         ways.insert(value.to_string(), nodes);
     87                     }
     88                 }
     89             }
     90         }
     91         StringOSMObj::Relation(_) => {}
     92     });
     93     for (key, val) in ways.iter() {
     94         val.iter().for_each(|&refval| {
     95             if let Some(node) = nodes.iter().find(|&x| x.id == refval) {
     96                 let lat_lon = LatLon {
     97                     lat: node.lat,
     98                     lon: node.lon,
     99                 };
    100 
    101                 if taxiways.contains_key(key.as_str()) {
    102                     (*(taxiways.get_mut(key.as_str()).unwrap())).push(lat_lon);
    103                 } else {
    104                     let mut coords: Coords = Vec::new();
    105                     coords.push(lat_lon);
    106                     taxiways.insert(key.clone(), coords);
    107                 }
    108             }
    109         });
    110     }
    111 
    112     write_taxiways(out_file, airport_name, taxiways);
    113 
    114     out_file.sync_all().expect("unable to sync to file");
    115     Ok(())
    116 }
    117 
    118 fn write_taxiways(out_file: &mut File, airport_name: &str, taxiways: FnvHashMap<String, Coords>) {
    119     taxiways.iter().for_each(|(name, coordinates)| {
    120         let type_string = format!("TAXI:{} {}:20:1\n", airport_name, name);
    121         out_file
    122             .write_all(type_string.as_bytes())
    123             .expect("failed to write file");
    124 
    125         let local_coords: Vec<&LatLon> = coordinates.iter().clone().collect();
    126         local_coords.iter().for_each(|latlon| {
    127             out_file
    128                 .write_all(format_coordinate!(latlon.lat, latlon.lon))
    129                 .expect("failed to write file");
    130         });
    131     });
    132 }