use std::cmp::Ordering;
use geo_types::LineString;
use serde::{Deserialize, Serialize};
use h3ron::to_geo::{ToLineString, ToMultiLineString};
use h3ron::{H3Cell, H3DirectedEdge, Index};
use crate::error::Error;
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub enum DirectedEdgePath {
OriginIsDestination(H3Cell),
DirectedEdgeSequence(Vec<H3DirectedEdge>),
}
impl DirectedEdgePath {
pub fn is_empty(&self) -> bool {
match self {
Self::OriginIsDestination(_) => true,
Self::DirectedEdgeSequence(edges) => edges.is_empty(),
}
}
pub fn len(&self) -> usize {
match self {
Self::OriginIsDestination(_) => 0,
Self::DirectedEdgeSequence(edges) => edges.len(),
}
}
pub fn origin_cell(&self) -> Result<H3Cell, Error> {
match self {
Self::OriginIsDestination(cell) => Ok(*cell),
Self::DirectedEdgeSequence(edges) => {
if let Some(edge) = edges.first() {
Ok(edge.origin_cell()?)
} else {
Err(Error::EmptyPath)
}
}
}
}
pub fn destination_cell(&self) -> Result<H3Cell, Error> {
match self {
Self::OriginIsDestination(cell) => Ok(*cell),
Self::DirectedEdgeSequence(edges) => {
if let Some(edge) = edges.last() {
Ok(edge.destination_cell()?)
} else {
Err(Error::EmptyPath)
}
}
}
}
pub fn to_linestring(&self) -> Result<LineString<f64>, Error> {
match self {
Self::OriginIsDestination(_) => Err(Error::InsufficientNumberOfEdges),
Self::DirectedEdgeSequence(edges) => match edges.len() {
0 => Err(Error::InsufficientNumberOfEdges),
1 => Ok(edges[0].to_linestring()?),
_ => {
let mut multilinesstring = edges.to_multilinestring()?;
match multilinesstring.0.len() {
0 => Err(Error::InsufficientNumberOfEdges),
1 => Ok(multilinesstring.0.remove(0)),
_ => Err(Error::SegmentedPath),
}
}
},
}
}
pub fn edges(&self) -> &[H3DirectedEdge] {
match self {
Self::DirectedEdgeSequence(edges) => edges.as_slice(),
Self::OriginIsDestination(_) => &[],
}
}
pub fn cells(&self) -> Result<Vec<H3Cell>, Error> {
match self {
Self::OriginIsDestination(cell) => Ok(vec![*cell]),
Self::DirectedEdgeSequence(edges) => {
let mut cells = Vec::with_capacity(edges.len() * 2);
for edge in edges.iter() {
cells.push(edge.origin_cell()?);
cells.push(edge.destination_cell()?);
}
cells.dedup();
cells.shrink_to_fit();
Ok(cells)
}
}
}
pub fn length_m(&self) -> Result<f64, Error> {
match self {
Self::OriginIsDestination(_) => Ok(0.0),
Self::DirectedEdgeSequence(edges) => {
let mut length_m = 0.0;
for edge in edges {
length_m += edge.length_m()?;
}
Ok(length_m)
}
}
}
}
#[derive(Debug, Clone, Eq, PartialEq, Serialize, Deserialize)]
pub struct Path<W> {
pub origin_cell: H3Cell,
pub destination_cell: H3Cell,
pub cost: W,
pub directed_edge_path: DirectedEdgePath,
}
impl<W> Path<W> {
#[inline]
pub fn is_empty(&self) -> bool {
self.directed_edge_path.is_empty()
}
#[inline]
pub fn len(&self) -> usize {
self.directed_edge_path.len()
}
}
impl<W> TryFrom<(DirectedEdgePath, W)> for Path<W> {
type Error = Error;
fn try_from((path_directed_edges, cost): (DirectedEdgePath, W)) -> Result<Self, Self::Error> {
let origin_cell = path_directed_edges.origin_cell()?;
let destination_cell = path_directed_edges.destination_cell()?;
Ok(Self {
origin_cell,
destination_cell,
cost,
directed_edge_path: path_directed_edges,
})
}
}
impl PartialOrd<Self> for DirectedEdgePath {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for DirectedEdgePath {
fn cmp(&self, other: &Self) -> Ordering {
let cmp_origin = index_or_zero(self.origin_cell()).cmp(&index_or_zero(other.origin_cell()));
if cmp_origin == Ordering::Equal {
index_or_zero(self.destination_cell()).cmp(&index_or_zero(other.destination_cell()))
} else {
cmp_origin
}
}
}
impl<W> Ord for Path<W>
where
W: Ord,
{
fn cmp(&self, other: &Self) -> Ordering {
let cmp_cost = self.cost.cmp(&other.cost);
if cmp_cost == Ordering::Equal {
self.directed_edge_path.cmp(&other.directed_edge_path)
} else {
cmp_cost
}
}
}
impl<W> PartialOrd for Path<W>
where
W: Ord,
{
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
#[inline]
fn index_or_zero(cell: Result<H3Cell, Error>) -> u64 {
cell.map(|c| c.h3index()).unwrap_or(0)
}
#[cfg(test)]
mod tests {
use h3ron::{H3DirectedEdge, Index};
use super::{DirectedEdgePath, Path};
#[test]
fn pathdirectededges_deterministic_ordering() {
let r1 =
DirectedEdgePath::DirectedEdgeSequence(vec![H3DirectedEdge::new(0x1176b49474ffffff)]);
let r2 =
DirectedEdgePath::DirectedEdgeSequence(vec![H3DirectedEdge::new(0x1476b49474ffffff)]);
let mut paths = vec![r2.clone(), r1.clone()];
paths.sort_unstable();
assert_eq!(paths[0], r1);
assert_eq!(paths[1], r2);
}
#[test]
fn paths_deterministic_ordering() {
let r1: Path<_> = (
DirectedEdgePath::DirectedEdgeSequence(vec![H3DirectedEdge::new(0x1176b49474ffffff)]),
1,
)
.try_into()
.unwrap();
let r2: Path<_> = (
DirectedEdgePath::DirectedEdgeSequence(vec![H3DirectedEdge::new(0x1476b49474ffffff)]),
3,
)
.try_into()
.unwrap();
let r3: Path<_> = (
DirectedEdgePath::DirectedEdgeSequence(vec![H3DirectedEdge::new(0x1476b4b2c2ffffff)]),
3,
)
.try_into()
.unwrap();
let mut paths = vec![r3.clone(), r1.clone(), r2.clone()];
paths.sort_unstable();
assert_eq!(paths[0], r1);
assert_eq!(paths[1], r2);
assert_eq!(paths[2], r3);
}
}