use std::collections::HashMap; use std::io::BufReader; use std::fs::File; use std::sync::Arc; use geo::prelude::{Area, Contains}; type Polygon = geo::Polygon; struct Location { name: Arc, poly: Arc, area: f64, } impl Location { pub fn new(name: &str, poly: Arc) -> Self { let area = poly.unsigned_area(); Location { name: Arc::new(name.to_owned()), poly, area, } } pub fn contains(&self, coord: &geo::Coordinate) -> bool { self.poly.contains(coord) } } pub struct Locations { locations: Vec, } impl Locations { pub fn load(file: &str) -> Self { println!("Loading {}...", file); let json: serde_json::Value = serde_json::from_reader(BufReader::new(File::open(file).unwrap())) .unwrap(); println!("parsed JSON"); 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| way_node.as_u64().expect("way_node")) .collect::>(); ways.insert(id, way_nodes.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()) { let poly = Arc::new(Polygon::new( geo::LineString(way_nodes.iter() .map(|node| nodes.get(node).expect("node")) .cloned() .collect()), vec![] )); locations.push(Location::new(name, 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") => { if let Some(name) = el.get("tags") .and_then(|v| v.as_object()) .and_then(|tags| tags.get("name")) .and_then(|v| v.as_str()) { let mut outers = 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(); let member_role = member.get("role").and_then(|v| v.as_str()).unwrap(); if member_type == "way" && member_role == "outer" { 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::>(); let mut rel_nodes = vec![]; while outers.len() > 0 { if rel_nodes.len() == 0 { rel_nodes = outers.pop().unwrap(); } else if let Some(next) = outers.iter().position(|outer| outer[0] == rel_nodes[rel_nodes.len() - 1]) { rel_nodes.append(&mut outers.remove(next)); } else if let Some(next) = outers.iter().position(|outer| outer[outer.len() - 1] == rel_nodes[0]) { let mut new = outers.remove(next); new.append(&mut rel_nodes); rel_nodes = new; } else if let Some(next) = outers.iter().position(|outer| outer[outer.len() - 1] == rel_nodes[rel_nodes.len() - 1]) { let mut new = outers.remove(next); new.reverse(); rel_nodes.append(&mut new); } else { println!("inconclusive polygon for relation {}", el.get("id").unwrap().as_u64().unwrap()); println!("rel_nodes: {:?}", rel_nodes); println!("remain: {:?}", outers); rel_nodes = vec![]; break; } if rel_nodes[0] == rel_nodes[rel_nodes.len() - 1] { let poly = Arc::new(Polygon::new( geo::LineString(rel_nodes.drain(..) .map(|node| nodes.get(&node).expect("node")) .cloned() .collect()), vec![] )); locations.push(Location::new(name, poly)); } } if rel_nodes.len() > 0 { println!("unclosed polygon for relation {}", el.get("id").unwrap().as_u64().unwrap()); } } } _ => {} } } 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) -> Option> { for l in &self.locations { if l.contains(coord) { return Some(l.name.clone()); } } None } }