use super::{
Degrees, Lle, Meters, Wgs84,
std::{self, ops::Range},
};
pub struct Grid<const N: usize>([u8; N]);
#[derive(Copy, Clone, Debug)]
pub enum Error {
OutOfBoundsGrid,
InvalidGridLength,
Malformed,
}
impl std::str::FromStr for Grid<6> {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 6 {
return Err(Error::InvalidGridLength);
}
Ok(Self(s.as_bytes().try_into().unwrap()))
}
}
impl Grid<6> {
pub fn center_lat_lon(&self) -> Result<Lle<Wgs84>, Error> {
let top_left = self.raw_top_left_lat_lon()?;
Ok(Lle::new(
Degrees::new(top_left.latitude.as_float() + (1.0 / 48.0) - 90.0),
Degrees::new(top_left.longitude.as_float() + (1.0 / 24.0) - 180.0),
Meters::new(0.0),
))
}
pub fn corners_lat_lon(&self) -> Result<[Lle<Wgs84>; 2], Error> {
let top_left = self.raw_top_left_lat_lon()?;
let next_lat = top_left.latitude.as_float() + (1.0 / 24.0) - 90.0;
let next_lon = top_left.longitude.as_float() + (1.0 / 12.0) - 180.0;
let lat = top_left.latitude.as_float() - 90.0;
let lon = top_left.longitude.as_float() - 180.0;
Ok([
Lle::new(Degrees::new(lat), Degrees::new(lon), Meters::new(0.0)),
Lle::new(
Degrees::new(next_lat),
Degrees::new(next_lon),
Meters::new(0.0),
),
])
}
fn raw_top_left_lat_lon(&self) -> Result<Lle<Wgs84>, Error> {
let [
lon,
lat,
square_lon,
square_lat,
subsquare_lon,
subsquare_lat,
] = self.0;
fn ascii_to_int(range: Range<u8>, v: u8) -> Result<u8, Error> {
if !(range.contains(&v)) {
return Err(Error::Malformed);
}
Ok(v - range.start)
}
let lon: f64 = ascii_to_int(65..91, lon)? as f64;
let lat: f64 = ascii_to_int(65..91, lat)? as f64;
let square_lon: f64 = ascii_to_int(48..58, square_lon)? as f64;
let square_lat: f64 = ascii_to_int(48..58, square_lat)? as f64;
let subsquare_lon: f64 = ascii_to_int(97..123, subsquare_lon)? as f64;
let subsquare_lat: f64 = ascii_to_int(97..123, subsquare_lat)? as f64;
Ok(Lle::new(
Degrees::new((lat * 10.0) + (square_lat) + (subsquare_lat / 24.0)),
Degrees::new((lon * 20.0) + (square_lon * 2.0) + (subsquare_lon / 12.0)),
Meters::new(0.0),
))
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_example_from_internet() {
let grid: Grid<6> = "IO93ob".parse().unwrap();
let center = grid.center_lat_lon().unwrap();
assert_eq!(-0.7916666666666856, center.longitude.as_float());
assert_eq!(53.0625, center.latitude.as_float());
}
#[test]
fn test_corners() {
let grid: Grid<6> = "IO93ob".parse().unwrap();
let [tl, br] = grid.corners_lat_lon().unwrap();
assert_eq!(-0.8333333333333428, tl.longitude.as_float());
assert_eq!(53.04166666666666, tl.latitude.as_float());
assert_eq!(-0.75, br.longitude.as_float());
assert_eq!(53.083333333333314, br.latitude.as_float());
}
}