oxigdal_noalloc/
geohash.rs1use crate::BBox2D;
7
8pub const BASE32_CHARS: &[u8; 32] = b"0123456789bcdefghjkmnpqrstuvwxyz";
10
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub struct GeoHashFixed {
16 chars: [u8; 12],
17 len: u8,
18}
19
20impl GeoHashFixed {
21 #[must_use]
25 pub fn encode(lat: f64, lon: f64, precision: u8) -> Self {
26 let precision = precision.clamp(1, 12) as usize;
27
28 let mut chars = [0u8; 12];
29 let mut min_lat = -90.0_f64;
31 let mut max_lat = 90.0_f64;
32 let mut min_lon = -180.0_f64;
33 let mut max_lon = 180.0_f64;
34
35 let mut is_lon = true; let mut bits = 0u8; let mut bit_count = 0u8;
38 let mut char_idx = 0usize;
39
40 let total_bits = precision * 5;
42
43 for _ in 0..total_bits {
44 if is_lon {
45 let mid = (min_lon + max_lon) * 0.5;
46 if lon >= mid {
47 bits = (bits << 1) | 1;
48 min_lon = mid;
49 } else {
50 bits <<= 1;
51 max_lon = mid;
52 }
53 } else {
54 let mid = (min_lat + max_lat) * 0.5;
55 if lat >= mid {
56 bits = (bits << 1) | 1;
57 min_lat = mid;
58 } else {
59 bits <<= 1;
60 max_lat = mid;
61 }
62 }
63 is_lon = !is_lon;
64 bit_count += 1;
65
66 if bit_count == 5 {
67 chars[char_idx] = BASE32_CHARS[bits as usize];
68 char_idx += 1;
69 bits = 0;
70 bit_count = 0;
71 }
72 }
73
74 Self {
75 chars,
76 len: precision as u8,
77 }
78 }
79
80 #[must_use]
82 #[inline]
83 pub fn as_bytes(&self) -> &[u8] {
84 &self.chars[..self.len as usize]
85 }
86
87 #[must_use]
89 #[inline]
90 pub fn precision(&self) -> u8 {
91 self.len
92 }
93
94 #[must_use]
96 pub fn decode(&self) -> (f64, f64) {
97 let bbox = self.bbox();
98 let lat = (bbox.min_y + bbox.max_y) * 0.5;
99 let lon = (bbox.min_x + bbox.max_x) * 0.5;
100 (lat, lon)
101 }
102
103 #[must_use]
107 pub fn bbox(&self) -> BBox2D {
108 let mut min_lat = -90.0_f64;
109 let mut max_lat = 90.0_f64;
110 let mut min_lon = -180.0_f64;
111 let mut max_lon = 180.0_f64;
112
113 let bytes = self.as_bytes();
114 let mut is_lon = true;
115
116 for &byte in bytes {
117 let idx = base32_decode_char(byte);
119 for bit_pos in (0..5).rev() {
121 let bit = (idx >> bit_pos) & 1;
122 if is_lon {
123 let mid = (min_lon + max_lon) * 0.5;
124 if bit == 1 {
125 min_lon = mid;
126 } else {
127 max_lon = mid;
128 }
129 } else {
130 let mid = (min_lat + max_lat) * 0.5;
131 if bit == 1 {
132 min_lat = mid;
133 } else {
134 max_lat = mid;
135 }
136 }
137 is_lon = !is_lon;
138 }
139 }
140
141 BBox2D::new(min_lon, min_lat, max_lon, max_lat)
142 }
143}
144
145#[inline]
148fn base32_decode_char(c: u8) -> u8 {
149 for (i, &ch) in BASE32_CHARS.iter().enumerate() {
150 if ch == c {
151 return i as u8;
152 }
153 }
154 0
155}