mod element;
pub mod geo;
pub mod osm_io;
use crate::geo::{Boundary, Coordinate};
pub use element::*;
use std::cmp::max;
use std::collections::HashMap;
pub struct OsmBuilder {
osm: Osm,
}
#[derive(Debug)]
pub struct Osm {
pub boundary: Option<Boundary>,
pub nodes: Vec<Node>,
pub ways: Vec<Way>,
pub relations: Vec<Relation>,
max_id: i64,
node_id_index: HashMap<Coordinate, i64>,
}
impl OsmBuilder {
pub fn build(self) -> Osm {
self.osm
}
pub fn add_polygon(&mut self, parts: &[Vec<Coordinate>], tags: &[(String, String)]) {
if parts.len() == 1 {
self.add_polyline(&parts[0], tags);
} else {
self.add_multipolygon(parts, tags);
}
}
pub fn add_polyline(&mut self, coordinates: &[Coordinate], tags: &[(String, String)]) -> i64 {
let refs = self.add_nodes(coordinates);
let id = self.next_id();
let tags = tags.iter().map(|t| t.into()).collect();
let meta = Meta {
tags,
..Default::default()
};
self.osm.add_way(Way { id, refs, meta });
id
}
fn add_multipolygon(&mut self, parts: &[Vec<Coordinate>], tags: &[(String, String)]) {
let mut polygon_ids = Vec::new();
for part in parts {
polygon_ids.push(self.add_polyline(part, &[]));
}
let mut tags: Vec<Tag> = tags.iter().map(|t| t.into()).collect();
tags.push(("type", "multipolygon").into());
let (outer, inner) = polygon_ids.split_first().unwrap();
self.add_polygon_relations(*outer, inner, tags);
}
fn add_polygon_relations(&mut self, outer: i64, inner: &[i64], tags: Vec<Tag>) {
let mut members = Vec::new();
for rel_ref in inner {
members.push(RelationMember::Way(*rel_ref, "inner".to_owned()));
}
members.push(RelationMember::Way(outer, "outer".to_owned()));
let id = self.next_id();
let meta = Meta {
tags,
..Default::default()
};
self.osm.add_relation(Relation { id, members, meta });
}
fn add_nodes(&mut self, coordinates: &[Coordinate]) -> Vec<i64> {
let mut refs = Vec::new();
for c in coordinates {
refs.push(self.add_node(c.clone(), vec![]));
}
refs
}
fn add_node(&mut self, coordinate: Coordinate, tags: Vec<Tag>) -> i64 {
if let Some(id) = self.osm.find_node_id(coordinate) {
return id;
}
let id = self.osm.max_id + 1;
let meta = Meta {
tags,
..Default::default()
};
self.osm.add_node(Node {
id,
coordinate,
meta,
});
id
}
fn next_id(&mut self) -> i64 {
self.osm.max_id += 1;
self.osm.max_id
}
}
impl Default for OsmBuilder {
fn default() -> Self {
OsmBuilder {
osm: Osm::default(),
}
}
}
impl Osm {
pub fn add_node(&mut self, node: Node) {
if let Some(boundary) = &mut self.boundary {
boundary.expand(node.coordinate);
}
self.max_id = max(self.max_id, node.id);
self.node_id_index.insert(node.coordinate.clone(), node.id);
self.nodes.push(node);
}
pub fn add_way(&mut self, way: Way) {
self.ways.push(way);
}
pub fn add_relation(&mut self, relation: Relation) {
self.relations.push(relation);
}
pub fn find_node_id(&mut self, coordinate: Coordinate) -> Option<i64> {
self.node_id_index.get(&coordinate).cloned()
}
}
impl Default for Osm {
fn default() -> Self {
Osm {
boundary: Some(Boundary::inverted()),
nodes: Vec::new(),
ways: Vec::new(),
relations: Vec::new(),
max_id: 0,
node_id_index: HashMap::new(),
}
}
}
#[cfg(test)]
mod tests {
use crate::geo::Boundary;
use crate::{Meta, Node, Osm};
#[test]
fn osm_add_node() {
let mut osm = Osm::default();
assert_eq!(osm.max_id, 0);
osm.add_node(Node {
id: 10,
coordinate: (65.0, 55.0).into(),
meta: Meta::default(),
});
let expected_boundary = Boundary {
min: (65.0, 55.0).into(),
max: (65.0, 55.0).into(),
freeze: false,
};
assert_eq!(osm.max_id, 10);
assert_eq!(osm.boundary, Some(expected_boundary));
}
}