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

commit 35734a7e6a83926aa03312cfb66a9f62c2937e6a
Author: Marcel <mtrnord1@gmail.com>
Date:   Sun, 29 Dec 2019 16:12:03 +0100

Initial Commit

Took 7 hours 8 minutes

Diffstat:
A.gitignore | 3+++
A.idea/.gitignore | 3+++
A.idea/misc.xml | 7+++++++
A.idea/modules.xml | 9+++++++++
A.idea/vcs.xml | 7+++++++
ACargo.lock | 275+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ACargo.toml | 11+++++++++++
AOSM_to_Euroscope.iml | 16++++++++++++++++
Asrc/main.rs | 169+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asrc/main_lahm.rs | 46++++++++++++++++++++++++++++++++++++++++++++++
Asrc/tests.rs | 20++++++++++++++++++++
11 files changed, 566 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore @@ -0,0 +1,3 @@ +/target +/data +/out diff --git a/.idea/.gitignore b/.idea/.gitignore @@ -0,0 +1,2 @@ +# Default ignored files +/workspace.xml +\ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectRootManager"> + <output url="file://$PROJECT_DIR$/out" /> + </component> +</project> +\ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml @@ -0,0 +1,8 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="ProjectModuleManager"> + <modules> + <module fileurl="file://$PROJECT_DIR$/OSM_to_Euroscope.iml" filepath="$PROJECT_DIR$/OSM_to_Euroscope.iml" /> + </modules> + </component> +</project> +\ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<project version="4"> + <component name="VcsDirectoryMappings"> + <mapping directory="" vcs="Git" /> + </component> +</project> +\ No newline at end of file diff --git a/Cargo.lock b/Cargo.lock @@ -0,0 +1,275 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +[[package]] +name = "OSM_to_Euroscope" +version = "0.1.0" +dependencies = [ + "fnv", + "osmpbf", +] + +[[package]] +name = "adler32" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d2e7343e7fc9de883d1b0341e0b13970f764c14101234857d2ddafa1cb1cac2" + +[[package]] +name = "autocfg" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d49d90015b3c36167a20fe2810c5cd875ad504b39cff3d4eae7977e6b7c1cb2" + +[[package]] +name = "byteorder" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" + +[[package]] +name = "cfg-if" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4785bdd1c96b2a846b2bd7cc02e86b6b3dbf14e7e53446c4f54c92a361040822" + +[[package]] +name = "crc32fast" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba125de2af0df55319f41944744ad91c71113bf74a4646efff39afe1f6842db1" +dependencies = [ + "cfg-if", +] + +[[package]] +name = "crossbeam-deque" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3aa945d63861bfe624b55d153a39684da1e8c0bc8fba932f7ee3a3c16cea3ca" +dependencies = [ + "crossbeam-epoch", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-epoch" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5064ebdbf05ce3cb95e45c8b086f72263f4166b29b97f6baff7ef7fe047b55ac" +dependencies = [ + "autocfg", + "cfg-if", + "crossbeam-utils", + "lazy_static", + "memoffset", + "scopeguard", +] + +[[package]] +name = "crossbeam-queue" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c695eeca1e7173472a32221542ae469b3e9aac3a4fc81f7696bcad82029493db" +dependencies = [ + "cfg-if", + "crossbeam-utils", +] + +[[package]] +name = "crossbeam-utils" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce446db02cdc3165b94ae73111e570793400d0794e46125cc4056c81cbb039f4" +dependencies = [ + "autocfg", + "cfg-if", + "lazy_static", +] + +[[package]] +name = "either" +version = "1.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bb1f6b1ce1c140482ea30ddd3335fc0024ac7ee112895426e0a629a6c20adfe3" + +[[package]] +name = "flate2" +version = "1.0.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bd6d6f4752952feb71363cffc9ebac9411b75b87c6ab6058c40c8900cf43c0f" +dependencies = [ + "cfg-if", + "crc32fast", + "libc", + "miniz_oxide", +] + +[[package]] +name = "fnv" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2fad85553e09a6f881f739c29f0b00b0f01357c743266d478b68951ce23285f3" + +[[package]] +name = "hermit-abi" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f629dc602392d3ec14bfc8a09b5e644d7ffd725102b48b81e59f90f2633621d7" +dependencies = [ + "libc", +] + +[[package]] +name = "inflate" +version = "0.4.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1cdb29978cc5797bd8dcc8e5bf7de604891df2a8dc576973d71a281e916db2ff" +dependencies = [ + "adler32", +] + +[[package]] +name = "lazy_static" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" + +[[package]] +name = "libc" +version = "0.2.66" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d515b1f41455adea1313a4a2ac8a8a477634fbae63cc6100e3aebb207ce61558" + +[[package]] +name = "memmap" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6585fd95e7bb50d6cc31e20d4cf9afb4e2ba16c5846fc76793f11218da9c475b" +dependencies = [ + "libc", + "winapi", +] + +[[package]] +name = "memoffset" +version = "0.5.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75189eb85871ea5c2e2c15abbdd541185f63b408415e5051f5cac122d8c774b9" +dependencies = [ + "rustc_version", +] + +[[package]] +name = "miniz_oxide" +version = "0.3.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f3f74f726ae935c3f514300cc6773a0c9492abc5e972d42ba0c0ebb88757625" +dependencies = [ + "adler32", +] + +[[package]] +name = "num_cpus" +version = "1.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76dac5ed2a876980778b8b85f75a71b6cbf0db0b1232ee12f826bccb00d09d72" +dependencies = [ + "hermit-abi", + "libc", +] + +[[package]] +name = "osmpbf" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52f8eed4edc2f1c5e720ff5cea4d872c88cfb81115096c3999f4a75e2d3c272a" +dependencies = [ + "byteorder", + "flate2", + "inflate", + "memmap", + "protobuf", + "rayon", +] + +[[package]] +name = "protobuf" +version = "2.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40361836defdd5871ff7e84096c6f6444af7fc157f8ef1789f54f147687caa20" + +[[package]] +name = "rayon" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db6ce3297f9c85e16621bb8cca38a06779ffc31bb8184e1be4bed2be4678a098" +dependencies = [ + "crossbeam-deque", + "either", + "rayon-core", +] + +[[package]] +name = "rayon-core" +version = "1.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "08a89b46efaf957e52b18062fb2f4660f8b8a4dde1807ca002690868ef2c85a9" +dependencies = [ + "crossbeam-deque", + "crossbeam-queue", + "crossbeam-utils", + "lazy_static", + "num_cpus", +] + +[[package]] +name = "rustc_version" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "138e3e0acb6c9fb258b19b67cb8abd63c00679d2851805ea151465464fe9030a" +dependencies = [ + "semver", +] + +[[package]] +name = "scopeguard" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b42e15e59b18a828bbf5c58ea01debb36b9b096346de35d941dcb89009f24a0d" + +[[package]] +name = "semver" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" +dependencies = [ + "semver-parser", +] + +[[package]] +name = "semver-parser" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" + +[[package]] +name = "winapi" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8093091eeb260906a183e6ae1abdba2ef5ef2257a21801128899c3fc699229c6" +dependencies = [ + "winapi-i686-pc-windows-gnu", + "winapi-x86_64-pc-windows-gnu", +] + +[[package]] +name = "winapi-i686-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" + +[[package]] +name = "winapi-x86_64-pc-windows-gnu" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" diff --git a/Cargo.toml b/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "OSM_to_Euroscope" +version = "0.1.0" +authors = ["Marcel <mtrnord1@gmail.com>"] +edition = "2018" + +# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html + +[dependencies] +osmpbf = "0.1" +fnv = "1.0.6" diff --git a/OSM_to_Euroscope.iml b/OSM_to_Euroscope.iml @@ -0,0 +1,15 @@ +<?xml version="1.0" encoding="UTF-8"?> +<module type="RUST_MODULE" version="4"> + <component name="NewModuleRootManager" inherit-compiler-output="true"> + <exclude-output /> + <content url="file://$MODULE_DIR$"> + <sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/examples" isTestSource="false" /> + <sourceFolder url="file://$MODULE_DIR$/tests" isTestSource="true" /> + <sourceFolder url="file://$MODULE_DIR$/benches" isTestSource="true" /> + <excludeFolder url="file://$MODULE_DIR$/target" /> + </content> + <orderEntry type="inheritedJdk" /> + <orderEntry type="sourceFolder" forTests="false" /> + </component> +</module> +\ No newline at end of file diff --git a/src/main.rs b/src/main.rs @@ -0,0 +1,169 @@ +//#![feature(test)] +#![allow(non_snake_case)] + +use std::fs; +use std::fs::File; +use std::io::Write; +use std::path::Path; + +use fnv::FnvHashMap; +use osmpbf::*; + +#[derive(Copy, Clone, Debug, PartialOrd, PartialEq)] +struct LatLon { + lat: f64, + lon: f64, +} + +type Coords = Vec<LatLon>; +type Refs = Vec<i64>; + +fn main() { + generate_ese_ground_taxiway("./data/EDDH_cleaned.osm.pbf", "EDDH") +} + +fn generate_ese_ground_taxiway<P: AsRef<Path> + Copy>(source_path: P, airport_name: &str) { + let out_path_string = format!("./out/{}.ese", airport_name); + let out_path = Path::new(&out_path_string); + if !out_path.parent().unwrap().exists() { + fs::create_dir_all(out_path.parent().unwrap()).expect("unable to generate dir"); + } + let out_file = &mut File::create(out_path).expect("unable to open file"); + + out_file + .write_all(b"[GROUND]\n") + .expect("failed to write file"); + let taxiways = get_taxiways(source_path); + + let coords = get_taxiways_coords(source_path, taxiways); + write_taxiways(out_file, airport_name, coords); + + out_file.sync_all().expect("unable to sync to file"); +} + +fn to_2_digits(n: i32) -> i32 { + let n = n as f32; + let digit_offset = std::cmp::max((n.abs().log10() - 2.0).ceil() as i32, 0); + let rounded = n / 10f32.powi(digit_offset); + rounded as i32 +} + +fn format_coordinate(coordinate_raw: f64) -> String { + let coordinate = coordinate_raw; + let degree = coordinate.trunc() as i64; + let minute = ((coordinate - degree as f64).abs() * 60.0).trunc() as i64; + let seconds = 3600.0 * (coordinate - degree as f64).abs() - 60.0 * (minute as f64); + let decimal_part_string = format!("{:.2}", seconds - (seconds.trunc() as f64) as f64); + let decimal_part_split = decimal_part_string.split('.').collect::<Vec<&str>>(); + let decimal_part = decimal_part_split.last().unwrap(); + + format!( + "{:03}.{:02}.{:02}.{:02}", + degree, + minute, + to_2_digits(seconds as i32), + decimal_part + ) +} + +fn write_taxiways(out_file: &mut File, airport_name: &str, taxiways: FnvHashMap<String, Coords>) { + taxiways.iter().for_each(|(name, coordinates)| { + let type_string = format!("TAXI:{} {}:20:1\n", airport_name, name); + out_file + .write_all(type_string.as_bytes()) + .expect("failed to write file"); + + let local_coords: Vec<&LatLon> = coordinates.iter().clone().collect(); + local_coords.iter().for_each(|latlon| { + let lat_str = format_coordinate(latlon.lat); + + let lon_str = format_coordinate(latlon.lon); + + let coordinate_string: String; + + coordinate_string = format!("COORD:N{}:E{}\n", lat_str, lon_str); + out_file + .write_all(coordinate_string.as_bytes()) + .expect("failed to write file"); + }); + }); +} + +struct Node { + id: i64, + lat: f64, + lon: f64, +} + +fn get_taxiways_coords<P: AsRef<Path> + Copy>( + path: P, + refs: FnvHashMap<String, Refs>, +) -> FnvHashMap<String, Coords> { + let mut taxiways: FnvHashMap<String, Coords> = FnvHashMap::default(); + let mut nodes: Vec<Node> = Vec::new(); + + let reader = ElementReader::from_path(path).unwrap(); + reader + .for_each(|element| { + if let Element::DenseNode(node) = element { + let new_node = Node { + id: node.id, + lat: node.lat(), + lon: node.lon(), + }; + nodes.push(new_node); + } + }) + .unwrap(); + + for (key, val) in refs.iter() { + val.iter().for_each(|refval| { + if let Some(node) = nodes.iter().find(|x| x.id == *refval) { + let lat_lon = LatLon { + lat: node.lat, + lon: node.lon, + }; + + if taxiways.contains_key(key.as_str()) { + (*(taxiways.get_mut(key.as_str()).unwrap())).push(lat_lon); + } else { + let mut coords: Coords = Vec::new(); + coords.push(lat_lon); + taxiways.insert(key.clone(), coords); + } + } + }) + } + + taxiways +} + +fn get_taxiways<P: AsRef<Path> + Copy>(path: P) -> FnvHashMap<String, Refs> { + let mut taxiways: FnvHashMap<String, Refs> = FnvHashMap::default(); + + let reader = ElementReader::from_path(path).unwrap(); + reader + .for_each(|element| { + if let Element::Way(way) = element { + if way + .tags() + .any(|(key, value)| key == "aeroway" && value == "taxiway") + { + if let Some((_, value)) = way.tags().find(|(key, _)| *key == "ref") { + let mut new_vals = way.refs().collect::<Vec<i64>>(); + + if taxiways.contains_key(value) { + taxiways.get_mut(value).unwrap().append(&mut new_vals); + } else { + taxiways.insert(value.to_string(), new_vals); + } + } + } + } + }) + .unwrap(); + taxiways +} + +#[cfg(test)] +mod tests; diff --git a/src/main_lahm.rs b/src/main_lahm.rs @@ -0,0 +1,46 @@ +use std::fs::File; + +use osm_xml::{Reference, Tag, OSM, Way}; + +fn main() { + let f = File::open("./data/EDDH.osm").unwrap(); + let doc = OSM::parse(f).unwrap(); + + let way_info = way_reference_statistics(&doc); + + println!( + "Way count {}", + doc.ways + .values() + .filter(|way| { + way.tags.contains(&Tag { + key: "aeroway".to_string(), + val: "taxiway".to_string(), + }) + }) + .collect::<Vec<&Way>>() + .len() + ); + println!( + "Way reference count: {}, invalid references: {}", + way_info.0, way_info.1 + ); +} + +fn way_reference_statistics(doc: &OSM) -> (usize, usize) { + doc.ways + .values() + .filter(|way| { + way.tags.contains(&Tag { + key: "aeroway".to_string(), + val: "taxiway".to_string(), + }) + }) + .flat_map(|way| way.nodes.iter()) + .fold((0, 0), |acc, node| match doc.resolve_reference(&node) { + Reference::Node(_) => (acc.0 + 1, acc.1), + Reference::Unresolved | Reference::Way(_) | Reference::Relation(_) => { + (acc.0, acc.1 + 1) + } + }) +} diff --git a/src/tests.rs b/src/tests.rs @@ -0,0 +1,20 @@ +extern crate test; + +use test::Bencher; + +use super::*; + +#[bench] +fn bench_eddh(b: &mut Bencher) { + b.iter(|| generate_ese_ground_taxiway("./data/EDDH.osm.pbf", "EDDH1")); +} + +#[bench] +fn bench_eddh_cleaned(b: &mut Bencher) { + b.iter(|| generate_ese_ground_taxiway("./data/EDDH_cleaned.osm.pbf", "EDDH2")); +} + +#[bench] +fn bench_hamburg(b: &mut Bencher) { + b.iter(|| generate_ese_ground_taxiway("./data/hamburg-latest.osm.pbf", "EDDH3")); +}