use super::{IndexPoint, PointIndex};
use crate::{
data_store::{KV, KVStore, Vector, VectorStore},
geometry::{LonLat, S2CellId, S2Point},
parsers::{FeatureReader, RGBA},
util::{
GetInterpolateValue, Interpolatable, InterpolationFunction, InterpolationMethod,
get_interpolation,
},
};
use alloc::{collections::BTreeSet, fmt::Debug, string::String, vec, vec::Vec};
use libm::{floor, fmin, log2};
use s2json::{BBox, Face, GetXY, JSONCollection, Projection};
use serde::{Deserialize, Serialize, de::DeserializeOwned};
#[allow(unpredictable_function_pointer_comparisons)]
#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
pub struct GridOptions<V: Clone + Debug> {
pub projection: Option<Projection>,
pub layer_name: Option<String>,
pub minzoom: Option<u8>,
pub maxzoom: Option<u8>,
#[serde(skip)]
pub maxzoom_interpolation: Option<InterpolationMethod>,
#[serde(skip)]
pub interpolation: Option<InterpolationMethod>,
#[serde(skip)]
pub get_value: Option<GetInterpolateValue<IndexPoint<V>, V>>,
pub grid_size: Option<u32>,
pub buffer_size: Option<u32>,
}
#[derive(Debug)]
pub struct TileGrid<'a, V> {
pub name: String,
pub size: u32,
pub data: &'a Vec<V>,
}
pub type LocalPointGrid<V = RGBA> =
PointGrid<V, Vector<S2CellId, IndexPoint<V>>, KV<S2CellId, Vec<V>>>;
#[derive(Debug)]
pub struct PointGrid<
V: Interpolatable + Serialize + DeserializeOwned + Debug = RGBA,
S: VectorStore<S2CellId, IndexPoint<V>> = Vector<S2CellId, IndexPoint<V>>,
G: KVStore<S2CellId, Vec<V>> = KV<S2CellId, Vec<V>>,
> {
projection: Projection,
layer_name: String,
minzoom: u8,
maxzoom: u8,
buffer_size: u32,
maxzoom_interpolation: InterpolationFunction<S2Point, IndexPoint<V>, V>,
interpolation: InterpolationFunction<S2Point, IndexPoint<V>, V>,
get_value: GetInterpolateValue<IndexPoint<V>, V>,
grid_size: u32, point_index: PointIndex<V, S>,
grid_tile_store: G,
}
impl<
V: Interpolatable + Serialize + DeserializeOwned + Debug,
S: VectorStore<S2CellId, IndexPoint<V>>,
G: KVStore<S2CellId, Vec<V>>,
> PointGrid<V, S, G>
{
pub fn new(options: Option<GridOptions<V>>) -> Self {
let options = options.unwrap_or_default();
let maxzoom_interpolation =
options.maxzoom_interpolation.unwrap_or(InterpolationMethod::IDW);
let interpolation = options.interpolation.unwrap_or(InterpolationMethod::Lanczos);
let projection = options.projection.unwrap_or(Projection::S2);
PointGrid {
projection,
maxzoom_interpolation: get_interpolation(maxzoom_interpolation),
interpolation: get_interpolation(interpolation),
get_value: options.get_value.unwrap_or(|v| v.data),
buffer_size: options.buffer_size.unwrap_or(0),
minzoom: options.minzoom.unwrap_or(0),
maxzoom: options.maxzoom.unwrap_or(16),
layer_name: options.layer_name.unwrap_or(String::from("default")),
grid_size: options.grid_size.unwrap_or(512),
point_index: PointIndex::new(None, Some(projection)),
grid_tile_store: G::new(None),
}
}
pub fn insert_point<T: GetXY>(&mut self, point: T, data: Option<V>) {
self.point_index.insert_point(point, data);
}
pub fn insert_lon_lat(&mut self, ll: LonLat<V>) {
self.point_index.insert_lon_lat(ll);
}
pub fn insert_face_st(&mut self, face: Face, s: f64, t: f64, data: V) {
self.point_index.insert_face_st(face, s, t, data);
}
pub fn insert_reader<L: Clone, F: FeatureReader<L, V, V>>(&mut self, reader: &F) {
self.point_index.insert_reader::<L, F>(reader);
}
pub fn insert_feature<L: Clone>(&mut self, data: JSONCollection<L, V, V>) {
self.point_index.insert_feature::<L>(data);
}
pub fn get_tile(&self, id: S2CellId) -> Option<TileGrid<'_, V>> {
let Self { layer_name, grid_size, buffer_size, .. } = self;
let data = self.grid_tile_store.get(id);
if let Some(data) = data {
return Some(TileGrid {
name: layer_name.clone(),
size: grid_size + buffer_size * 2,
data,
});
}
None
}
pub fn build_clusters(&mut self) {
let mut parents = self.cluster_maxzoom();
for zoom in (self.minzoom..self.maxzoom).rev() {
parents = self.custer_zoom(zoom, &parents);
}
}
fn cluster_maxzoom(&mut self) -> BTreeSet<S2CellId> {
let mut parents = BTreeSet::<S2CellId>::new();
let grid_length = (self.grid_size + self.buffer_size * 2) as usize;
let buffer_size_f64 = self.buffer_size as f64;
let grid_size_f64: f64 = self.grid_size as f64;
let zoom_grid_level =
fmin(self.maxzoom as f64 + floor(log2(grid_size_f64)) - 1., 30.) as u8;
self.point_index.sort();
for (cell, _) in self.point_index.iter() {
let maxzoom_id = cell.parent(Some(self.maxzoom)); if self.grid_tile_store.has(maxzoom_id) {
continue;
}
let face = cell.face();
let BBox { left: s_min, bottom: t_min, right: s_max, top: t_max } =
maxzoom_id.bounds_st(Some(self.maxzoom));
let s_pixel = (s_max - s_min) / grid_size_f64;
let t_pixel = (t_max - t_min) / grid_size_f64;
let s_start = s_min - s_pixel * buffer_size_f64;
let t_start = t_min - t_pixel * buffer_size_f64;
let mut grid: Vec<V> = vec![V::default(); grid_length * grid_length];
for y in 0..grid_length {
for x in 0..grid_length {
let t = t_start + (y as f64) * t_pixel;
let mut s = s_start + (x as f64) * s_pixel;
if self.projection == Projection::WG {
s = (s + 1.) % 1.;
} let mut grid_level_search = zoom_grid_level;
let mut point_shapes: Vec<IndexPoint<V>>;
let fst_point = S2CellId::from_face_st(face, s, t);
let mut st_cell = fst_point.parent(Some(zoom_grid_level));
loop {
point_shapes = self
.point_index
.search_range(st_cell, None, None)
.into_iter()
.map(|(_, point)| point.clone())
.collect();
if !point_shapes.is_empty()
|| grid_level_search <= zoom_grid_level - 3
|| grid_level_search == 0
{
break;
}
grid_level_search -= 1;
st_cell = st_cell.parent(Some(grid_level_search));
}
if point_shapes.is_empty() {
continue;
}
let point: S2Point = fst_point.to_point();
grid[y * grid_length + x] =
(self.maxzoom_interpolation)(&point, &point_shapes, self.get_value);
}
}
self.grid_tile_store.set(maxzoom_id, grid);
if self.maxzoom != 0 {
parents.insert(maxzoom_id.parent(Some(self.maxzoom - 1)));
}
}
parents
}
fn custer_zoom(&mut self, zoom: u8, cells: &BTreeSet<S2CellId>) -> BTreeSet<S2CellId> {
let mut parents = BTreeSet::<S2CellId>::new();
let grid_length = (self.grid_size + self.buffer_size * 2) as usize;
let half_grid_length = grid_length / 2;
for cell in cells {
let mut grid: Vec<V> = vec![V::default(); grid_length * grid_length];
let (face, cell_zoom, i, j) = cell.to_face_ij();
let [bl_id, br_id, tl_id, tr_id] = S2CellId::children_ij(face, cell_zoom, i, j);
self.downsample_grid(bl_id, &mut grid, 0, 0);
self.downsample_grid(br_id, &mut grid, half_grid_length, 0);
self.downsample_grid(tl_id, &mut grid, 0, half_grid_length);
self.downsample_grid(tr_id, &mut grid, half_grid_length, half_grid_length);
self.grid_tile_store.set(*cell, grid);
if zoom != 0 {
parents.insert(cell.parent(Some(zoom - 1)));
}
}
parents
}
fn downsample_grid(&self, cell_id: S2CellId, target: &mut [V], x: usize, y: usize) {
let grid = if let Some(grid) = self.grid_tile_store.get(cell_id) {
grid
} else {
return;
};
let Self { grid_size, buffer_size, .. } = self;
let grid_length = (grid_size + buffer_size * 2) as usize;
let half_grid_length = grid_length / 2;
let mid_point = S2Point::new(0.5, 0.5, 0.);
let null_val = V::default();
for j in 0..half_grid_length {
for i in 0..half_grid_length {
let base_j = j * 2;
let base_i = i * 2;
let source_points: Vec<IndexPoint<V>> = [
((0.0, 0.0), base_j * grid_length + base_i),
((1.0, 0.0), base_j * grid_length + base_i + 1),
((0.0, 1.0), (base_j + 1) * grid_length + base_i),
((1.0, 1.0), (base_j + 1) * grid_length + base_i + 1),
]
.into_iter()
.filter_map(|((x, y), idx)| {
let value = grid[idx];
if value != null_val {
Some(IndexPoint::new(S2Point::new(x, y, 0.0), value))
} else {
None
}
})
.collect();
if source_points.is_empty() {
continue;
}
target[(j + y) * grid_length + (i + x)] =
(self.interpolation)(&mid_point, &source_points, self.get_value);
}
}
}
}