heliwatch/src/location.rs

144 lines
4.9 KiB
Rust
Raw Normal View History

2021-10-28 03:36:18 +02:00
use std::collections::HashMap;
use std::fs::File;
use std::sync::Arc;
use geo::prelude::{Area, Contains};
type Polygon = geo::Polygon<f64>;
struct Location {
name: Arc<String>,
polys: Vec<Arc<Polygon>>,
area: f64,
}
impl Location {
pub fn new(name: &str, polys: Vec<Arc<Polygon>>) -> Self {
let area = polys.iter()
.map(|poly| poly.unsigned_area())
.sum();
Location {
name: Arc::new(name.to_owned()),
polys,
area,
}
}
pub fn contains(&self, coord: &geo::Coordinate<f64>) -> bool {
self.polys.iter().any(|poly| poly.contains(coord))
}
}
pub struct Locations {
locations: Vec<Location>,
}
impl Locations {
pub fn load(file: &str) -> Self {
println!("Loading {}...", file);
let json: serde_json::Value = serde_json::from_reader(File::open(file).unwrap())
.unwrap();
let obj = json.as_object().expect("json obj");
let els = obj.get("elements").and_then(|v| v.as_array()).expect("els");
println!("{} elements", els.len());
let mut nodes = HashMap::new();
for el in els {
let el = el.as_object().expect("el");
match el.get("type").and_then(|v| v.as_str()) {
Some("node") => {
let id = el.get("id").and_then(|v| v.as_u64()).expect("id");
let lon = el.get("lon").expect("lon")
.as_f64().expect("lon f64");
let lat = el.get("lat").expect("lat")
.as_f64().expect("lat f64");
let coord = geo::Coordinate { x: lon, y: lat };
nodes.insert(id, coord);
}
_ => {}
}
}
println!("{} nodes", nodes.len());
let mut locations = vec![];
let mut ways = HashMap::new();
for el in els {
let el = el.as_object().expect("el");
match el.get("type").and_then(|v| v.as_str()) {
Some("way") => {
let id = el.get("id").and_then(|v| v.as_u64()).expect("id");
let way_nodes = el.get("nodes").and_then(|v| v.as_array()).expect("nodes")
.iter()
.map(|way_node| {
let way_node = way_node.as_u64()
.expect("way_node");
nodes.get(&way_node).expect("way_node node")
})
.cloned()
.collect::<Vec<_>>();
let poly = Arc::new(Polygon::new(
geo::LineString(way_nodes),
vec![]
));
ways.insert(id, poly.clone());
if let Some(tags) = el.get("tags").and_then(|v| v.as_object()) {
if let Some(name) = tags.get("name").and_then(|v| v.as_str()) {
locations.push(Location::new(name, vec![poly]));
}
}
}
_ => {}
}
}
println!("{} ways", ways.len());
for el in els {
let el = el.as_object().expect("el");
match el.get("type").and_then(|v| v.as_str()) {
Some("relation") => {
let polys = el.get("members").and_then(|v| v.as_array()).expect("members")
.iter()
.filter_map(|member| {
let member = member.as_object().unwrap();
let member_type = member.get("type").and_then(|v| v.as_str()).unwrap();
if member_type == "way" {
let member_ref = member.get("ref").and_then(|v| v.as_u64()).unwrap();
let way = ways.get(&member_ref).expect("member way");
Some(way.clone())
} else {
None
}
})
.collect::<Vec<_>>();
if let Some(tags) = el.get("tags").and_then(|v| v.as_object()) {
if let Some(name) = tags.get("name").and_then(|v| v.as_str()) {
locations.push(Location::new(name, polys));
}
}
}
_ => {}
}
}
locations.sort_by(|a, b| a.area.partial_cmp(&b.area).unwrap());
println!("{} locations", locations.len());
Locations {
locations,
}
}
pub fn find(&self, coord: &geo::Coordinate<f64>) -> Option<Arc<String>> {
for l in &self.locations {
if l.contains(coord) {
return Some(l.name.clone());
}
}
None
}
}