h3ron 0.18.0

High-level rust API for H3
Documentation
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 }
    }

    /// iterate over the coordinates of the boundary vertices.
    ///
    /// The order of the vertices is preserved as generated by the H3 library
    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> {
    /// the `cell_boundary` must be initialized
    pub const fn new(cell_boundary: &'b CellBoundary, close_ring: bool) -> Self {
        Self {
            cell_boundary,
            close_ring,
            pos: 0,
        }
    }

    /// number of vertices in the boundary
    ///
    /// Does not include the extra vertex which is returned when `close_ring` is set
    /// to true.
    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; // rewind
        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))
    }
}