use crate::{io, Result};
use serde::{Deserialize, Serialize};
use std::collections::{
btree_map::Entry as BTreeMapEntry, hash_map::Entry as HashMapEntry, BTreeMap, HashMap,
};
use std::path::Path;
#[derive(Debug, Clone, PartialEq)]
pub struct Coord(pub geo::Coord<f64>);
impl Coord {
pub fn new(lon: f64, lat: f64) -> Coord {
Coord(geo::Coord { x: lon, y: lat })
}
pub fn lon(&self) -> f64 {
self.x
}
pub fn lat(&self) -> f64 {
self.y
}
pub fn is_default(&self) -> bool {
self.lat() == 0. && self.lon() == 0.
}
pub fn is_valid(&self) -> bool {
!self.is_default()
&& -90. <= self.lat()
&& self.lat() <= 90.
&& -180. <= self.lon()
&& self.lon() <= 180.
}
}
impl ::std::ops::Deref for Coord {
type Target = geo::Coord<f64>;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl Default for Coord {
fn default() -> Coord {
Coord(geo::Coord { x: 0., y: 0. })
}
}
impl From<geo::Point<f64>> for Coord {
fn from(point: geo::Point<f64>) -> Self {
Coord::new(point.x(), point.y())
}
}
#[derive(Debug, Clone, Ord, PartialOrd, Eq, PartialEq)]
pub struct Property {
pub key: String,
pub value: String,
}
#[derive(Debug, Clone)]
pub struct Poi {
pub id: String,
pub name: String,
pub coord: Coord,
pub poi_type_id: String,
pub properties: BTreeMap<String, String>,
pub visible: bool,
pub weight: u32,
}
#[derive(Debug, Clone, Deserialize, Serialize, Ord, PartialOrd, Eq, PartialEq)]
pub struct PoiType {
pub id: String,
pub name: String,
}
#[derive(Debug, Default)]
pub struct Model {
pub pois: BTreeMap<String, Poi>,
pub poi_types: HashMap<String, PoiType>,
}
impl Model {
pub fn try_from_path<P: AsRef<Path>>(path: P) -> Result<Model> {
io::load_model_from_path(path.as_ref())
}
pub fn save_to_path<P: AsRef<Path>>(&self, path: P) -> Result<()> {
io::write_model_to_path(self, path.as_ref())
}
pub fn try_merge(mut self, rhs: Model) -> Result<Model> {
let merged_pois = rhs
.pois
.into_iter()
.try_fold(self.pois, |mut acc, (k, v)| match acc.entry(k) {
BTreeMapEntry::Occupied(entry) => {
anyhow::bail!("POI with id {} already in the model", entry.key())
}
BTreeMapEntry::Vacant(entry) => {
entry.insert(v);
Ok(acc)
}
})?;
self.pois = merged_pois;
let merged_poi_types =
rhs.poi_types
.into_iter()
.try_fold(self.poi_types, |mut acc, (k, v)| match acc.entry(k) {
HashMapEntry::Occupied(entry) => {
if *entry.get() == v {
Ok(acc) } else {
anyhow::bail!("Trying to override POI Type with id {}", entry.key())
}
}
HashMapEntry::Vacant(entry) => {
entry.insert(v);
Ok(acc)
}
})?;
self.poi_types = merged_poi_types;
Ok(self)
}
}