diff --git a/src/cells.rs b/src/cells.rs index c052cc1..c7be269 100644 --- a/src/cells.rs +++ b/src/cells.rs @@ -93,8 +93,33 @@ impl Cell { cell_to_parent(self, parent_res) } - // TODO: - // Add child and/or children + /// Return Cell's children. + pub fn children( + &self, + children_res: u8, + ) -> Result>, QuadbinError> { + let resolution = self.resolution(); + if children_res <= resolution || children_res > 26 { + return Err(QuadbinError::InvalidResolution(InvalidResolution::new( + children_res, + "Children resolution should be greater than the current resolution", + ))); + } + + let resolution_diff = children_res - resolution; + let block_range = (1 << (resolution_diff << 1)) as u64; + let block_shift = (52 - (children_res << 1)) as u64; + + let cell = self.get(); + + let child_base = (cell & !(0x1F << 52)) | ((children_res as u64) << 52); + let child_base = child_base & !((block_range - 1) << block_shift); + + Ok((0..block_range).map(move |x| { + let child = child_base | (x << block_shift); + Cell::try_from(child) + })) + } /// Find the Cell's neighbor in a specific [Direction]. /// diff --git a/src/test/cells.rs b/src/test/cells.rs index fa6f54d..1b0df0c 100644 --- a/src/test/cells.rs +++ b/src/test/cells.rs @@ -241,3 +241,69 @@ fn test_cell_neighbors() { .neighbors(); assert_eq!(nn[1], None) } + +#[test] +fn test_cell_children_one() { + let parent = Cell::new(5192650370358181887); + let kids = parent.children(1).unwrap(); + + let truth: [u64; 4] = [ + 5193776270265024511, + 5194902170171867135, + 5196028070078709759, + 5197153969985552383, + ]; + + for (i, cell_result) in kids.enumerate() { + let cell = cell_result.expect("cell index"); + assert_eq!(cell.get(), truth[i]); + } +} + +#[test] +fn test_cell_children_five() { + let parent = Cell::new(5209574053332910079); + let kids = parent.children(5).unwrap(); + + let truth: [u64; 4] = [ + 5214064458820747263, + 5214068856867258367, + 5214073254913769471, + 5214077652960280575, + ]; + + for (i, cell_result) in kids.enumerate() { + let cell = cell_result.expect("cell index"); + assert_eq!(cell.get(), truth[i]); + } +} + +#[test] +fn test_cell_children_six() { + let parent = Cell::new(5209574053332910079); + let kids = parent.children(6).unwrap(); + + let truth: [u64; 16] = [ + 5218564759913234431, + 5218565859424862207, + 5218566958936489983, + 5218568058448117759, + 5218569157959745535, + 5218570257471373311, + 5218571356983001087, + 5218572456494628863, + 5218573556006256639, + 5218574655517884415, + 5218575755029512191, + 5218576854541139967, + 5218577954052767743, + 5218579053564395519, + 5218580153076023295, + 5218581252587651071, + ]; + + for (i, cell_result) in kids.enumerate() { + let cell = cell_result.expect("cell index"); + assert_eq!(cell.get(), truth[i]); + } +} diff --git a/src/test/errors.rs b/src/test/errors.rs index 625efb4..c79f4ec 100644 --- a/src/test/errors.rs +++ b/src/test/errors.rs @@ -56,3 +56,19 @@ fn test_invalid_cell_index() { ); } } + +#[test] +fn test_invalid_child_res() { + let cell = Cell::try_from(5209574053332910079).expect("cell index"); + let kids = cell.children(3); + + assert!(kids.is_err()); + + assert_eq!( + kids.err(), + Some(QuadbinError::InvalidResolution(InvalidResolution::new( + 3, + "Children resolution should be greater than the current resolution" + ))) + ); +} diff --git a/src/test/geo.rs b/src/test/geo.rs index 924847c..57a53c5 100644 --- a/src/test/geo.rs +++ b/src/test/geo.rs @@ -36,6 +36,20 @@ fn test_invalid_resolution() { ); } +#[test] +fn test_invalid_multipoint_resolution() { + let orig = multi_point_2d(); + let res = 27; + let cell = Cell::from_multipoint(orig, res); + assert_eq!( + cell.filter_map(|i| i.err()).next(), + Some(QuadbinError::InvalidResolution(InvalidResolution::new( + res, + "Resolution should be between 0 and 26" + ))) + ); +} + #[test] fn test_cell_to_polygon() { let bbox = [22.5, -21.943045533438166, 45.0, 0.0];