use crate::cell::GeoCell;
pub fn grid_disk(cell: &GeoCell, k: usize) -> Option<Vec<GeoCell>> {
let resolution = cell.resolution();
let raw = a5::grid_disk(cell.raw(), k).ok()?;
let uniform = a5::uncompact(&raw, resolution).ok()?;
Some(uniform.into_iter().map(GeoCell::new).collect())
}
pub fn grid_disk_vertex(cell: &GeoCell, k: usize) -> Option<Vec<GeoCell>> {
let resolution = cell.resolution();
let raw = a5::grid_disk_vertex(cell.raw(), k).ok()?;
let uniform = a5::uncompact(&raw, resolution).ok()?;
Some(uniform.into_iter().map(GeoCell::new).collect())
}
pub fn neighbors(cell: &GeoCell) -> Option<Vec<GeoCell>> {
let resolution = cell.resolution();
let raw = a5::grid_disk(cell.raw(), 1).ok()?;
let uniform = a5::uncompact(&raw, resolution).ok()?;
Some(
uniform
.into_iter()
.filter(|&c| c != cell.raw())
.map(GeoCell::new)
.collect(),
)
}
pub fn vertex_neighbors(cell: &GeoCell) -> Option<Vec<GeoCell>> {
let resolution = cell.resolution();
let raw = a5::grid_disk_vertex(cell.raw(), 1).ok()?;
let uniform = a5::uncompact(&raw, resolution).ok()?;
Some(
uniform
.into_iter()
.filter(|&c| c != cell.raw())
.map(GeoCell::new)
.collect(),
)
}
pub fn spherical_cap(center: &GeoCell, radius: f64) -> Option<Vec<GeoCell>> {
let resolution = center.resolution();
let mixed = a5::spherical_cap(center.raw(), radius).ok()?;
let uniform = a5::uncompact(&mixed, resolution).ok()?;
Some(uniform.into_iter().map(GeoCell::new).collect())
}
pub fn res0_cells() -> Vec<GeoCell> {
a5::get_res0_cells()
.unwrap_or_default()
.into_iter()
.map(GeoCell::new)
.collect()
}
pub fn num_cells(resolution: i32) -> u64 {
a5::get_num_cells(resolution)
}
pub fn compact(cells: &[GeoCell]) -> Vec<GeoCell> {
let raw: Vec<u64> = cells.iter().map(|c| c.raw()).collect();
a5::compact(&raw)
.unwrap_or_default()
.into_iter()
.map(GeoCell::new)
.collect()
}
pub fn uncompact(cells: &[GeoCell], resolution: i32) -> Vec<GeoCell> {
let raw: Vec<u64> = cells.iter().map(|c| c.raw()).collect();
a5::uncompact(&raw, resolution)
.unwrap_or_default()
.into_iter()
.map(GeoCell::new)
.collect()
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn neighbors_returns_cells() {
let cell = GeoCell::from_lon_lat(2.3522, 48.8566, 5).unwrap();
let n = neighbors(&cell).unwrap();
assert!(!n.is_empty());
assert!(n.len() >= 3 && n.len() <= 6);
}
#[test]
fn grid_disk_includes_center() {
let cell = GeoCell::from_lon_lat(2.3522, 48.8566, 5).unwrap();
let disk = grid_disk(&cell, 1).unwrap();
assert!(disk.contains(&cell));
}
#[test]
fn res0_cells_count() {
let cells = res0_cells();
assert!(!cells.is_empty());
}
#[test]
fn spherical_cap_returns_uniform_resolution() {
for res in [5, 7, 9] {
let center = GeoCell::from_lon_lat(2.3522, 48.8566, res).unwrap();
let cells = spherical_cap(¢er, 50_000.0).expect("cap should succeed");
assert!(!cells.is_empty(), "spherical_cap empty at res {}", res);
for cell in &cells {
assert_eq!(
cell.resolution(),
res,
"spherical_cap leaked resolution {} cell at requested res {}",
cell.resolution(),
res
);
}
}
}
#[test]
fn vertex_neighbors_includes_edge_neighbours_and_more() {
let cell = GeoCell::from_lon_lat(2.3522, 48.8566, 5).unwrap();
let edge = neighbors(&cell).unwrap();
let vertex = vertex_neighbors(&cell).unwrap();
let vertex_set: std::collections::HashSet<u64> =
vertex.iter().map(|c| c.raw()).collect();
for c in &edge {
assert!(
vertex_set.contains(&c.raw()),
"edge-neighbour {:#x} missing from vertex_neighbors",
c.raw()
);
}
assert!(
vertex.len() >= edge.len(),
"vertex_neighbors ({}) should be ≥ neighbors ({})",
vertex.len(),
edge.len()
);
for c in &vertex {
assert_eq!(c.resolution(), 5);
assert_ne!(c.raw(), cell.raw());
}
}
#[test]
fn grid_disk_returns_uniform_resolution() {
let center = GeoCell::from_lon_lat(2.3522, 48.8566, 7).unwrap();
let disk = grid_disk(¢er, 4).expect("grid_disk should succeed");
for cell in &disk {
assert_eq!(cell.resolution(), 7);
}
}
}