use super::{TileBBox, TileCoord2, TileCoord3};
use std::fmt;
const MAX_ZOOM_LEVEL: u64 = 32;
#[derive(Clone)]
pub struct TileBBoxPyramide {
level_bbox: [TileBBox; MAX_ZOOM_LEVEL as usize],
}
#[allow(dead_code)]
impl TileBBoxPyramide {
pub fn new_full() -> TileBBoxPyramide {
TileBBoxPyramide {
level_bbox: std::array::from_fn(|z| TileBBox::new_full(z as u64)),
}
}
pub fn new_empty() -> TileBBoxPyramide {
TileBBoxPyramide {
level_bbox: std::array::from_fn(|_z| TileBBox::new_empty()),
}
}
pub fn limit_by_geo_bbox(&mut self, geo_bbox: &[f32; 4]) {
for (level, bbox) in self.level_bbox.iter_mut().enumerate() {
bbox.intersect_bbox(&TileBBox::from_geo(geo_bbox, level as u64));
}
}
pub fn intersect(&mut self, other_bbox_pyramide: &TileBBoxPyramide) {
for (level, bbox) in self.level_bbox.iter_mut().enumerate() {
let other_bbox = other_bbox_pyramide.get_level_bbox(level as u64);
bbox.intersect_bbox(other_bbox);
}
}
pub fn get_level_bbox(&self, level: u64) -> &TileBBox {
&self.level_bbox[level as usize]
}
pub fn set_level_bbox(&mut self, level: u64, bbox: TileBBox) {
self.level_bbox[level as usize] = bbox;
}
pub fn include_coord(&mut self, coord: &TileCoord3) {
self.level_bbox[coord.z as usize].include_tile(coord.x, coord.y);
}
pub fn include_bbox(&mut self, level: u64, bbox: &TileBBox) {
self.level_bbox[level as usize].union_bbox(bbox);
}
pub fn iter_levels(&self) -> impl Iterator<Item = (u64, &TileBBox)> {
self
.level_bbox
.iter()
.enumerate()
.filter_map(|(level, bbox)| {
if bbox.is_empty() {
None
} else {
Some((level as u64, bbox))
}
})
}
pub fn iter_tile_indexes(&self) -> impl Iterator<Item = TileCoord3> + '_ {
return self.level_bbox.iter().enumerate().flat_map(|(z, bbox)| {
bbox
.iter_coords()
.map(move |TileCoord2 { x, y }| TileCoord3 { x, y, z: z as u64 })
});
}
pub fn get_zoom_min(&self) -> Option<u64> {
self
.level_bbox
.iter()
.enumerate()
.find(|(_level, bbox)| !bbox.is_empty())
.map(|(level, _bbox)| level as u64)
}
pub fn get_zoom_max(&self) -> Option<u64> {
self
.level_bbox
.iter()
.enumerate()
.rev()
.find(|(_level, bbox)| !bbox.is_empty())
.map(|(level, _bbox)| level as u64)
}
pub fn set_zoom_min(&mut self, zoom_level_min: u64) {
for (index, bbox) in self.level_bbox.iter_mut().enumerate() {
let level = index as u64;
if level < zoom_level_min {
bbox.set_empty();
}
}
}
pub fn set_zoom_max(&mut self, zoom_level_max: u64) {
for (index, bbox) in self.level_bbox.iter_mut().enumerate() {
let level = index as u64;
if level > zoom_level_max {
bbox.set_empty();
}
}
}
pub fn count_tiles(&self) -> u64 {
return self.level_bbox.iter().map(|bbox| bbox.count_tiles()).sum();
}
pub fn is_empty(&self) -> bool {
self.level_bbox.iter().all(|bbox| bbox.is_empty())
}
pub fn is_full(&self) -> bool {
self
.level_bbox
.iter()
.enumerate()
.all(|(i, bbox)| bbox.is_full(i as u64))
}
}
impl fmt::Debug for TileBBoxPyramide {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_list()
.entries(
self
.iter_levels()
.map(|(level, bbox)| format!("{level}: {bbox:?}")),
)
.finish()
}
}
impl PartialEq for TileBBoxPyramide {
fn eq(&self, other: &Self) -> bool {
for i in 0..MAX_ZOOM_LEVEL {
let bbox0 = self.get_level_bbox(i);
let bbox1 = other.get_level_bbox(i);
if bbox0.is_empty() != bbox1.is_empty() {
return false;
}
if bbox0.is_empty() {
continue;
}
if bbox0 != bbox1 {
return false;
}
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn intersection_tests() {
let mut pyramide1 = TileBBoxPyramide::new_empty();
pyramide1.intersect(&TileBBoxPyramide::new_empty());
assert!(pyramide1.is_empty());
let mut pyramide1 = TileBBoxPyramide::new_full();
pyramide1.intersect(&TileBBoxPyramide::new_empty());
assert!(pyramide1.is_empty());
let mut pyramide1 = TileBBoxPyramide::new_empty();
pyramide1.intersect(&TileBBoxPyramide::new_full());
assert!(pyramide1.is_empty());
let mut pyramide1 = TileBBoxPyramide::new_full();
pyramide1.intersect(&TileBBoxPyramide::new_full());
assert!(pyramide1.is_full());
}
#[test]
fn limit_by_geo_bbox() {
let mut pyramide = TileBBoxPyramide::new_full();
pyramide.set_zoom_max(8);
pyramide.limit_by_geo_bbox(&[8.0653f32, 51.3563f32, 12.3528f32, 52.2564f32]);
assert_eq!(pyramide.get_level_bbox(0), &TileBBox::new(0, 0, 0, 0));
assert_eq!(pyramide.get_level_bbox(1), &TileBBox::new(1, 0, 1, 0));
assert_eq!(pyramide.get_level_bbox(2), &TileBBox::new(2, 1, 2, 1));
assert_eq!(pyramide.get_level_bbox(3), &TileBBox::new(4, 2, 4, 2));
assert_eq!(pyramide.get_level_bbox(4), &TileBBox::new(8, 5, 8, 5));
assert_eq!(pyramide.get_level_bbox(5), &TileBBox::new(16, 10, 17, 10));
assert_eq!(pyramide.get_level_bbox(6), &TileBBox::new(33, 21, 34, 21));
assert_eq!(pyramide.get_level_bbox(7), &TileBBox::new(66, 42, 68, 42));
assert_eq!(pyramide.get_level_bbox(8), &TileBBox::new(133, 84, 136, 85));
}
#[test]
fn include_coord() {
let mut pyramide = TileBBoxPyramide::new_empty();
pyramide.include_coord(&TileCoord3::new(1, 2, 3));
pyramide.include_coord(&TileCoord3::new(4, 5, 3));
pyramide.include_coord(&TileCoord3::new(6, 7, 8));
assert!(pyramide.get_level_bbox(0).is_empty());
assert!(pyramide.get_level_bbox(1).is_empty());
assert!(pyramide.get_level_bbox(2).is_empty());
assert_eq!(pyramide.get_level_bbox(3), &TileBBox::new(1, 2, 4, 5));
assert!(pyramide.get_level_bbox(4).is_empty());
assert!(pyramide.get_level_bbox(5).is_empty());
assert!(pyramide.get_level_bbox(6).is_empty());
assert!(pyramide.get_level_bbox(7).is_empty());
assert_eq!(pyramide.get_level_bbox(8), &TileBBox::new(6, 7, 6, 7));
assert!(pyramide.get_level_bbox(9).is_empty());
}
#[test]
fn include_bbox() {
let mut pyramide = TileBBoxPyramide::new_empty();
pyramide.include_bbox(4, &TileBBox::new(1, 2, 3, 4));
pyramide.include_bbox(4, &TileBBox::new(5, 6, 7, 8));
assert!(pyramide.get_level_bbox(0).is_empty());
assert!(pyramide.get_level_bbox(1).is_empty());
assert!(pyramide.get_level_bbox(2).is_empty());
assert!(pyramide.get_level_bbox(3).is_empty());
assert_eq!(pyramide.get_level_bbox(4), &TileBBox::new(1, 2, 7, 8));
assert!(pyramide.get_level_bbox(5).is_empty());
assert!(pyramide.get_level_bbox(6).is_empty());
assert!(pyramide.get_level_bbox(7).is_empty());
assert!(pyramide.get_level_bbox(8).is_empty());
assert!(pyramide.get_level_bbox(9).is_empty());
}
#[test]
fn level_bbox() {
let test = |z0: u64, z1: u64| {
let mut pyramide = TileBBoxPyramide::new_empty();
let bbox = TileBBox::new_full(z0);
pyramide.set_level_bbox(z1, bbox.clone());
assert_eq!(pyramide.get_level_bbox(z1).clone(), bbox);
};
test(0, 1);
test(0, 30);
test(30, 30);
}
#[test]
fn zoom_min_max() {
let test = |z0: u64, z1: u64| {
let mut pyramide = TileBBoxPyramide::new_full();
pyramide.set_zoom_min(z0);
pyramide.set_zoom_max(z1);
assert_eq!(pyramide.get_zoom_min().unwrap(), z0);
assert_eq!(pyramide.get_zoom_max().unwrap(), z1);
};
test(0, 1);
test(0, 30);
test(30, 30);
}
}