use std::fmt::{self, Debug, Formatter};
use std::mem::MaybeUninit;
use std::ops::Deref;
use std::os::raw::c_int;
use std::str::FromStr;
use geo_types::{Line, LineString, MultiLineString};
#[cfg(feature = "use-serde")]
use serde::{Deserialize, Serialize};
use h3ron_h3_sys::H3Index;
use crate::index::{index_from_str, Index};
use crate::iter::CellBoundaryIter;
use crate::to_geo::{ToLine, ToLineString, ToMultiLineString};
use crate::{Error, FromH3Index, H3Cell, ToCoordinate};
#[derive(PartialOrd, PartialEq, Clone, Hash, Eq, Ord, Copy)]
#[cfg_attr(feature = "use-serde", derive(Serialize, Deserialize))]
#[repr(transparent)]
pub struct H3DirectedEdge(H3Index);
impl Debug for H3DirectedEdge {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "H3DirectedEdge({})", self.to_string())
}
}
impl TryFrom<u64> for H3DirectedEdge {
type Error = Error;
fn try_from(h3index: H3Index) -> Result<Self, Self::Error> {
let index = Self::new(h3index);
index.validate()?;
Ok(index)
}
}
impl H3DirectedEdge {
pub fn from_cells(origin_cell: H3Cell, destination_cell: H3Cell) -> Result<Self, Error> {
origin_cell.directed_edge_to(destination_cell)
}
pub fn is_edge_valid(&self) -> bool {
self.validate().is_ok()
}
pub fn edge_length_avg_km(resolution: u8) -> Result<f64, Error> {
let mut edge_length: f64 = 0.0;
Error::check_returncode(unsafe {
h3ron_h3_sys::getHexagonEdgeLengthAvgKm(c_int::from(resolution), &mut edge_length)
})
.map(|_| edge_length)
}
pub fn edge_length_avg_m(resolution: u8) -> Result<f64, Error> {
let mut edge_length: f64 = 0.0;
Error::check_returncode(unsafe {
h3ron_h3_sys::getHexagonEdgeLengthAvgM(c_int::from(resolution), &mut edge_length)
})
.map(|_| edge_length)
}
pub fn cell_centroid_distance_avg_m_at_resolution(resolution: u8) -> Result<f64, Error> {
Self::edge_length_avg_m(resolution).map(cell_centroid_distance_m_by_edge_length)
}
pub fn cell_centroid_distance_m(&self) -> Result<f64, Error> {
self.length_m().map(cell_centroid_distance_m_by_edge_length)
}
pub fn destination_cell(&self) -> Result<H3Cell, Error> {
let mut cell_h3index: H3Index = 0;
Error::check_returncode(unsafe {
h3ron_h3_sys::getDirectedEdgeDestination(self.h3index(), &mut cell_h3index)
})
.map(|_| H3Cell::new(cell_h3index))
}
pub fn origin_cell(&self) -> Result<H3Cell, Error> {
let mut cell_h3index: H3Index = 0;
Error::check_returncode(unsafe {
h3ron_h3_sys::getDirectedEdgeOrigin(self.h3index(), &mut cell_h3index)
})
.map(|_| H3Cell::new(cell_h3index))
}
pub fn cells(&self) -> Result<H3EdgeCells, Error> {
let mut out: [H3Index; 2] = [0, 0];
Error::check_returncode(unsafe {
h3ron_h3_sys::directedEdgeToCells(self.h3index(), out.as_mut_ptr())
})?;
let res = H3EdgeCells {
origin: H3Cell::new(out[0]),
destination: H3Cell::new(out[1]),
};
Ok(res)
}
pub fn reversed(&self) -> Result<Self, Error> {
let edge_cells = self.cells()?;
edge_cells.destination.directed_edge_to(edge_cells.origin)
}
pub fn boundary_linestring(&self) -> Result<LineString<f64>, Error> {
let cb = unsafe {
let mut mu = MaybeUninit::<h3ron_h3_sys::CellBoundary>::uninit();
Error::check_returncode(h3ron_h3_sys::directedEdgeToBoundary(
self.0,
mu.as_mut_ptr(),
))?;
mu.assume_init()
};
Ok(CellBoundaryIter::new(&cb, false).collect())
}
pub fn length_m(&self) -> Result<f64, Error> {
let mut length: f64 = 0.0;
Error::check_returncode(unsafe { h3ron_h3_sys::edgeLengthM(self.h3index(), &mut length) })
.map(|_| length)
}
pub fn length_km(&self) -> Result<f64, Error> {
let mut length: f64 = 0.0;
Error::check_returncode(unsafe { h3ron_h3_sys::edgeLengthKm(self.h3index(), &mut length) })
.map(|_| length)
}
pub fn length_rads(&self) -> Result<f64, Error> {
let mut length: f64 = 0.0;
Error::check_returncode(unsafe {
h3ron_h3_sys::edgeLengthRads(self.h3index(), &mut length)
})
.map(|_| length)
}
}
impl FromH3Index for H3DirectedEdge {
fn from_h3index(h3index: H3Index) -> Self {
Self::new(h3index)
}
}
impl Index for H3DirectedEdge {
fn h3index(&self) -> H3Index {
self.0
}
fn new(h3index: H3Index) -> Self {
Self(h3index)
}
fn validate(&self) -> Result<(), Error> {
if unsafe { h3ron_h3_sys::isValidDirectedEdge(self.h3index()) == 0 } {
Err(Error::DirectedEdgeInvalid)
} else {
Ok(())
}
}
}
impl ToString for H3DirectedEdge {
fn to_string(&self) -> String {
format!("{:x}", self.0)
}
}
impl FromStr for H3DirectedEdge {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
index_from_str(s)
}
}
impl ToLine for H3DirectedEdge {
type Error = Error;
fn to_line(&self) -> Result<Line<f64>, Self::Error> {
let edge_cells = self.cells()?;
Ok(Line::new(
edge_cells.origin.to_coordinate()?,
edge_cells.destination.to_coordinate()?,
))
}
}
impl ToLineString for H3DirectedEdge {
type Error = Error;
fn to_linestring(&self) -> Result<LineString<f64>, Self::Error> {
Ok(self.to_line()?.into())
}
}
impl ToMultiLineString for &[H3DirectedEdge] {
type Error = Error;
fn to_multilinestring(&self) -> Result<MultiLineString<f64>, Self::Error> {
let cell_tuples = self
.iter()
.map(|edge| match (edge.origin_cell(), edge.destination_cell()) {
(Ok(origin_cell), Ok(destination_cell)) => Ok((origin_cell, destination_cell)),
(Err(e), _) | (_, Err(e)) => Err(e),
})
.collect::<Result<Vec<_>, _>>()?;
celltuples_to_multlinestring(cell_tuples)
}
}
impl Deref for H3DirectedEdge {
type Target = H3Index;
fn deref(&self) -> &Self::Target {
&self.0
}
}
const F64_SQRT_3: f64 = 1.7320508075688772_f64;
#[inline(always)]
fn cell_centroid_distance_m_by_edge_length(edge_length: f64) -> f64 {
edge_length * F64_SQRT_3
}
fn celltuples_to_multlinestring<I>(iter: I) -> Result<MultiLineString<f64>, Error>
where
I: IntoIterator<Item = (H3Cell, H3Cell)>,
{
let mut linestrings = vec![];
let mut last_destination_cell: Option<H3Cell> = None;
let mut coordinates = vec![];
for (origin_cell, destination_cell) in iter {
if coordinates.is_empty() {
coordinates.push(origin_cell.to_coordinate()?);
} else if last_destination_cell != Some(origin_cell) {
linestrings.push(LineString::from(std::mem::take(&mut coordinates)));
coordinates.push(origin_cell.to_coordinate()?);
}
coordinates.push(destination_cell.to_coordinate()?);
last_destination_cell = Some(destination_cell);
}
if !coordinates.is_empty() {
linestrings.push(LineString::from(coordinates));
}
Ok(MultiLineString(linestrings))
}
impl ToMultiLineString for Vec<H3DirectedEdge> {
type Error = Error;
fn to_multilinestring(&self) -> Result<MultiLineString<f64>, Self::Error> {
self.as_slice().to_multilinestring()
}
}
pub struct H3EdgeCells {
pub origin: H3Cell,
pub destination: H3Cell,
}
#[cfg(test)]
mod tests {
use super::*;
#[should_panic(expected = "DirectedEdgeInvalid")]
#[test]
fn checks_both_validity() {
let edge = H3DirectedEdge::new(0x149283080ddbffff);
assert!(edge.validate().is_ok());
let edge = H3DirectedEdge::new(0x89283080ddbffff_u64);
edge.validate().unwrap();
}
#[test]
fn debug_hexadecimal() {
let edge = H3DirectedEdge::new(0x149283080ddbffff);
assert_eq!(
format!("{:?}", edge),
"H3DirectedEdge(149283080ddbffff)".to_string()
);
}
#[test]
fn reversed() {
let edge = H3DirectedEdge::new(0x149283080ddbffff);
let rev_edge = edge.reversed().unwrap();
assert_ne!(edge, rev_edge);
assert_eq!(
edge.origin_cell().unwrap(),
rev_edge.destination_cell().unwrap()
);
assert_eq!(
edge.destination_cell().unwrap(),
rev_edge.origin_cell().unwrap()
);
}
#[test]
fn boundary_linestring() {
let edge = H3DirectedEdge::new(0x149283080ddbffff);
let boundary_ls = edge.boundary_linestring().unwrap();
assert_eq!(boundary_ls.0.len(), 2);
dbg!(&boundary_ls);
let ls = edge.to_linestring().unwrap();
assert_eq!(ls.0.len(), 2);
dbg!(&ls);
assert_ne!(ls, boundary_ls);
}
#[test]
fn test_cell_centroid_distance_m() {
let edge = H3DirectedEdge::new(0x149283080ddbffff);
assert!(edge.length_m().unwrap() < edge.cell_centroid_distance_m().unwrap());
assert!((2.0 * edge.length_m().unwrap()) > edge.cell_centroid_distance_m().unwrap());
}
}