diff --git a/docs/ALGORITHM.adoc b/docs/ALGORITHM.adoc index f7f5de1..6628fae 100644 --- a/docs/ALGORITHM.adoc +++ b/docs/ALGORITHM.adoc @@ -10,6 +10,10 @@ = Algorithm +The goal of this document is to define the algorithm. + +If you want to see the steps to get to this definition, go to link:DESIGN.html[DESIGN]. + == Data format [source,title='this_algorithm and S2 CellID Format'] @@ -21,9 +25,9 @@ WORD0 (13 bits) : | | |vvvvvv vvvvvvv 0000 (10 bits) : | | | |vvvvvvvvv v Not represented : | | | | | : | | | | | -Bit : 64 52 48 39 32 26 16 1 - : | | | | | | | | - : 0100101110101000 1011100010010011 1001001100100l00 1100000000000000 +Bit : 63 51 48 38 32 25 16| 0 + : | | | | | | | | | + : 0100101110101000 1011100010010011 1001001100100100 1100000000000000 === S2 CellID Format === | | || | Face number : ^^^ || | Data bits : ^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^ ^| | diff --git a/docs/DESIGN.adoc b/docs/DESIGN.adoc index ad9b78b..6afbbc5 100644 --- a/docs/DESIGN.adoc +++ b/docs/DESIGN.adoc @@ -12,6 +12,8 @@ The goal of this document is to walk through how the design was chosen. +If you want to see the algorithm definition, go to link:ALGORITHM.html[ALGORITHM]. + == 10,000 meter view This project allows anyone to address ~1 square meter of land by bringing together: @@ -423,7 +425,7 @@ data_bits = level * 2 = 23 * 2 = 46 Bit : 64 48 32 16 1 : | | | | | - : 01001011101010001011100010010011 1001001100100l001100000000000000 + : 01001011101010001011100010010011 10010011001001001100000000000000 Face number : ^^^ Data bits : ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^^ Bit after data bits (1) : ^ @@ -486,7 +488,7 @@ Data bits : vvvvvvvvvvvvvvvvvvvvvvvvvvvvv vvvvvvvvvvvvvvvvv Face number : vvv Bit : 64 48 32 16 1 : | | | | | - : 01001011101010001011100010010011 1001001100100l001100000000000000 + : 01001011101010001011100010010011 10010011001001001100000000000000 Not represented : ^^^^^^^^^^^^^^^ 0000 (10 bits) : ^^^^^^^^^^ WORD0 (13 bits) : ^^^^^^ ^^^^^^^ diff --git a/this_algorithm/src/conversions.rs b/this_algorithm/src/conversions.rs new file mode 100644 index 0000000..f888c56 --- /dev/null +++ b/this_algorithm/src/conversions.rs @@ -0,0 +1,34 @@ +use std::ops::RangeInclusive; + +use s2::{cellid::CellID, latlng::LatLng}; + +pub fn lat_lon_to_cellid(lat: f64, lon: f64) -> CellID { + Into::::into(LatLng::from_degrees(lat, lon)) +} + +pub(crate) fn extract_binary(number: u64, range: RangeInclusive) -> u64 { + (number >> range.start()) & (u64::MAX >> (63 - (range.end() - range.start()))) +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_extract_binary() { + assert_eq!(extract_binary(0b0, 0..=0), 0b0); + assert_eq!(extract_binary(0b11_1111_11, 2..=5), 0b1111); + assert_eq!(extract_binary(0b11_0101_11, 2..=5), 0b0101); + assert_eq!(extract_binary(0b11_1100_11, 2..=5), 0b1100); + assert_eq!(extract_binary(0b111_10011_, 0..=4), 0b10011); + assert_eq!(extract_binary(0b111100_1_1, 1..=1), 0b1); + assert_eq!(extract_binary(0b111100_0_1, 1..=1), 0b0); + + // A full test from the docs + let cell_id = 0b0100101110101000_1011100010010011_1001001100100100_1100000000000000_u64; + assert_eq!(extract_binary(cell_id, 15..=24), 0b10_01001001); + assert_eq!(extract_binary(cell_id, 25..=37), 0b01001_11001001); + assert_eq!(extract_binary(cell_id, 38..=50), 0b00010_11100010); + assert_eq!(extract_binary(cell_id, 51..=63), 0b01001_01110101); + } +} diff --git a/this_algorithm/src/lib.rs b/this_algorithm/src/lib.rs index dc009cb..c65b501 100644 --- a/this_algorithm/src/lib.rs +++ b/this_algorithm/src/lib.rs @@ -2,14 +2,18 @@ #![allow(clippy::cast_possible_truncation, clippy::multiple_crate_versions)] #![allow(unused_imports)] +pub mod v0; +use conversions::lat_lon_to_cellid; +pub use s2::s1::angle::Angle; use std::{ fmt::Display, ops::{Add, AddAssign}, str::FromStr, }; +mod conversions; use thiserror::Error; -use s2::cellid::CellID; +use s2::{cellid::CellID, latlng::LatLng, s1::Deg}; use words::Word; pub type Number = u32; @@ -92,6 +96,24 @@ impl FromStr for Address<'_> { } impl Address<'_> { + pub fn from_lat_lon(lat: f64, lon: f64) -> Self { + let cellid = conversions::lat_lon_to_cellid(lat, lon); + + v0::UnpackedCellID::from(cellid).into() + + // Self::cellid_to_v0(&cellid) + } + + // fn cellid_to_v0(cell_id: &CellID) -> Self { + // // The raw binary representation of the cellid + // let raw_cellid = cell_id.0; + + // // Self { + // // number, + // // words: words.into_inner().unwrap(), + // // } + // } + fn parse_v0(number: Number, word_components: Vec<&str>) -> Result { if !(V0_MIN_NUMBER..=V0_MAX_NUMBER).contains(&number) { return Err(Error::NumberOutOfRange(number)); diff --git a/this_algorithm/src/v0.rs b/this_algorithm/src/v0.rs new file mode 100644 index 0000000..ed2c68c --- /dev/null +++ b/this_algorithm/src/v0.rs @@ -0,0 +1,41 @@ +use crate::{conversions, Address}; +use std::ops::RangeInclusive; + +use s2::{cell::Cell, cellid::CellID}; + +pub struct UnpackedCellID { + pub number_bits: u16, + pub word0_bits: u16, + pub word1_bits: u16, + pub word2_bits: u16, +} + +impl From for UnpackedCellID { + fn from(cell_id: CellID) -> Self { + Self::from(cell_id.0) + } +} + +impl From for UnpackedCellID { + fn from(cell_id: u64) -> Self { + Self { + number_bits: conversions::extract_binary(cell_id, 15..=24) as u16, + word0_bits: conversions::extract_binary(cell_id, 25..=37) as u16, + word1_bits: conversions::extract_binary(cell_id, 38..=50) as u16, + word2_bits: conversions::extract_binary(cell_id, 51..=63) as u16, + } + } +} + +impl From for Address<'_> { + fn from(unpacked_cell_id: UnpackedCellID) -> Self { + let word0 = words::NUMBER_TO_WORDS[unpacked_cell_id.word0_bits as usize][0]; + let word1 = words::NUMBER_TO_WORDS[unpacked_cell_id.word1_bits as usize][0]; + let word2 = words::NUMBER_TO_WORDS[unpacked_cell_id.word2_bits as usize][0]; + + Self { + number: unpacked_cell_id.number_bits as u32, + words: [word0, word1, word2], + } + } +}