use crate::base_cells::baseCellNumToCell;
use crate::bbox::{bbox_contains_bbox, bbox_overlaps_bbox, bbox_to_cell_boundary, bboxes_from_geo_polygon};
use crate::constants::{
CELL_SCALE_FACTOR, CHILD_SCALE_FACTOR, EPSILON, MAX_EDGE_LENGTH_RADS, MAX_H3_RES,
M_PI, M_PI_2, NORTH_POLE_CELLS, NUM_BASE_CELLS, RES0_BBOXES, SOUTH_POLE_CELLS,
};
use crate::h3_index::{
get_base_cell, get_index_digit, get_resolution, is_pentagon, set_index_digit,
set_resolution,
};
use crate::hierarchy::parent_child::{cell_to_center_child, cell_to_children_size};
use crate::iterators::{iter_init_parent, iter_step_child, IterCellsChildren};
use crate::polygon::{
cell_boundary_crosses_polygon, cell_boundary_inside_polygon, point_inside_polygon,
validate_polygon_flags,
};
use crate::types::{BBox, ContainmentMode, GeoPolygon, H3Error, H3Index, LatLng, H3_NULL};
use std::ptr;
fn next_cell_for_polyfill(mut cell: H3Index) -> H3Index {
let mut res = get_resolution(cell);
loop {
if res == 0 {
let next_bc = get_base_cell(cell) + 1;
return if next_bc < (NUM_BASE_CELLS as i32) {
baseCellNumToCell(next_bc) } else {
H3_NULL
};
}
let mut parent = cell;
set_resolution(&mut parent, res - 1);
set_index_digit(&mut parent, res, crate::types::Direction::InvalidDigit);
let digit = get_index_digit(cell, res);
if digit < crate::types::Direction::InvalidDigit {
let mut next_digit_val = digit as u8 + 1;
if is_pentagon(parent) && next_digit_val == crate::types::Direction::KAxes as u8 {
next_digit_val += 1;
}
if next_digit_val < crate::types::Direction::InvalidDigit as u8 {
set_index_digit(
&mut cell,
res,
crate::types::Direction::try_from(next_digit_val).unwrap_or(crate::types::Direction::InvalidDigit),
);
return cell;
}
}
res -= 1;
cell = parent;
}
}
#[derive(Debug)]
pub struct IterCellsPolygonCompact {
pub cell: H3Index,
pub error: H3Error,
_res: i32, _flags: u32, _polygon_ptr: *const GeoPolygon, _bboxes_ptr: *mut BBox, _num_bboxes: usize, _started: bool, }
impl IterCellsPolygonCompact {
fn _new(polygon: &GeoPolygon, res: i32, flags: u32) -> Self {
let mut iter = Self {
cell: baseCellNumToCell(0), error: H3Error::Success,
_res: res,
_flags: flags,
_polygon_ptr: polygon as *const GeoPolygon,
_bboxes_ptr: ptr::null_mut(),
_num_bboxes: 0,
_started: false,
};
if res < 0 || res > MAX_H3_RES {
iter.error = H3Error::ResDomain;
iter.cell = H3_NULL;
return iter;
}
if let Err(e) = validate_polygon_flags(flags) {
iter.error = e;
iter.cell = H3_NULL;
return iter;
}
iter._num_bboxes = polygon.num_holes + 1;
let bboxes_layout = std::alloc::Layout::array::<BBox>(iter._num_bboxes).unwrap();
iter._bboxes_ptr = unsafe { std::alloc::alloc(bboxes_layout) as *mut BBox };
if iter._bboxes_ptr.is_null() {
iter.error = H3Error::MemoryAlloc;
iter.cell = H3_NULL;
return iter;
}
unsafe {
bboxes_from_geo_polygon(
&*iter._polygon_ptr,
std::slice::from_raw_parts_mut(iter._bboxes_ptr, iter._num_bboxes),
);
}
iter
}
pub fn step(&mut self) {
if self.cell == H3_NULL || self.error != H3Error::Success {
self.destroy_internal_data(); self.cell = H3_NULL;
return;
}
let mut current_cell_iter = self.cell;
if self._started {
current_cell_iter = next_cell_for_polyfill(current_cell_iter);
} else {
self._started = true;
}
let polygon = unsafe { &*self._polygon_ptr };
let bboxes = unsafe { std::slice::from_raw_parts(self._bboxes_ptr, self._num_bboxes) };
if polygon.geoloop.num_verts == 0 && polygon.num_holes == 0 {
self.destroy_internal_data();
self.cell = H3_NULL;
return;
}
let mode = match crate::polygon::flag_get_containment_mode(self._flags) {
Ok(m) => m,
Err(e) => {
self.error = e;
self.cell = H3_NULL;
self.destroy_internal_data(); return;
}
};
while current_cell_iter != H3_NULL {
let cell_res = get_resolution(current_cell_iter);
if cell_res == self._res {
let mut should_output = false;
match mode {
ContainmentMode::Center | ContainmentMode::Overlapping | ContainmentMode::OverlappingBbox => {
match crate::indexing::cell_to_lat_lng(current_cell_iter) {
Ok(center_val) => {
if point_inside_polygon(polygon, bboxes, ¢er_val) {
should_output = true;
}
}
Err(e) => {
self.error = e; self.cell = H3_NULL;
return; }
}
}
_ => {} }
if !should_output
&& (mode == ContainmentMode::Full
|| mode == ContainmentMode::Overlapping
|| mode == ContainmentMode::OverlappingBbox)
{
match crate::indexing::cell_to_boundary(current_cell_iter) {
Ok(boundary_val) => {
let mut cell_bbox = BBox::default();
match cell_to_bbox(current_cell_iter, &mut cell_bbox, false) {
Ok(()) => {
if (mode == ContainmentMode::Full || mode == ContainmentMode::OverlappingBbox)
&& cell_boundary_inside_polygon(polygon, bboxes, &boundary_val, &cell_bbox)
{
should_output = true;
} else if (mode == ContainmentMode::Overlapping || mode == ContainmentMode::OverlappingBbox)
&& cell_boundary_crosses_polygon(polygon, bboxes, &boundary_val, &cell_bbox)
{
should_output = true;
}
}
Err(e) => {
self.error = e; self.cell = H3_NULL;
return; }
}
}
Err(e) => {
self.error = e; self.cell = H3_NULL;
return; }
}
}
if !should_output && mode == ContainmentMode::OverlappingBbox {
let mut cell_children_bbox = BBox::default(); if cell_to_bbox(current_cell_iter, &mut cell_children_bbox, true).is_ok() {
if bbox_overlaps_bbox(&bboxes[0], &cell_children_bbox) {
let bbox_boundary = bbox_to_cell_boundary(&cell_children_bbox);
if bbox_contains_bbox(&cell_children_bbox, &bboxes[0]) || point_inside_polygon(polygon, bboxes, &bbox_boundary.verts[0]) || cell_boundary_crosses_polygon(polygon, bboxes, &bbox_boundary, &cell_children_bbox)
{
should_output = true;
}
}
} else {
self.error = H3Error::Failed;
break;
}
}
if should_output {
self.cell = current_cell_iter;
return;
}
} else if cell_res < self._res {
let mut children_bbox = BBox::default();
if cell_to_bbox(current_cell_iter, &mut children_bbox, true).is_ok() {
if bbox_overlaps_bbox(&bboxes[0], &children_bbox) {
let children_bbox_boundary = bbox_to_cell_boundary(&children_bbox);
if cell_boundary_inside_polygon(polygon, bboxes, &children_bbox_boundary, &children_bbox) {
self.cell = current_cell_iter; return;
}
let center_child = cell_to_center_child(current_cell_iter, cell_res + 1).unwrap_or(H3_NULL); if center_child == H3_NULL {
self.error = H3Error::Failed;
break;
} current_cell_iter = center_child;
continue; }
} else {
self.error = H3Error::Failed;
break;
}
}
current_cell_iter = next_cell_for_polyfill(current_cell_iter);
}
self.destroy_internal_data();
self.cell = H3_NULL; }
fn destroy_internal_data(&mut self) {
if !self._bboxes_ptr.is_null() {
let layout = std::alloc::Layout::array::<BBox>(self._num_bboxes).unwrap();
unsafe { std::alloc::dealloc(self._bboxes_ptr as *mut u8, layout) };
self._bboxes_ptr = ptr::null_mut();
}
}
}
impl Drop for IterCellsPolygonCompact {
fn drop(&mut self) {
self.destroy_internal_data();
}
}
#[derive(Debug)]
pub struct IterCellsPolygon {
pub cell: H3Index,
pub error: H3Error,
_cell_iter: IterCellsPolygonCompact, _child_iter: IterCellsChildren, }
impl IterCellsPolygon {
pub fn new(polygon: &GeoPolygon, res: i32, flags: u32) -> Self {
let cell_iter = IterCellsPolygonCompact::_new(polygon, res, flags); let mut child_iter = iter_init_parent(cell_iter.cell, res);
let initial_cell;
if cell_iter.error == H3Error::Success {
if cell_iter.cell == H3_NULL {
initial_cell = H3_NULL;
} else if get_resolution(cell_iter.cell) == res {
initial_cell = cell_iter.cell; child_iter.h = H3_NULL; } else {
initial_cell = child_iter.h; }
} else {
initial_cell = H3_NULL; }
Self {
cell: initial_cell,
error: cell_iter.error,
_cell_iter: cell_iter,
_child_iter: child_iter,
}
}
pub fn step(&mut self) {
if self.cell == H3_NULL || self.error != H3Error::Success {
self.destroy_internal_data();
self.cell = H3_NULL;
return;
}
if self._child_iter.h != H3_NULL {
iter_step_child(&mut self._child_iter);
if self._child_iter.h != H3_NULL {
self.cell = self._child_iter.h;
return;
}
}
self._cell_iter.step();
if self._cell_iter.cell != H3_NULL {
self._child_iter = iter_init_parent(self._cell_iter.cell, self._cell_iter._res);
if get_resolution(self._cell_iter.cell) == self._cell_iter._res {
self.cell = self._cell_iter.cell;
self._child_iter.h = H3_NULL; } else {
self.cell = self._child_iter.h;
}
} else {
self.cell = H3_NULL;
self.error = self._cell_iter.error; self.destroy_internal_data();
}
}
fn destroy_internal_data(&mut self) {
self._cell_iter.destroy_internal_data();
}
}
impl Drop for IterCellsPolygon {
fn drop(&mut self) {
self.destroy_internal_data();
}
}
pub fn max_polygon_to_cells_size(polygon: &GeoPolygon, res: i32, flags: u32) -> Result<i64, H3Error> {
if polygon.geoloop.num_verts == 0 && polygon.num_holes == 0 {
return Ok(0);
}
let mut iter = IterCellsPolygonCompact::_new(polygon, res, flags);
if iter.error != H3Error::Success {
let err = iter.error;
iter.destroy_internal_data(); return Err(err);
}
let mut count: i64 = 0;
iter.step(); while iter.cell != H3_NULL {
if iter.error != H3Error::Success {
let err = iter.error;
iter.destroy_internal_data();
return Err(err);
}
let children_count = cell_to_children_size(iter.cell, res)?;
count = count.saturating_add(children_count);
iter.step();
}
if iter.error != H3Error::Success {
let err = iter.error;
iter.destroy_internal_data();
return Err(err);
}
iter.destroy_internal_data(); Ok(count)
}
pub fn polygon_to_cells(polygon: &GeoPolygon, res: i32, flags: u32, out: &mut [H3Index]) -> Result<(), H3Error> {
let mut iter = IterCellsPolygon::new(polygon, res, flags);
if iter.error != H3Error::Success {
return Err(iter.error);
}
let mut i: usize = 0;
while iter.cell != H3_NULL {
if iter.error != H3Error::Success {
return Err(iter.error);
}
if i >= out.len() {
iter.destroy_internal_data();
return Err(H3Error::MemoryBounds);
}
out[i] = iter.cell;
i += 1;
iter.step();
}
if iter.error != H3Error::Success {
return Err(iter.error);
}
Ok(())
}
pub(crate) fn cell_to_bbox(cell: H3Index, out: &mut BBox, cover_children: bool) -> Result<(), H3Error> {
let res = get_resolution(cell);
if res == 0 {
let base_cell = get_base_cell(cell);
if base_cell < 0 || base_cell >= (NUM_BASE_CELLS as i32) {
return Err(H3Error::CellInvalid);
}
*out = RES0_BBOXES[base_cell as usize];
} else {
let center = crate::indexing::cell_to_lat_lng(cell)?; let edge_len_rad = MAX_EDGE_LENGTH_RADS[res as usize];
let lat_span = edge_len_rad; let lng_span_at_equator = edge_len_rad;
let lng_span = if center.lat.cos().abs() > EPSILON {
lng_span_at_equator / center.lat.cos().abs()
} else {
M_PI };
out.north = center.lat + lat_span;
out.south = center.lat - lat_span;
out.east = center.lng + lng_span;
out.west = center.lng - lng_span;
}
out.north = out.north.min(M_PI_2);
out.south = out.south.max(-M_PI_2);
crate::bbox::scale_bbox(
out,
if cover_children {
CHILD_SCALE_FACTOR
} else {
CELL_SCALE_FACTOR
},
);
if cell == H3Index(NORTH_POLE_CELLS[res as usize]) {
out.north = M_PI_2;
}
if cell == H3Index(SOUTH_POLE_CELLS[res as usize]) {
out.south = -M_PI_2;
}
if out.north == M_PI_2 || out.south == -M_PI_2 {
out.east = M_PI;
out.west = -M_PI;
}
Ok(())
}