use crate::{
geometry::{
CellId, ConvertFeature, ConvertVectorFeatureS2, ConvertVectorFeatureWM, S2CellId,
SimplifyVectorGeometry, build_sq_dists, clip_features, convert,
},
parsers::FeatureReader,
};
use alloc::{
collections::{BTreeMap, BTreeSet},
string::{String, ToString},
vec,
vec::Vec,
};
use s2json::{
Axis, Face, Feature, JSONCollection, MValue, Projection, Properties, VectorFeature,
VectorGeometry, VectorPoint,
};
use serde::{Deserialize, Serialize};
pub trait HasLayer {
fn get_layer(&self) -> Option<String>;
}
impl HasLayer for () {
fn get_layer(&self) -> Option<String> {
None
}
}
impl HasLayer for MValue {
fn get_layer(&self) -> Option<String> {
let layer = self.get("layer");
match layer {
Some(l) => l.to_prim()?.to_string(),
_ => None,
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Tile<M = (), P: Clone + Default = Properties, D: Clone + Default = MValue> {
pub id: CellId,
pub layers: BTreeMap<String, Layer<M, P, D>>,
pub transformed: bool,
}
impl<M: HasLayer + Clone, P: Clone + Default, D: Clone + Default> Tile<M, P, D> {
pub fn new(id: CellId) -> Self {
Self { id, layers: BTreeMap::new(), transformed: false }
}
pub fn len(&self) -> usize {
self.layers.len()
}
pub fn is_empty(&self) -> bool {
for layer in self.layers.values() {
if !layer.features.is_empty() {
return false;
}
}
true
}
pub fn add_reader<R>(&mut self, reader: R, layer: Option<String>)
where
R: FeatureReader<M, P, D>,
{
for feature in reader.iter() {
self.add_feature(feature, layer.clone());
}
}
pub fn add_feature(&mut self, feature: VectorFeature<M, P, D>, layer: Option<String>) {
let layer_name = feature
.metadata
.as_ref()
.and_then(|meta| meta.get_layer()) .or(layer) .unwrap_or_else(|| "default".to_string());
let layer = self.layers.entry(layer_name.clone()).or_insert(Layer::new(layer_name));
layer.features.push(feature);
}
pub fn transform(&mut self, tolerance: f64, maxzoom: Option<u8>) {
if self.transformed || self.id.is_face() {
self.transformed = true;
return;
}
let (_, zoom, i, j) = self.id.to_face_ij();
for layer in self.layers.values_mut() {
for feature in layer.features.iter_mut() {
feature.geometry.simplify(tolerance, zoom, maxzoom);
feature.geometry.transform(zoom.into(), i as f64, j as f64)
}
}
self.transformed = true;
}
pub fn split(&mut self, buffer: Option<f64>) -> TileChildren<M, P, D> {
let buffer = buffer.unwrap_or(0.0625);
let (face, zoom, i, j) = self.id.to_face_ij();
let [bl_id, br_id, tl_id, tr_id] = S2CellId::children_ij(face, zoom, i, j);
let mut children = TileChildren {
bottom_left: Tile::new(bl_id),
bottom_right: Tile::new(br_id),
top_left: Tile::new(tl_id),
top_right: Tile::new(tr_id),
};
let scale = (1 << zoom) as f64;
let k1 = 0.;
let k2 = 0.5;
let k3 = 0.5;
let k4 = 1.;
let i = i as f64;
let j = j as f64;
let mut tl: Option<Vec<VectorFeature<M, P, D>>>;
let mut bl: Option<Vec<VectorFeature<M, P, D>>>;
let mut tr: Option<Vec<VectorFeature<M, P, D>>>;
let mut br: Option<Vec<VectorFeature<M, P, D>>>;
for (name, layer) in self.layers.iter_mut() {
let features = &layer.features;
let left = clip_features(features, scale, i - k1, i + k3, Axis::X, buffer);
let right = clip_features(features, scale, i + k2, i + k4, Axis::X, buffer);
if let Some(left) = left {
bl = clip_features(&left, scale, j - k1, j + k3, Axis::Y, buffer);
tl = clip_features(&left, scale, j + k2, j + k4, Axis::Y, buffer);
if let Some(bl) = bl {
for d in bl {
children.bottom_left.add_feature(d, Some(name.to_string()));
}
}
if let Some(tl) = tl {
for d in tl {
children.top_left.add_feature(d, Some(name.to_string()));
}
}
}
if let Some(right) = right {
br = clip_features(&right, scale, j - k1, j + k3, Axis::Y, buffer);
tr = clip_features(&right, scale, j + k2, j + k4, Axis::Y, buffer);
if let Some(br) = br {
for d in br {
children.bottom_right.add_feature(d, Some(name.to_string()));
}
}
if let Some(tr) = tr {
for d in tr {
children.top_right.add_feature(d, Some(name.to_string()));
}
}
}
}
children
}
}
#[derive(Debug)]
pub struct TileChildren<M = (), P: Clone + Default = Properties, D: Clone + Default = MValue> {
pub bottom_left: Tile<M, P, D>,
pub bottom_right: Tile<M, P, D>,
pub top_left: Tile<M, P, D>,
pub top_right: Tile<M, P, D>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct Layer<M = (), P: Clone + Default = Properties, D: Clone + Default = MValue> {
pub name: String,
pub features: Vec<VectorFeature<M, P, D>>,
}
impl<M, P: Clone + Default, D: Clone + Default> Layer<M, P, D> {
pub fn new(name: String) -> Self {
Self { name, features: vec![] }
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)]
pub struct TileStoreOptions {
pub projection: Option<Projection>,
pub minzoom: Option<u8>,
pub maxzoom: Option<u8>,
pub index_maxzoom: Option<u8>,
pub tolerance: Option<f64>,
pub buffer: Option<f64>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct TileStore<
M: HasLayer + Clone = (),
P: Clone + Default = Properties,
D: Clone + Default = MValue,
> {
pub minzoom: u8,
pub maxzoom: u8,
pub faces: BTreeSet<Face>,
pub index_maxzoom: u8,
pub tolerance: f64,
pub buffer: f64,
pub tiles: BTreeMap<CellId, Tile<M, P, D>>,
pub projection: Projection,
}
impl<M: HasLayer + Clone, P: Clone + Default, D: Clone + Default> Default for TileStore<M, P, D> {
fn default() -> Self {
Self {
minzoom: 0,
maxzoom: 14,
faces: BTreeSet::<Face>::new(),
index_maxzoom: 4,
tolerance: 3. / 4_096.,
buffer: 0.0625,
tiles: BTreeMap::<CellId, Tile<M, P, D>>::new(),
projection: Projection::S2,
}
}
}
impl<M: HasLayer + Clone, P: Clone + Default, D: Clone + Default> TileStore<M, P, D>
where
VectorFeature<M, P, D>: ConvertVectorFeatureWM<M, P, D> + ConvertVectorFeatureS2<M, P, D>,
Feature<M, P, D>: ConvertFeature<M, P, D>,
{
pub fn new(data: JSONCollection<M, P, D>, options: TileStoreOptions) -> Self {
let mut tile_store = Self {
minzoom: options.minzoom.unwrap_or(0),
maxzoom: options.maxzoom.unwrap_or(14),
faces: BTreeSet::<Face>::new(),
index_maxzoom: options.index_maxzoom.unwrap_or(4),
tolerance: options.tolerance.unwrap_or(3.) / 4_096.,
buffer: options.buffer.unwrap_or(64.),
tiles: BTreeMap::<CellId, Tile<M, P, D>>::new(),
projection: options.projection.unwrap_or(Projection::S2),
};
debug_assert!(
tile_store.minzoom <= tile_store.maxzoom
&& tile_store.maxzoom > 0
&& tile_store.maxzoom <= 20,
"maxzoom should be in the 0-20 range"
);
let features: Vec<VectorFeature<M, P, D>> =
convert(tile_store.projection, &data, Some(true), Some(true));
features.into_iter().for_each(|feature| tile_store.add_feature(feature));
for i in 0..6 {
tile_store.split_tile(CellId::from_face(i), None, None);
}
tile_store
}
fn add_feature(&mut self, mut feature: VectorFeature<M, P, D>) {
let face: u8 = feature.face.into();
let tile = self.tiles.entry(CellId::from_face(face)).or_insert_with(|| {
self.faces.insert(feature.face);
Tile::new(CellId::from_face(face))
});
build_sq_dists(&mut feature.geometry, self.tolerance, Some(self.maxzoom));
tile.add_feature(feature, None);
}
fn split_tile(&mut self, start_id: CellId, end_id: Option<CellId>, end_zoom: Option<u8>) {
let TileStore { buffer, tiles, tolerance, maxzoom, index_maxzoom, .. } = self;
let end_zoom = end_zoom.unwrap_or(*maxzoom);
let mut stack: Vec<CellId> = vec![start_id];
while !stack.is_empty() {
let stack_id = stack.pop();
if stack_id.is_none() {
break;
}
let tile = tiles.get_mut(&stack_id.unwrap());
if tile.is_none() {
continue;
}
let tile = tile.unwrap();
if tile.is_empty() || tile.transformed {
continue;
}
let tile_zoom = tile.id.level();
if tile_zoom >= *maxzoom || (end_id.is_none() && tile_zoom >= *index_maxzoom) || (end_id.is_some() && (tile_zoom > end_zoom || !tile.id.contains(end_id.unwrap())))
{
continue;
}
let TileChildren {
bottom_left: bl_id,
bottom_right: br_id,
top_left: tl_id,
top_right: tr_id,
} = tile.split(Some(*buffer));
tile.transform(*tolerance, Some(*maxzoom));
stack.extend(vec![bl_id.id, br_id.id, tl_id.id, tr_id.id]);
tiles.insert(bl_id.id, bl_id);
tiles.insert(br_id.id, br_id);
tiles.insert(tl_id.id, tl_id);
tiles.insert(tr_id.id, tr_id);
}
}
pub fn get_tile(&mut self, id: CellId) -> Option<&Tile<M, P, D>> {
let zoom = id.level();
let face = id.face();
if !(0..=20).contains(&zoom) || !self.faces.contains(&face.into()) {
return None;
}
let mut p_id = id;
while !self.tiles.contains_key(&p_id) && !p_id.is_face() {
p_id = p_id.parent(None);
}
self.split_tile(p_id, Some(id), Some(zoom));
self.tiles.get(&id)
}
}
pub trait TransformVectorGeometry<M: Clone + Default = MValue> {
fn transform(&mut self, zoom: f64, ti: f64, tj: f64);
}
impl<M: Clone + Default> TransformVectorGeometry<M> for VectorGeometry<M> {
fn transform(&mut self, zoom: f64, ti: f64, tj: f64) {
let zoom = (1 << (zoom as u64)) as f64;
match self {
VectorGeometry::Point(p) => p.coordinates.transform(zoom, ti, tj),
VectorGeometry::LineString(l) | VectorGeometry::MultiPoint(l) => {
l.coordinates.iter_mut().for_each(|p| p.transform(zoom, ti, tj))
}
VectorGeometry::MultiLineString(l) | VectorGeometry::Polygon(l) => l
.coordinates
.iter_mut()
.for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj))),
VectorGeometry::MultiPolygon(l) => l.coordinates.iter_mut().for_each(|p| {
p.iter_mut().for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj)))
}),
}
}
}
impl<M: Clone + Default> TransformVectorGeometry<M> for VectorPoint<M> {
fn transform(&mut self, zoom: f64, ti: f64, tj: f64) {
self.x = self.x * zoom - ti;
self.y = self.y * zoom - tj;
}
}