use axum::http::StatusCode; use rustc_hash::FxHashMap; use tracing::warn; use crate::consts::{H3_PRECOMPUTE_MAX, H3_REQUEST_MAX, H3_REQUEST_MIN}; /// Validate that an H3 resolution is within the allowed range and convert to h3o::Resolution. pub fn validate_h3_resolution(resolution: u8) -> Result { if !(H3_REQUEST_MIN..=H3_REQUEST_MAX).contains(&resolution) { warn!( resolution, "Resolution out of range [{}, {}]", H3_REQUEST_MIN, H3_REQUEST_MAX ); return Err(( StatusCode::BAD_REQUEST, format!( "resolution must be between {} and {}", H3_REQUEST_MIN, H3_REQUEST_MAX ), )); } h3o::Resolution::try_from(resolution).map_err(|error| { ( StatusCode::INTERNAL_SERVER_ERROR, format!("Invalid H3 resolution {}: {}", resolution, error), ) }) } /// Resolve a row's H3 cell at the requested resolution, using precomputed max-resolution cells. #[inline] pub fn cell_for_row( row: usize, precomputed: &[u64], h3_res: h3o::Resolution, need_parent: bool, ) -> u64 { let max_cell = precomputed[row]; if !need_parent || max_cell == 0 { return max_cell; } let cell = h3o::CellIndex::try_from(max_cell).expect("precomputed H3 cell must be valid"); u64::from( cell.parent(h3_res) .expect("parent resolution must be valid for precomputed cell"), ) } /// Like cell_for_row but caches parent lookups in the provided map. #[inline] pub fn cell_for_row_cached( row: usize, precomputed: &[u64], h3_res: h3o::Resolution, need_parent: bool, cache: &mut FxHashMap, ) -> u64 { let max_cell = precomputed[row]; if !need_parent || max_cell == 0 { return max_cell; } *cache.entry(max_cell).or_insert_with(|| { let cell = h3o::CellIndex::try_from(max_cell).expect("precomputed H3 cell must be valid"); u64::from( cell.parent(h3_res) .expect("parent resolution must be valid"), ) }) } /// Whether the given resolution requires computing a parent from precomputed cells. #[inline] pub fn needs_parent(resolution: u8) -> bool { resolution < H3_PRECOMPUTE_MAX }