use super::lnglat::{LngLat, LngLatBox};
use crate::Error;
use std::{fmt::Display, str::FromStr};
#[derive(Debug)]
pub enum LevelAndCode {
Primary(PrimaryCode),
Secondary(SecondaryCode),
Standard(StandardCode),
Half(HalfCode),
Quarter(QuarterCode),
Eighth(EighthCode),
}
impl LevelAndCode {
pub fn from_int(code: u64) -> Result<LevelAndCode, Error> {
let digits = code.ilog10() as usize + 1;
Ok(match digits {
4 => LevelAndCode::Primary(PrimaryCode::from_int(code as u16)?),
6 => LevelAndCode::Secondary(SecondaryCode::from_int(code as u32)?),
7 => todo!(), 8 => LevelAndCode::Standard(StandardCode::from_int(code as u32)?),
9 => LevelAndCode::Half(HalfCode::from_int(code as u32)?), 10 => LevelAndCode::Quarter(QuarterCode::from_int(code)?),
11 => LevelAndCode::Eighth(EighthCode::from_int(code)?),
_ => return Err(Error::InvalidCode),
})
}
}
impl FromStr for LevelAndCode {
type Err = Error;
fn from_str(code: &str) -> Result<LevelAndCode, Error> {
let digits = code.len();
Ok(match digits {
4 => LevelAndCode::Primary(PrimaryCode::from_str(code)?),
6 => LevelAndCode::Secondary(SecondaryCode::from_str(code)?),
7 => todo!(), 8 => LevelAndCode::Standard(StandardCode::from_str(code)?),
9 => LevelAndCode::Half(HalfCode::from_str(code)?), 10 => LevelAndCode::Quarter(QuarterCode::from_str(code)?),
11 => LevelAndCode::Eighth(EighthCode::from_str(code)?),
_ => return Err(Error::InvalidCode),
})
}
}
pub trait GridSquareCode {
fn envelope(&self) -> LngLatBox;
fn index_xy(&self) -> (u32, u32);
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct PrimaryCode {
pub(crate) y: u8,
pub(crate) x: u8,
}
impl PrimaryCode {
#[inline]
pub const fn from_int(code: u16) -> Result<Self, Error> {
if code > 9999 {
return Err(Error::InvalidCode);
}
Ok(Self {
y: (code / 100) as u8,
x: (code % 100) as u8,
})
}
#[inline]
pub const fn from_lnglat(lnglat: LngLat) -> Result<Self, Error> {
let y = (lnglat.vlat / 20.) % 100.0;
let x = lnglat.lng() % 100.0;
if y >= 100.0 || x >= 100.0 || y < 0.0 || x < 0.0 {
return Err(Error::OutOfBounds);
}
Ok(Self {
y: y as u8,
x: x as u8,
})
}
#[inline]
pub const fn from_yx_raw(y: u8, x: u8) -> Result<Self, Error> {
if y > 99 || x > 99 {
return Err(Error::InvalidCode);
}
Ok(Self { y, x })
}
#[inline]
pub fn y1(&self) -> u8 {
self.y
}
#[inline]
pub fn x1(&self) -> u8 {
self.x
}
pub fn iter_secondary(self) -> impl Iterator<Item = SecondaryCode> {
(0..=7).flat_map(move |x2| {
(0..=7).map(move |y2| SecondaryCode {
primary: self,
y2,
x2,
})
})
}
}
impl GridSquareCode for PrimaryCode {
fn envelope(&self) -> LngLatBox {
LngLatBox::new(
LngLat::new_raw(
((self.x1() as u32 + 100) * 30) as f64,
(self.y1() as u32 * 20) as f64,
),
LngLat::new_raw(
((self.x1() as u32 + 101) * 30) as f64,
((self.y1() as u32 + 1) * 20) as f64,
),
)
}
#[inline]
fn index_xy(&self) -> (u32, u32) {
(self.x as u32, self.y as u32)
}
}
impl FromStr for PrimaryCode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 4 {
return Err(Error::InvalidCode);
}
if let Some((y1_str, x1_str)) = s.split_at_checked(2) {
let y1 = y1_str.parse::<u8>().map_err(|_| Error::InvalidCode)?;
let x1 = x1_str.parse::<u8>().map_err(|_| Error::InvalidCode)?;
Ok(Self { y: y1, x: x1 })
} else {
Err(Error::InvalidCode)
}
}
}
impl Display for PrimaryCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:02}{:02}", self.y, self.x)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct SecondaryCode {
primary: PrimaryCode,
y2: u8,
x2: u8,
}
impl SecondaryCode {
#[inline]
pub fn from_int(code: u32) -> Result<Self, Error> {
if code > 999999 {
return Err(Error::InvalidCode);
}
let y2 = ((code % 100) / 10) as u8;
let x2 = (code % 10) as u8;
if y2 > 7 || x2 > 7 {
return Err(Error::InvalidCode);
}
Ok(Self {
primary: PrimaryCode::from_int((code / 100) as u16)?,
y2,
x2,
})
}
#[inline]
pub fn from_lnglat(lnglat: LngLat) -> Result<Self, Error> {
let yd = (lnglat.vlat / 20. * 8.) as u32 % 8;
let xd = (lnglat.vlng / 30. * 8.) as u32 % 8;
Ok(Self {
primary: PrimaryCode::from_lnglat(lnglat)?,
y2: yd as u8,
x2: xd as u8,
})
}
#[inline]
pub fn y1(&self) -> u8 {
self.primary.y
}
#[inline]
pub fn x1(&self) -> u8 {
self.primary.x
}
#[inline]
pub fn y2(&self) -> u8 {
self.y2
}
#[inline]
pub fn x2(&self) -> u8 {
self.x2
}
#[inline]
pub fn primary(&self) -> PrimaryCode {
self.primary
}
pub fn iter_standard(self) -> impl Iterator<Item = StandardCode> {
(0..=9).flat_map(move |x3| {
(0..=9).map(move |y3| StandardCode {
secondary: self,
y3,
x3,
})
})
}
}
impl GridSquareCode for SecondaryCode {
fn envelope(&self) -> LngLatBox {
LngLatBox::new(
LngLat::new_raw(
((self.x1() as u32 + 100) * 30) as f64 + self.x2() as f64 * 3.75,
(self.y1() as u32 * 20) as f64 + self.y2() as f64 * 2.5,
),
LngLat::new_raw(
((self.x1() as u32 + 100) * 30) as f64 + (self.x2() + 1) as f64 * 3.75,
(self.y1() as u32 * 20) as f64 + (self.y2() + 1) as f64 * 2.5,
),
)
}
#[inline]
fn index_xy(&self) -> (u32, u32) {
let (px, py) = self.primary.index_xy();
(px * 8 + self.x2 as u32, py * 8 + self.y2 as u32)
}
}
impl FromStr for SecondaryCode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 6 {
return Err(Error::InvalidCode);
}
let Some((primary, rest)) = s.split_at_checked(4) else {
return Err(Error::InvalidCode);
};
let Some((y2_str, x2_str)) = rest.split_at_checked(1) else {
return Err(Error::InvalidCode);
};
let y2 = y2_str.parse::<u8>().map_err(|_| Error::InvalidCode)?;
let x2 = x2_str.parse::<u8>().map_err(|_| Error::InvalidCode)?;
if y2 > 7 || x2 > 7 {
return Err(Error::InvalidCode);
}
Ok(Self {
primary: PrimaryCode::from_str(primary)?,
y2,
x2,
})
}
}
impl Display for SecondaryCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:02}{:02}{}{}",
self.primary.y, self.primary.x, self.y2, self.x2
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StandardCode {
secondary: SecondaryCode,
y3: u8,
x3: u8,
}
impl StandardCode {
#[inline]
pub fn from_int(code: u32) -> Result<Self, Error> {
if code > 99999999 {
return Err(Error::InvalidCode);
}
let secondary = SecondaryCode::from_int(code / 100)?;
let y3 = ((code % 100) / 10) as u8;
let x3 = (code % 10) as u8;
Ok(Self { secondary, y3, x3 })
}
#[inline]
pub fn from_lnglat(lnglat: LngLat) -> Result<Self, Error> {
let yd = (lnglat.vlat / 20. * 8. * 10.) as u32 % 10;
let xd = (lnglat.vlng / 30. * 8. * 10.) as u32 % 10;
Ok(Self {
secondary: SecondaryCode::from_lnglat(lnglat)?,
y3: yd as u8,
x3: xd as u8,
})
}
#[inline]
pub fn y1(&self) -> u8 {
self.secondary.y1()
}
#[inline]
pub fn x1(&self) -> u8 {
self.secondary.x1()
}
#[inline]
pub fn y2(&self) -> u8 {
self.secondary.y2()
}
#[inline]
pub fn x2(&self) -> u8 {
self.secondary.x2()
}
#[inline]
pub fn y3(&self) -> u8 {
self.y3
}
#[inline]
pub fn x3(&self) -> u8 {
self.x3
}
#[inline]
pub fn primary(&self) -> PrimaryCode {
self.secondary.primary
}
#[inline]
pub fn secondary(&self) -> SecondaryCode {
self.secondary
}
pub fn iter_half(self) -> impl Iterator<Item = HalfCode> {
(1..=4).map(move |quad| HalfCode { parent: self, quad })
}
}
impl GridSquareCode for StandardCode {
fn envelope(&self) -> LngLatBox {
LngLatBox::new(
LngLat::new_raw(
((self.x1() as u32 + 100) * 30) as f64
+ self.x2() as f64 * 3.75
+ self.x3() as f64 * 0.375,
(self.y1() as u32 * 20) as f64 + self.y2() as f64 * 2.5 + self.y3() as f64 * 0.25,
),
LngLat::new_raw(
((self.x1() as u32 + 100) * 30) as f64
+ self.x2() as f64 * 3.75
+ (self.x3() + 1) as f64 * 0.375,
(self.y1() as u32 * 20) as f64
+ self.y2() as f64 * 2.5
+ (self.y3() + 1) as f64 * 0.25,
),
)
}
#[inline]
fn index_xy(&self) -> (u32, u32) {
let (px, py) = self.secondary.index_xy();
(px * 10 + self.x3 as u32, py * 10 + self.y3 as u32)
}
}
impl FromStr for StandardCode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 8 {
return Err(Error::InvalidCode);
}
let Some((secondary, rest)) = s.split_at_checked(6) else {
return Err(Error::InvalidCode);
};
let Some((y3_str, x3_str)) = rest.split_at_checked(1) else {
return Err(Error::InvalidCode);
};
let y3 = y3_str.parse::<u8>().map_err(|_| Error::InvalidCode)?;
let x3 = x3_str.parse::<u8>().map_err(|_| Error::InvalidCode)?;
Ok(Self {
secondary: SecondaryCode::from_str(secondary)?,
y3,
x3,
})
}
}
impl Display for StandardCode {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:02}{:02}{}{}{}{}",
self.secondary.y1(),
self.secondary.x1(),
self.secondary.y2(),
self.secondary.x2(),
self.y3,
self.x3
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Quad<P: GridSquareCode> {
parent: P,
quad: u8,
}
impl<P: GridSquareCode + Copy> Quad<P> {
pub fn new(parent: P, quad: u8) -> Self {
assert!(quad < 4);
Self { parent, quad }
}
pub fn iter_quad(self) -> impl Iterator<Item = Quad<Self>> {
(1..=4).map(move |quad| Quad::<Self> { parent: self, quad })
}
}
impl<P: GridSquareCode> GridSquareCode for Quad<P> {
fn envelope(&self) -> LngLatBox {
let envelope = self.parent.envelope();
let d = self.quad - 1;
envelope.split::<2>(d & 1, d >> 1)
}
#[inline]
fn index_xy(&self) -> (u32, u32) {
let (px, py) = self.parent.index_xy();
let x = (self.quad - 1) & 1;
let y = (self.quad - 1) >> 1;
(px * 2 + x as u32, py * 2 + y as u32)
}
}
impl<P: GridSquareCode + Display> Display for Quad<P> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.parent.fmt(f)?;
write!(f, "{}", self.quad)
}
}
pub type HalfCode = Quad<StandardCode>;
pub type QuarterCode = Quad<HalfCode>;
pub type EighthCode = Quad<QuarterCode>;
impl HalfCode {
pub fn from_int(code: u32) -> Result<Self, Error> {
let parent = StandardCode::from_int(code / 10)?;
let quad = (code % 10) as u8;
if !(1..=4).contains(&quad) {
return Err(Error::InvalidCode);
}
Ok(Self { parent, quad })
}
#[inline]
pub fn from_lnglat(lnglat: LngLat) -> Result<Self, Error> {
let yd = ((lnglat.vlat / 20. * 8. * 10. * 2.) as u64 % 2) as u8;
let xd = ((lnglat.vlng / 30. * 8. * 10. * 2.) as u64 % 2) as u8;
let quad = (yd << 1) + xd + 1;
Ok(Self {
parent: StandardCode::from_lnglat(lnglat)?,
quad,
})
}
pub fn y1(&self) -> u8 {
self.parent.y1()
}
pub fn x1(&self) -> u8 {
self.parent.x1()
}
pub fn y2(&self) -> u8 {
self.parent.y2()
}
pub fn x2(&self) -> u8 {
self.parent.x2()
}
pub fn y3(&self) -> u8 {
self.parent.y3()
}
pub fn x3(&self) -> u8 {
self.parent.x3()
}
pub fn quad1(&self) -> u8 {
self.quad
}
pub fn standard(&self) -> StandardCode {
self.parent
}
}
impl FromStr for HalfCode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 9 {
return Err(Error::InvalidCode);
}
let Some((standard, rest)) = s.split_at_checked(8) else {
return Err(Error::InvalidCode);
};
let quad = rest.parse::<u8>().map_err(|_| Error::InvalidCode)?;
if !(1..=4).contains(&quad) {
return Err(Error::InvalidCode);
}
Ok(Self {
parent: StandardCode::from_str(standard)?,
quad,
})
}
}
impl QuarterCode {
pub fn from_int(code: u64) -> Result<Self, Error> {
let parent = HalfCode::from_int((code / 10) as u32)?;
let quad = (code % 10) as u8;
if !(1..=4).contains(&quad) {
return Err(Error::InvalidCode);
}
Ok(Self { parent, quad })
}
#[inline]
pub fn from_lnglat(lnglat: LngLat) -> Result<Self, Error> {
let yd = ((lnglat.vlat / 20. * 8. * 10. * 4.) as u64 % 2) as u8;
let xd = ((lnglat.vlng / 30. * 8. * 10. * 4.) as u64 % 2) as u8;
let quad = (yd << 1) + xd + 1;
Ok(Self {
parent: HalfCode::from_lnglat(lnglat)?,
quad,
})
}
pub fn y1(&self) -> u8 {
self.parent.y1()
}
pub fn x1(&self) -> u8 {
self.parent.x1()
}
pub fn y2(&self) -> u8 {
self.parent.y2()
}
pub fn x2(&self) -> u8 {
self.parent.x2()
}
pub fn y3(&self) -> u8 {
self.parent.y3()
}
pub fn x3(&self) -> u8 {
self.parent.x3()
}
pub fn quad1(&self) -> u8 {
self.parent.quad1()
}
pub fn quad2(&self) -> u8 {
self.quad
}
pub fn standard(&self) -> StandardCode {
self.parent.parent
}
pub fn half(&self) -> HalfCode {
self.parent
}
}
impl FromStr for QuarterCode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 10 {
return Err(Error::InvalidCode);
}
let Some((half, rest)) = s.split_at_checked(9) else {
return Err(Error::InvalidCode);
};
let quad = rest.parse::<u8>().map_err(|_| Error::InvalidCode)?;
if !(1..=4).contains(&quad) {
return Err(Error::InvalidCode);
}
Ok(Self {
parent: HalfCode::from_str(half)?,
quad,
})
}
}
impl EighthCode {
pub fn from_int(code: u64) -> Result<Self, Error> {
let parent = QuarterCode::from_int(code / 10)?;
let quad = (code % 10) as u8;
if !(1..=4).contains(&quad) {
return Err(Error::InvalidCode);
}
Ok(Self { parent, quad })
}
#[inline]
pub fn from_lnglat(lnglat: LngLat) -> Result<Self, Error> {
let yd = ((lnglat.vlat / 20. * 8. * 10. * 8.) as u64 % 2) as u8;
let xd = ((lnglat.vlng / 30. * 8. * 10. * 8.) as u64 % 2) as u8;
let quad = (yd << 1) + xd + 1;
Ok(Self {
parent: QuarterCode::from_lnglat(lnglat)?,
quad,
})
}
pub fn y1(&self) -> u8 {
self.parent.y1()
}
pub fn x1(&self) -> u8 {
self.parent.x1()
}
pub fn y2(&self) -> u8 {
self.parent.y2()
}
pub fn x2(&self) -> u8 {
self.parent.x2()
}
pub fn y3(&self) -> u8 {
self.parent.y3()
}
pub fn x3(&self) -> u8 {
self.parent.x3()
}
pub fn quad1(&self) -> u8 {
self.parent.quad1()
}
pub fn quad2(&self) -> u8 {
self.parent.quad2()
}
pub fn quad3(&self) -> u8 {
self.quad
}
pub fn standard(&self) -> StandardCode {
self.parent.parent.parent
}
pub fn half(&self) -> HalfCode {
self.parent.parent
}
pub fn quarter(&self) -> QuarterCode {
self.parent
}
}
impl FromStr for EighthCode {
type Err = Error;
fn from_str(s: &str) -> Result<Self, Self::Err> {
if s.len() != 11 {
return Err(Error::InvalidCode);
}
let Some((half, rest)) = s.split_at_checked(10) else {
return Err(Error::InvalidCode);
};
let quad = rest.parse::<u8>().map_err(|_| Error::InvalidCode)?;
if !(1..=4).contains(&quad) {
return Err(Error::InvalidCode);
}
Ok(Self {
parent: QuarterCode::from_str(half)?,
quad,
})
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_primary_code() {
assert!(matches!(
LevelAndCode::from_int(9999),
Ok(LevelAndCode::Primary(_))
));
assert!(matches!(
LevelAndCode::from_str("9999"),
Ok(LevelAndCode::Primary(_))
));
let code = PrimaryCode::from_int(1234).unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
let code = PrimaryCode::from_str("1234").unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.to_string(), "1234");
PrimaryCode::from_str("12345").expect_err("must be 4 digits");
let code = PrimaryCode::from_int(6441).unwrap();
assert_eq!(
code.envelope(),
LngLatBox::new(
LngLat::new_raw(30. * 141.0, 20. * 64.),
LngLat::new_raw(30. * 142.0, 20. * 65.)
)
);
let code = PrimaryCode::from_lnglat(LngLat::new(141.99, 43.33)).unwrap();
assert_eq!(code.to_string(), "6441");
let code = PrimaryCode::from_lnglat(LngLat::new(142.01, 43.34)).unwrap();
assert_eq!(code.to_string(), "6542");
let envelope = code.envelope();
for child in code.iter_secondary() {
envelope.contains_box(&child.envelope());
}
let code = PrimaryCode::from_str("0102").unwrap();
assert_eq!(code.index_xy(), (2, 1));
}
#[test]
fn test_secondary_code() {
assert!(matches!(
LevelAndCode::from_int(123456),
Ok(LevelAndCode::Secondary(_))
));
let code = SecondaryCode::from_int(123456).unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
assert_eq!(code.to_string(), "123456");
assert_eq!(code.primary(), PrimaryCode::from_int(1234).unwrap());
let code = SecondaryCode::from_str("123456").unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
SecondaryCode::from_str("1234567").expect_err("must be 6 digits");
SecondaryCode::from_str("123488").expect_err("y2 and x2 must be less than 8");
let code = SecondaryCode::from_int(644142).unwrap();
assert_eq!(
code.envelope(),
LngLatBox::new(
LngLat::new_raw(30. * (141.0 + 2. / 8.), 20. * (64. + 4. / 8.)),
LngLat::new_raw(30. * (141.0 + 3. / 8.), 20. * (64. + 5. / 8.))
)
);
let code = SecondaryCode::from_lnglat(LngLat::new(141.87132, 43.24550)).unwrap();
assert_eq!(code.to_string(), "644166");
let code = SecondaryCode::from_lnglat(LngLat::new(141.88596, 43.25935)).unwrap();
assert_eq!(code.to_string(), "644177");
let envelope = code.envelope();
for child in code.iter_standard() {
envelope.contains_box(&child.envelope());
}
let code = SecondaryCode::from_str("010276").unwrap();
assert_eq!(code.index_xy(), (16 + 6, 8 + 7));
}
#[test]
fn test_standard_code() {
assert!(matches!(
LevelAndCode::from_int(12345678),
Ok(LevelAndCode::Standard(_))
));
assert!(matches!(
LevelAndCode::from_str("12345678"),
Ok(LevelAndCode::Standard(_))
));
let code = StandardCode::from_int(12345678).unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
assert_eq!(code.y3(), 7);
assert_eq!(code.x3(), 8);
assert_eq!(code.to_string(), "12345678");
assert_eq!(code.secondary(), SecondaryCode::from_int(123456).unwrap());
assert_eq!(code.primary(), PrimaryCode::from_int(1234).unwrap());
let code = StandardCode::from_str("12345678").unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
assert_eq!(code.y3(), 7);
assert_eq!(code.x3(), 8);
SecondaryCode::from_str("123456789").expect_err("must be 8 digits");
SecondaryCode::from_str("12348899").expect_err("y2 and x2 must be less than 8");
let code = StandardCode::from_int(64414278).unwrap();
assert_eq!(
code.envelope(),
LngLatBox::new(
LngLat::new_raw(
30. * 141.0 + 30. * (2. + 8. / 10.) / 8.,
20. * 64. + 20. * (4. + 7. / 10.) / 8.,
),
LngLat::new_raw(
30. * 141.0 + 30. * (2. + 9. / 10.) / 8.,
20. * 64. + 20. * (4. + 8. / 10.) / 8.,
),
)
);
let code = StandardCode::from_lnglat(LngLat::new(141.861882, 43.249259)).unwrap();
assert_eq!(code.to_string(), "64416698");
let envelope = code.envelope();
for child in code.iter_half() {
envelope.contains_box(&child.envelope());
}
let code = StandardCode::from_str("01027654").unwrap();
assert_eq!(code.index_xy(), (22 * 10 + 4, 15 * 10 + 5));
}
#[test]
fn test_half_code() {
assert!(matches!(
LevelAndCode::from_int(123456781),
Ok(LevelAndCode::Half(_))
));
assert!(matches!(
LevelAndCode::from_str("123456781"),
Ok(LevelAndCode::Half(_))
));
let code = HalfCode::from_int(123456781).unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
assert_eq!(code.y3(), 7);
assert_eq!(code.x3(), 8);
assert_eq!(code.quad1(), 1);
assert_eq!(code.to_string(), "123456781");
assert_eq!(code.standard(), StandardCode::from_int(12345678).unwrap());
let code2 = HalfCode::from_str("123456781").unwrap();
assert_eq!(code, code2);
let code = HalfCode::from_int(644142782).unwrap();
assert_eq!(
code.envelope(),
LngLatBox::new(
LngLat::new_raw(
30. * 141.0 + 30. * (2. + (8. + 0.5) / 10.) / 8.,
20. * 64. + 20. * (4. + 7. / 10.) / 8.,
),
LngLat::new_raw(
30. * (141.0 + (2. + 9. / 10.) / 8.),
20. * 64. + 20. * (4. + (7. + 0.5) / 10.) / 8.,
),
)
);
let code = HalfCode::from_lnglat(LngLat::new(141.8686782, 43.2405564)).unwrap();
assert_eq!(code.to_string(), "644166893");
let envelope = code.envelope();
for child in code.iter_quad() {
envelope.contains_box(&child.envelope());
}
let code = HalfCode::from_str("010276542").unwrap();
assert_eq!(code.index_xy(), ((22 * 10 + 4) * 2 + 1, (15 * 10 + 5) * 2));
let code = HalfCode::from_str("010276543").unwrap();
assert_eq!(code.index_xy(), ((22 * 10 + 4) * 2, (15 * 10 + 5) * 2 + 1));
}
#[test]
fn test_quarter_code() {
assert!(matches!(
LevelAndCode::from_int(1234567812),
Ok(LevelAndCode::Quarter(_))
));
assert!(matches!(
LevelAndCode::from_str("1234567812"),
Ok(LevelAndCode::Quarter(_))
));
let code = QuarterCode::from_int(1234567812).unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
assert_eq!(code.y3(), 7);
assert_eq!(code.x3(), 8);
assert_eq!(code.quad1(), 1);
assert_eq!(code.quad2(), 2);
assert_eq!(code.to_string(), "1234567812");
assert_eq!(code.standard(), StandardCode::from_int(12345678).unwrap());
assert_eq!(code.half(), HalfCode::from_int(123456781).unwrap());
let code2 = QuarterCode::from_str("1234567812").unwrap();
assert_eq!(code, code2);
let code = QuarterCode::from_int(6441427823).unwrap();
assert_eq!(
code.envelope(),
LngLatBox::new(
LngLat::new_raw(
30. * 141.0 + 30. * (2. + (8. + 0.5) / 10.) / 8.,
20. * 64. + 20. * (4. + (7. + 0.25) / 10.) / 8.,
),
LngLat::new_raw(
30. * 141.0 + 30. * (2. + (8. + 0.5 + 0.25) / 10.) / 8.,
20. * 64. + 20. * (4. + (7. + 0.5) / 10.) / 8.,
),
)
);
let code = QuarterCode::from_lnglat(LngLat::new(141.8686782, 43.2405564)).unwrap();
assert_eq!(code.to_string(), "6441668934");
let envelope = code.envelope();
for child in code.iter_quad() {
envelope.contains_box(&child.envelope());
}
let code = QuarterCode::from_str("0102765423").unwrap();
assert_eq!(
code.index_xy(),
(((22 * 10 + 4) * 2 + 1) * 2, ((15 * 10 + 5) * 2) * 2 + 1)
);
}
#[test]
fn test_eighth_code() {
assert!(matches!(
LevelAndCode::from_int(12345678123),
Ok(LevelAndCode::Eighth(_))
));
assert!(matches!(
LevelAndCode::from_str("12345678123"),
Ok(LevelAndCode::Eighth(_))
));
let code = EighthCode::from_int(12345678123).unwrap();
assert_eq!(code.y1(), 12);
assert_eq!(code.x1(), 34);
assert_eq!(code.y2(), 5);
assert_eq!(code.x2(), 6);
assert_eq!(code.y3(), 7);
assert_eq!(code.x3(), 8);
assert_eq!(code.quad1(), 1);
assert_eq!(code.quad2(), 2);
assert_eq!(code.quad3(), 3);
assert_eq!(code.to_string(), "12345678123");
assert_eq!(code.standard(), StandardCode::from_int(12345678).unwrap());
assert_eq!(code.half(), HalfCode::from_int(123456781).unwrap());
assert_eq!(code.quarter(), QuarterCode::from_int(1234567812).unwrap());
let code2 = EighthCode::from_str("12345678123").unwrap();
assert_eq!(code, code2);
let code = EighthCode::from_int(64414278234).unwrap();
assert_eq!(
code.envelope(),
LngLatBox::new(
LngLat::new_raw(
30. * 141.0 + 30. * (2. + (8. + 0.5 + 0.125) / 10.) / 8.,
20. * 64. + 20. * (4. + (7. + 0.25 + 0.125) / 10.) / 8.,
),
LngLat::new_raw(
30. * 141.0 + 30. * (2. + (8. + 0.5 + 0.25) / 10.) / 8.,
20. * 64. + 20. * (4. + (7. + 0.5) / 10.) / 8.,
),
)
);
let code = EighthCode::from_lnglat(LngLat::new(141.8686372, 43.2404931)).unwrap();
assert_eq!(code.to_string(), "64416689342");
assert_eq!(code.standard(), StandardCode::from_int(64416689).unwrap());
assert_eq!(code.half(), HalfCode::from_int(644166893).unwrap());
assert_eq!(code.quarter(), QuarterCode::from_int(6441668934).unwrap());
}
}