use geo::{point, Destination, Distance, Euclidean, Geodesic, Point};
use rstar::{Envelope, AABB};
use std::ops::{Add, Mul};
use crate::codec::osm;
use crate::codec::osm::DenseNodes;
use super::common::OsmEntryId;
#[derive(Debug, Copy, Clone, PartialEq)]
pub struct Node {
pub id: OsmEntryId,
pub position: Point,
}
impl rstar::PointDistance for Node {
fn distance_2(
&self,
point: &<Self::Envelope as Envelope>::Point,
) -> <<Self::Envelope as Envelope>::Point as rstar::Point>::Scalar {
Euclidean::distance(self.position, *point).powi(2)
}
}
impl rstar::RTreeObject for Node {
type Envelope = AABB<Point>;
fn envelope(&self) -> Self::Envelope {
AABB::from_point(self.position)
}
}
impl Node {
pub const fn new(position: Point, id: OsmEntryId) -> Self {
Node { position, id }
}
pub fn id(&self) -> OsmEntryId {
self.id
}
pub fn bounding(&self, distance: f64) -> AABB<Point> {
let bottom_right = Geodesic::destination(self.position, 135.0, distance);
let top_left = Geodesic::destination(self.position, 315.0, distance);
AABB::from_corners(top_left, bottom_right)
}
#[inline]
pub fn from_dense(value: &DenseNodes, granularity: i32) -> impl Iterator<Item = Self> + '_ {
let scaling_factor: f64 = (granularity as f64) * 1e-9f64;
value
.lon
.iter()
.map(|v| *v as f64)
.zip(value.lat.iter().map(|v| *v as f64))
.zip(value.id.iter())
.fold(vec![], |mut curr: Vec<Self>, ((lng, lat), id)| {
let new_node = match &curr.last() {
Some(prior_node) => Node::new(
prior_node
.position
.add(point! { x: lng, y: lat }.mul(scaling_factor)),
prior_node.id + *id,
),
None => Node::new(
point! { x: lng, y: lat }.mul(scaling_factor),
OsmEntryId::from(*id),
),
};
curr.push(new_node);
curr
})
.into_iter()
}
}
impl From<&osm::Node> for Node {
fn from(value: &osm::Node) -> Self {
Node {
id: OsmEntryId::as_node(value.id),
position: point! { x: value.lon as f64, y: value.lat as f64 },
}
}
}