geohash_rs/
decoder.rs

1//! Decoder for geohash-rs
2
3use crate::base32;
4use crate::LEFT;
5use crate::RIGHT;
6use std::collections::VecDeque;
7
8/// For each 5-bit chunk of the geohash, we calculate the midpoint of the current bound and then split
9/// the bound into two halves based on the value of the 5-bit chunk
10///
11/// Arguments:
12///
13/// * `geohash`: The geohash string to decode.
14///
15/// Returns:
16///
17/// A GPSBoundInfo struct
18///
19/// Panics:
20///
21/// Panics when geohash String is invalid
22///
23/// # Examples
24///
25/// ```
26/// use geohash_rs;
27/// let gps_bounds = geohash_rs::decode(String::from("wydm9qyc"));
28///
29/// assert_eq!(gps_bounds.latitude[0], 37.56654739379883);
30/// assert_eq!(gps_bounds.latitude[1], 37.56671905517578);
31/// assert_eq!(gps_bounds.longitude[0], 126.97826385498047);
32/// assert_eq!(gps_bounds.longitude[1], 126.97860717773438);
33/// ```
34pub fn decode(geohash: String) -> GPSBoundInfo {
35    let geo_bits: Vec<u8> = geohash.chars().map(|ch| base32::decode_b32(&ch)).collect();
36    let mut lat_bound: [f32; 2] = [-90.0, 90.0];
37    let mut long_bound: [f32; 2] = [-180.0, 180.0];
38    let mut bound_queue = VecDeque::from([&mut long_bound, &mut lat_bound]);
39
40    for bit_5 in geo_bits {
41        cal_coord_bound(&bit_5, &mut bound_queue);
42    }
43
44    GPSBoundInfo {
45        latitude: lat_bound,
46        longitude: long_bound,
47    }
48}
49
50/// `cal_coord_bound` takes a `bit_5` and a `bound_queue` and for each of the 5 bits in `bit_5` it calls
51/// `cal_bound` with the `bit_5` and the bit.
52///
53/// Arguments:
54///
55/// * `bit_5`: the 5-bit binary representation of the current number
56/// * `bound_queue`: a queue of bounding boxes, each bounding box is represented by a 2-element array,
57/// the first element is the lower bound, the second element is the upper bound.
58fn cal_coord_bound(bit_5: &u8, bound_queue: &mut VecDeque<&mut [f32; 2]>) {
59    for i in [4_u8, 3, 2, 1, 0] {
60        cal_bound(bit_5, &i, bound_queue);
61    }
62}
63
64/// It takes a bit and a bound queue, and updates the bound queue
65///
66/// Arguments:
67///
68/// * `bit_5`: The 5-bit binary representation of the current character.
69/// * `i`: the index of the bit we're currently looking at
70/// * `bound_queue`: A queue of the bounds of the current bit.
71fn cal_bound(bit_5: &u8, i: &u8, bound_queue: &mut VecDeque<&mut [f32; 2]>) {
72    let bound_info = bound_queue.pop_front().unwrap();
73    let mid = bound_info.iter().sum::<f32>() / 2.0;
74    if 0 < (bit_5 & 1 << i) {
75        bound_info[LEFT] = mid;
76    } else {
77        bound_info[RIGHT] = mid;
78    }
79    bound_queue.push_back(bound_info);
80}
81
82/// `GPSBoundInfo` is a struct that contains two arrays of two floats each, one for latitude and one for
83/// longitude.
84///
85/// Properties:
86///
87/// * `latitude`: [f32;2] - The latitude of the bounding box. The first value is the minimum latitude,
88/// the second value is the maximum latitude.
89/// * `longitude`: The longitude of the location.
90pub struct GPSBoundInfo {
91    pub latitude: [f32; 2],
92    pub longitude: [f32; 2],
93}