use crate::error::{MeshCodeError, Result};
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[repr(u8)]
pub enum MeshLevel {
First = 1,
Second = 2,
Third = 3,
FourthHalf = 4,
FourthQuarter = 5,
FourthEighth = 6,
Fifth = 7,
}
impl MeshLevel {
pub fn from_code_length(len: usize) -> Result<Self> {
match len {
4 => Ok(MeshLevel::First),
6 => Ok(MeshLevel::Second),
8 => Ok(MeshLevel::Third),
9 => Ok(MeshLevel::FourthHalf),
10 => Ok(MeshLevel::FourthQuarter),
11 => Ok(MeshLevel::FourthEighth),
_ => Err(MeshCodeError::InvalidLevel(len)),
}
}
pub fn from_code_string(code_str: &str) -> Result<Self> {
let len = code_str.len();
if len == 10 {
let ninth_digit = code_str.chars().nth(8).unwrap();
if ('1'..='4').contains(&ninth_digit) {
return Ok(MeshLevel::FourthQuarter);
} else {
return Ok(MeshLevel::Fifth);
}
}
Self::from_code_length(len)
}
pub fn code_length(self) -> usize {
match self {
MeshLevel::First => 4,
MeshLevel::Second => 6,
MeshLevel::Third => 8,
MeshLevel::FourthHalf => 9,
MeshLevel::FourthQuarter => 10,
MeshLevel::FourthEighth => 11,
MeshLevel::Fifth => 10,
}
}
pub fn lat_size_degrees(self) -> f64 {
match self {
MeshLevel::First => 40.0 / 60.0,
MeshLevel::Second => 5.0 / 60.0,
MeshLevel::Third => 30.0 / 3600.0,
MeshLevel::FourthHalf => 15.0 / 3600.0,
MeshLevel::FourthQuarter => 7.5 / 3600.0,
MeshLevel::FourthEighth => 3.75 / 3600.0,
MeshLevel::Fifth => 3.0 / 3600.0,
}
}
pub fn lon_size_degrees(self) -> f64 {
match self {
MeshLevel::First => 1.0,
MeshLevel::Second => 7.5 / 60.0,
MeshLevel::Third => 45.0 / 3600.0,
MeshLevel::FourthHalf => 22.5 / 3600.0,
MeshLevel::FourthQuarter => 11.25 / 3600.0,
MeshLevel::FourthEighth => 5.625 / 3600.0,
MeshLevel::Fifth => 4.5 / 3600.0,
}
}
pub fn approximate_size_meters(self) -> f64 {
match self {
MeshLevel::First => 80000.0,
MeshLevel::Second => 10000.0,
MeshLevel::Third => 1000.0,
MeshLevel::FourthHalf => 500.0,
MeshLevel::FourthQuarter => 250.0,
MeshLevel::FourthEighth => 125.0,
MeshLevel::Fifth => 100.0,
}
}
pub fn parent(self) -> Option<Self> {
match self {
MeshLevel::First => None,
MeshLevel::Second => Some(MeshLevel::First),
MeshLevel::Third => Some(MeshLevel::Second),
MeshLevel::FourthHalf => Some(MeshLevel::Third),
MeshLevel::FourthQuarter => Some(MeshLevel::Third),
MeshLevel::FourthEighth => Some(MeshLevel::Third),
MeshLevel::Fifth => Some(MeshLevel::Third),
}
}
pub fn as_u8(self) -> u8 {
self as u8
}
pub fn from_u8(value: u8) -> Result<Self> {
match value {
1 => Ok(MeshLevel::First),
2 => Ok(MeshLevel::Second),
3 => Ok(MeshLevel::Third),
4 => Ok(MeshLevel::FourthHalf),
5 => Ok(MeshLevel::FourthQuarter),
6 => Ok(MeshLevel::FourthEighth),
7 => Ok(MeshLevel::Fifth),
_ => Err(MeshCodeError::InvalidLevel(value as usize)),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_from_code_length() {
assert_eq!(MeshLevel::from_code_length(4).unwrap(), MeshLevel::First);
assert_eq!(MeshLevel::from_code_length(6).unwrap(), MeshLevel::Second);
assert_eq!(MeshLevel::from_code_length(8).unwrap(), MeshLevel::Third);
assert!(MeshLevel::from_code_length(3).is_err());
}
#[test]
fn test_lat_lon_size() {
assert!((MeshLevel::First.lat_size_degrees() - 40.0 / 60.0).abs() < 1e-10);
assert!((MeshLevel::First.lon_size_degrees() - 1.0).abs() < 1e-10);
assert!((MeshLevel::Third.lat_size_degrees() - 30.0 / 3600.0).abs() < 1e-10);
}
#[test]
fn test_parent() {
assert_eq!(MeshLevel::Third.parent(), Some(MeshLevel::Second));
assert_eq!(MeshLevel::Second.parent(), Some(MeshLevel::First));
assert_eq!(MeshLevel::First.parent(), None);
}
}