use crate::BBox2D;
pub const BASE32_CHARS: &[u8; 32] = b"0123456789bcdefghjkmnpqrstuvwxyz";
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct GeoHashFixed {
chars: [u8; 12],
len: u8,
}
impl GeoHashFixed {
#[must_use]
pub fn encode(lat: f64, lon: f64, precision: u8) -> Self {
let precision = precision.clamp(1, 12) as usize;
let mut chars = [0u8; 12];
let mut min_lat = -90.0_f64;
let mut max_lat = 90.0_f64;
let mut min_lon = -180.0_f64;
let mut max_lon = 180.0_f64;
let mut is_lon = true; let mut bits = 0u8; let mut bit_count = 0u8;
let mut char_idx = 0usize;
let total_bits = precision * 5;
for _ in 0..total_bits {
if is_lon {
let mid = (min_lon + max_lon) * 0.5;
if lon >= mid {
bits = (bits << 1) | 1;
min_lon = mid;
} else {
bits <<= 1;
max_lon = mid;
}
} else {
let mid = (min_lat + max_lat) * 0.5;
if lat >= mid {
bits = (bits << 1) | 1;
min_lat = mid;
} else {
bits <<= 1;
max_lat = mid;
}
}
is_lon = !is_lon;
bit_count += 1;
if bit_count == 5 {
chars[char_idx] = BASE32_CHARS[bits as usize];
char_idx += 1;
bits = 0;
bit_count = 0;
}
}
Self {
chars,
len: precision as u8,
}
}
#[must_use]
#[inline]
pub fn as_bytes(&self) -> &[u8] {
&self.chars[..self.len as usize]
}
#[must_use]
#[inline]
pub fn precision(&self) -> u8 {
self.len
}
#[must_use]
pub fn decode(&self) -> (f64, f64) {
let bbox = self.bbox();
let lat = (bbox.min_y + bbox.max_y) * 0.5;
let lon = (bbox.min_x + bbox.max_x) * 0.5;
(lat, lon)
}
#[must_use]
pub fn bbox(&self) -> BBox2D {
let mut min_lat = -90.0_f64;
let mut max_lat = 90.0_f64;
let mut min_lon = -180.0_f64;
let mut max_lon = 180.0_f64;
let bytes = self.as_bytes();
let mut is_lon = true;
for &byte in bytes {
let idx = base32_decode_char(byte);
for bit_pos in (0..5).rev() {
let bit = (idx >> bit_pos) & 1;
if is_lon {
let mid = (min_lon + max_lon) * 0.5;
if bit == 1 {
min_lon = mid;
} else {
max_lon = mid;
}
} else {
let mid = (min_lat + max_lat) * 0.5;
if bit == 1 {
min_lat = mid;
} else {
max_lat = mid;
}
}
is_lon = !is_lon;
}
}
BBox2D::new(min_lon, min_lat, max_lon, max_lat)
}
}
#[inline]
fn base32_decode_char(c: u8) -> u8 {
for (i, &ch) in BASE32_CHARS.iter().enumerate() {
if ch == c {
return i as u8;
}
}
0
}