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}