1pub const QUANTIZED_MAX: u16 = 32767;
5
6#[derive(Debug, Clone, Default)]
13pub struct QuantizedVertices {
14 pub u: Vec<u16>,
16 pub v: Vec<u16>,
18 pub height: Vec<u16>,
20}
21
22impl QuantizedVertices {
23 pub fn new() -> Self {
25 Self::default()
26 }
27
28 pub fn with_capacity(capacity: usize) -> Self {
30 Self {
31 u: Vec::with_capacity(capacity),
32 v: Vec::with_capacity(capacity),
33 height: Vec::with_capacity(capacity),
34 }
35 }
36
37 pub fn len(&self) -> usize {
39 self.u.len()
40 }
41
42 pub fn is_empty(&self) -> bool {
44 self.u.is_empty()
45 }
46
47 pub fn push(&mut self, u: u16, v: u16, height: u16) {
49 self.u.push(u);
50 self.v.push(v);
51 self.height.push(height);
52 }
53}
54
55#[derive(Debug, Clone, Default)]
60pub struct EdgeIndices {
61 pub west: Vec<u32>,
63 pub south: Vec<u32>,
65 pub east: Vec<u32>,
67 pub north: Vec<u32>,
69}
70
71impl EdgeIndices {
72 pub fn new() -> Self {
74 Self::default()
75 }
76
77 pub fn from_vertices(vertices: &QuantizedVertices) -> Self {
82 let mut west = Vec::new();
83 let mut south = Vec::new();
84 let mut east = Vec::new();
85 let mut north = Vec::new();
86
87 for (i, (&u, &v)) in vertices.u.iter().zip(vertices.v.iter()).enumerate() {
88 let idx = i as u32;
89 if u == 0 {
90 west.push((idx, v));
91 }
92 if u == QUANTIZED_MAX {
93 east.push((idx, v));
94 }
95 if v == 0 {
96 south.push((idx, u));
97 }
98 if v == QUANTIZED_MAX {
99 north.push((idx, u));
100 }
101 }
102
103 west.sort_by_key(|&(_, v)| v);
105 east.sort_by_key(|&(_, v)| v);
106 south.sort_by_key(|&(_, u)| u);
107 north.sort_by_key(|&(_, u)| u);
108
109 Self {
110 west: west.into_iter().map(|(idx, _)| idx).collect(),
111 south: south.into_iter().map(|(idx, _)| idx).collect(),
112 east: east.into_iter().map(|(idx, _)| idx).collect(),
113 north: north.into_iter().map(|(idx, _)| idx).collect(),
114 }
115 }
116}
117
118#[derive(Debug, Clone, Copy, PartialEq, Eq)]
120#[repr(u8)]
121pub enum ExtensionId {
122 OctEncodedVertexNormals = 1,
124 WaterMask = 2,
126 Metadata = 4,
128}
129
130#[derive(Debug, Clone)]
132pub enum WaterMask {
133 Uniform(u8),
135 Grid(Box<[u8; 256 * 256]>),
137}
138
139impl Default for WaterMask {
140 fn default() -> Self {
141 Self::Uniform(0) }
143}
144
145impl WaterMask {
146 pub fn from_data(data: &[u8]) -> Self {
151 if data.len() < 256 * 256 {
152 return Self::Uniform(0); }
154
155 if let Some(&first) = data.first()
157 && data[..256 * 256].iter().all(|&v| v == first)
158 {
159 return Self::Uniform(first);
160 }
161
162 let mut grid = Box::new([0u8; 256 * 256]);
164 grid.copy_from_slice(&data[..256 * 256]);
165 Self::Grid(grid)
166 }
167}
168
169#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
171#[serde(rename_all = "camelCase")]
172pub struct AvailableRange {
173 pub start_x: u32,
175 pub end_x: u32,
177 pub start_y: u32,
179 pub end_y: u32,
181}
182
183impl AvailableRange {
184 pub fn new(start_x: u32, end_x: u32, start_y: u32, end_y: u32) -> Self {
186 Self {
187 start_x,
188 end_x,
189 start_y,
190 end_y,
191 }
192 }
193
194 pub fn full_level_geodetic(zoom: u8) -> Self {
200 let max_x = (1u32 << (zoom + 1)) - 1;
201 let max_y = (1u32 << zoom) - 1;
202 Self {
203 start_x: 0,
204 end_x: max_x,
205 start_y: 0,
206 end_y: max_y,
207 }
208 }
209}
210
211#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
215pub struct TileMetadata {
216 #[serde(default)]
220 pub available: Vec<Vec<AvailableRange>>,
221}
222
223impl TileMetadata {
224 pub fn new() -> Self {
226 Self {
227 available: Vec::new(),
228 }
229 }
230
231 #[deprecated(note = "Use for_tile instead, which computes correct child tile ranges")]
235 pub fn all_available(current_zoom: u8, max_zoom: u8) -> Self {
236 let mut available = Vec::new();
237
238 for child_zoom in (current_zoom + 1)..=max_zoom {
240 available.push(vec![AvailableRange::full_level_geodetic(child_zoom)]);
241 }
242
243 Self { available }
244 }
245
246 pub fn for_tile(tile_x: u32, tile_y: u32, current_zoom: u8, max_zoom: u8) -> Self {
257 let mut available = Vec::new();
258
259 for child_zoom in (current_zoom + 1)..=max_zoom {
261 let levels_deep = child_zoom - current_zoom;
263 let scale = 1u32 << levels_deep; let start_x = tile_x * scale;
268 let start_y = tile_y * scale;
269 let end_x = start_x + scale - 1;
270 let end_y = start_y + scale - 1;
271
272 available.push(vec![AvailableRange::new(start_x, end_x, start_y, end_y)]);
273 }
274
275 Self { available }
276 }
277}
278
279impl Default for TileMetadata {
280 fn default() -> Self {
281 Self::new()
282 }
283}
284
285#[derive(Debug, Clone, Copy)]
287pub struct TileBounds {
288 pub west: f64,
290 pub south: f64,
292 pub east: f64,
294 pub north: f64,
296}
297
298impl TileBounds {
299 pub fn new(west: f64, south: f64, east: f64, north: f64) -> Self {
301 Self {
302 west,
303 south,
304 east,
305 north,
306 }
307 }
308
309 pub fn width(&self) -> f64 {
311 self.east - self.west
312 }
313
314 pub fn height(&self) -> f64 {
316 self.north - self.south
317 }
318
319 pub fn center_lon(&self) -> f64 {
321 (self.west + self.east) / 2.0
322 }
323
324 pub fn center_lat(&self) -> f64 {
326 (self.south + self.north) / 2.0
327 }
328}
329
330#[cfg(test)]
331mod tests {
332 use super::*;
333
334 #[test]
335 fn test_quantized_vertices() {
336 let mut vertices = QuantizedVertices::new();
337 assert!(vertices.is_empty());
338
339 vertices.push(0, 0, 100);
340 vertices.push(QUANTIZED_MAX, QUANTIZED_MAX, 200);
341
342 assert_eq!(vertices.len(), 2);
343 assert_eq!(vertices.u, vec![0, QUANTIZED_MAX]);
344 assert_eq!(vertices.v, vec![0, QUANTIZED_MAX]);
345 assert_eq!(vertices.height, vec![100, 200]);
346 }
347
348 #[test]
349 fn test_edge_indices_extraction() {
350 let vertices = QuantizedVertices {
352 u: vec![0, QUANTIZED_MAX, 0, QUANTIZED_MAX],
353 v: vec![0, 0, QUANTIZED_MAX, QUANTIZED_MAX],
354 height: vec![0, 0, 0, 0],
355 };
356
357 let edges = EdgeIndices::from_vertices(&vertices);
358
359 assert_eq!(edges.west, vec![0, 2]);
361 assert_eq!(edges.east, vec![1, 3]);
363 assert_eq!(edges.south, vec![0, 1]);
365 assert_eq!(edges.north, vec![2, 3]);
367 }
368
369 #[test]
370 fn test_tile_bounds() {
371 let bounds = TileBounds::new(-180.0, -90.0, 180.0, 90.0);
372
373 assert_eq!(bounds.width(), 360.0);
374 assert_eq!(bounds.height(), 180.0);
375 assert_eq!(bounds.center_lon(), 0.0);
376 assert_eq!(bounds.center_lat(), 0.0);
377 }
378
379 #[test]
380 fn tile_metadata_decodes_without_available() {
381 let json = r#"{"geometricError":1234.5}"#;
384 let m: TileMetadata = serde_json::from_str(json).expect("lenient decode");
385 assert!(m.available.is_empty());
386 }
387}