use std::mem::MaybeUninit;
use std::ptr::addr_of_mut;
use geo_types::{Coord, LineString, Polygon};
use h3ron_h3_sys::{cellToBoundary, CellBoundary};
use crate::{Error, H3Cell, Index};
pub struct CellBoundaryBuilder {
cell_boundary: CellBoundary,
}
impl CellBoundaryBuilder {
pub fn new() -> Self {
let cell_boundary = unsafe {
let mut mu = MaybeUninit::<CellBoundary>::uninit();
(*mu.as_mut_ptr()).numVerts = 0;
mu.assume_init()
};
Self { cell_boundary }
}
pub fn iter_cell_boundary_vertices(
&mut self,
cell: &H3Cell,
close_ring: bool,
) -> Result<CellBoundaryIter, Error> {
Error::check_returncode(unsafe {
cellToBoundary(cell.h3index(), addr_of_mut!(self.cell_boundary))
})?;
Ok(CellBoundaryIter::new(&self.cell_boundary, close_ring))
}
}
impl Default for CellBoundaryBuilder {
fn default() -> Self {
Self::new()
}
}
pub struct CellBoundaryIter<'b> {
cell_boundary: &'b CellBoundary,
close_ring: bool,
pos: usize,
}
impl<'b> CellBoundaryIter<'b> {
pub const fn new(cell_boundary: &'b CellBoundary, close_ring: bool) -> Self {
Self {
cell_boundary,
close_ring,
pos: 0,
}
}
pub const fn num_verts(&self) -> usize {
self.cell_boundary.numVerts as usize
}
#[inline(always)]
fn get_coordinate(&self, pos: usize) -> Coord<f64> {
assert!(pos < self.num_verts());
Coord::from((
self.cell_boundary.verts[pos].lng.to_degrees(),
self.cell_boundary.verts[pos].lat.to_degrees(),
))
}
}
impl<'b> Iterator for CellBoundaryIter<'b> {
type Item = Coord<f64>;
fn next(&mut self) -> Option<Self::Item> {
let num_verts = self.num_verts();
let value = if self.pos < num_verts {
Some(self.get_coordinate(self.pos))
} else if self.pos == num_verts && self.close_ring && num_verts != 0 {
Some(self.get_coordinate(0))
} else {
None
};
self.pos += 1;
value
}
fn size_hint(&self) -> (usize, Option<usize>) {
let len = self.num_verts() + usize::from(self.close_ring);
(len.saturating_sub(self.pos), None)
}
}
impl<'b> From<CellBoundaryIter<'b>> for Polygon<f64> {
fn from(mut gb_iter: CellBoundaryIter<'b>) -> Self {
gb_iter.close_ring = true;
gb_iter.pos = 0; let mut exterior = Vec::with_capacity(gb_iter.num_verts() + 1);
for coord in gb_iter {
exterior.push(coord);
}
Self::new(LineString::from(exterior), Vec::with_capacity(0))
}
}