1use alloc::collections::{BTreeMap, BTreeSet};
2use alloc::string::String;
3use alloc::string::ToString;
4use alloc::vec;
5use alloc::vec::Vec;
6
7use libm::round;
8
9use crate::{
10    convert, CellId, Face, JSONCollection, Projection, TileChildren, VectorFeature, VectorGeometry,
11    VectorPoint,
12};
13
14pub trait HasLayer {
16    fn get_layer(&self) -> Option<String>;
18}
19impl HasLayer for () {
20    fn get_layer(&self) -> Option<String> {
21        None
22    }
23}
24
25pub struct Tile<M> {
27    pub id: CellId,
29    pub layers: BTreeMap<String, Layer<M>>,
31    pub transformed: bool,
33}
34impl<M: HasLayer + Clone> Tile<M> {
35    pub fn new(id: CellId) -> Self {
37        Self { id, layers: BTreeMap::new(), transformed: false }
38    }
39
40    pub fn is_empty(&self) -> bool {
42        for layer in self.layers.values() {
43            if !layer.features.is_empty() {
44                return false;
45            }
46        }
47
48        true
49    }
50
51    pub fn add_feature(&mut self, feature: VectorFeature<M>, layer: Option<String>) {
53        let layer_name = feature
54            .metadata
55            .as_ref()
56            .and_then(|meta| meta.get_layer()) .or(layer) .unwrap_or_else(|| "default".to_string()); if !self.layers.contains_key(&layer_name) {
61            self.layers.insert(layer_name.clone(), Layer::new(layer_name.clone()));
62        }
63        self.layers.get_mut(&layer_name).unwrap().features.push(feature);
64    }
65
66    pub fn transform(&mut self, tolerance: f64, maxzoom: Option<u8>) {
70        if self.transformed {
71            return;
72        }
73        let (zoom, i, j) = self.id.to_zoom_ij(None);
74
75        for layer in self.layers.values_mut() {
76            for feature in layer.features.iter_mut() {
77                feature.geometry.simplify(tolerance, zoom, maxzoom);
78                feature.geometry.transform(zoom, i as f64, j as f64)
79            }
80        }
81
82        self.transformed = true;
83    }
84}
85
86pub struct Layer<M> {
88    pub name: String,
90    pub features: Vec<VectorFeature<M>>,
92}
93impl<M> Layer<M> {
94    pub fn new(name: String) -> Self {
96        Self { name, features: vec![] }
97    }
98}
99
100pub struct TileStoreOptions {
102    pub projection: Option<Projection>,
104    pub minzoom: Option<u8>,
106    pub maxzoom: Option<u8>,
108    pub index_maxzoom: Option<u8>,
110    pub tolerance: Option<f64>,
112    pub buffer: Option<f64>,
114}
115
116pub struct TileStore<M: HasLayer + Clone> {
118    minzoom: u8,                      maxzoom: u8,                      faces: BTreeSet<Face>, index_maxzoom: u8,     tolerance: f64,        buffer: f64,           tiles: BTreeMap<CellId, Tile<M>>, projection: Projection, }
127impl<M: HasLayer + Clone> Default for TileStore<M> {
128    fn default() -> Self {
129        Self {
130            minzoom: 0,
131            maxzoom: 18,
132            faces: BTreeSet::<Face>::new(),
133            index_maxzoom: 4,
134            tolerance: 3.,
135            buffer: 0.0625,
136            tiles: BTreeMap::<CellId, Tile<M>>::new(),
137            projection: Projection::S2,
138        }
139    }
140}
141impl<M: HasLayer + Clone> TileStore<M> {
142    pub fn new(data: JSONCollection<M>, options: TileStoreOptions) -> Self {
144        let mut tile_store = Self {
145            minzoom: options.minzoom.unwrap_or(0),
146            maxzoom: options.maxzoom.unwrap_or(20),
147            faces: BTreeSet::<Face>::new(),
148            index_maxzoom: options.index_maxzoom.unwrap_or(4),
149            tolerance: options.tolerance.unwrap_or(3.),
150            buffer: options.buffer.unwrap_or(64.),
151            tiles: BTreeMap::<CellId, Tile<M>>::new(),
152            projection: options.projection.unwrap_or(Projection::S2),
153        };
154        debug_assert!(
156            tile_store.minzoom <= tile_store.maxzoom
157                && tile_store.maxzoom > 0
158                && tile_store.maxzoom <= 20,
159            "maxzoom should be in the 0-20 range"
160        );
161        let features: Vec<VectorFeature<M>> = convert(
163            tile_store.projection,
164            &data,
165            Some(tile_store.tolerance),
166            Some(tile_store.maxzoom),
167            None,
168        );
169        features.into_iter().for_each(|feature| tile_store.add_feature(feature));
170        for i in 0..6 {
171            tile_store.split_tile(CellId::from_face(i), None, None);
172        }
173
174        tile_store
175    }
176
177    pub fn add_feature(&mut self, feature: VectorFeature<M>) {
179        let face: u8 = feature.face.into();
180        let tile = self.tiles.entry(CellId::from_face(face)).or_insert_with(|| {
181            self.faces.insert(feature.face);
182            Tile::new(CellId::from_face(face))
183        });
184
185        tile.add_feature(feature, None);
186    }
187
188    fn split_tile(&mut self, start_id: CellId, end_id: Option<CellId>, end_zoom: Option<u8>) {
190        let TileStore { buffer, tiles, tolerance, maxzoom, index_maxzoom, .. } = self;
191        let end_zoom = end_zoom.unwrap_or(*maxzoom);
192        let mut stack: Vec<CellId> = vec![start_id];
193        while !stack.is_empty() {
195            let stack_id = stack.pop();
197            if stack_id.is_none() {
198                break;
199            }
200            let tile = tiles.get_mut(&stack_id.unwrap());
201            if tile.is_none() {
203                continue;
204            }
205            let tile = tile.unwrap();
206            if tile.is_empty() || tile.transformed {
207                continue;
208            }
209            let tile_zoom = tile.id.level();
210            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())))
216            {
217                continue;
218            }
219
220            let TileChildren {
222                bottom_left: bl_id,
223                bottom_right: br_id,
224                top_left: tl_id,
225                top_right: tr_id,
226            } = tile.split(Some(*buffer));
227            tile.transform(*tolerance, Some(*maxzoom));
229            stack.extend(vec![bl_id.id, br_id.id, tl_id.id, tr_id.id]);
231            tiles.insert(bl_id.id, bl_id);
233            tiles.insert(br_id.id, br_id);
234            tiles.insert(tl_id.id, tl_id);
235            tiles.insert(tr_id.id, tr_id);
236        }
237    }
238
239    pub fn get_tile(&mut self, id: CellId) -> Option<&Tile<M>> {
241        let zoom = id.level();
242        let face = id.face();
243        if !(0..=20).contains(&zoom) || !self.faces.contains(&face.into()) {
245            return None;
246        }
247
248        let mut p_id = id;
250        while !self.tiles.contains_key(&p_id) && !p_id.is_face() {
251            p_id = p_id.parent(None);
252        }
253        self.split_tile(p_id, Some(id), Some(zoom));
255
256        self.tiles.get(&id)
258    }
259}
260
261impl VectorGeometry {
262    pub fn transform(&mut self, zoom: u8, ti: f64, tj: f64) {
264        let zoom = (1 << (zoom as u64)) as f64;
265        match self {
266            VectorGeometry::Point(p) => p.coordinates.transform(zoom, ti, tj),
267            VectorGeometry::LineString(l) | VectorGeometry::MultiPoint(l) => {
268                l.coordinates.iter_mut().for_each(|p| p.transform(zoom, ti, tj))
269            }
270            VectorGeometry::MultiLineString(l) | VectorGeometry::Polygon(l) => l
271                .coordinates
272                .iter_mut()
273                .for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj))),
274            VectorGeometry::MultiPolygon(l) => l.coordinates.iter_mut().for_each(|p| {
275                p.iter_mut().for_each(|l| l.iter_mut().for_each(|p| p.transform(zoom, ti, tj)))
276            }),
277        }
278    }
279}
280
281impl VectorPoint {
282    pub fn transform(&mut self, zoom: f64, ti: f64, tj: f64) {
284        self.x = round(self.x * zoom - ti);
285        self.y = round(self.y * zoom - tj);
286    }
287}