gistools/readers/pmtiles/
s2pm_spec.rs

1use super::{PMDirectory, PMTileType};
2use crate::{parsers::Buffer, util::CompressionFormat};
3use s2json::Face;
4
5/// Store entries for each Face
6#[derive(Debug, Clone, Default, PartialEq)]
7pub struct S2PMEntries {
8    /// The entries for face 0
9    pub face_0: PMDirectory,
10    /// The entries for face 1
11    pub face_1: PMDirectory,
12    /// The entries for face 2
13    pub face_2: PMDirectory,
14    /// The entries for face 3
15    pub face_3: PMDirectory,
16    /// The entries for face 4
17    pub face_4: PMDirectory,
18    /// The entries for face 5
19    pub face_5: PMDirectory,
20}
21impl S2PMEntries {
22    /// Get the directory for the given face
23    pub fn get(&self, face: Face) -> &PMDirectory {
24        match face {
25            Face::Face0 => &self.face_0,
26            Face::Face1 => &self.face_1,
27            Face::Face2 => &self.face_2,
28            Face::Face3 => &self.face_3,
29            Face::Face4 => &self.face_4,
30            Face::Face5 => &self.face_5,
31        }
32    }
33
34    /// Get the mutable directory for the given face
35    pub fn get_mut(&mut self, face: Face) -> &mut PMDirectory {
36        match face {
37            Face::Face0 => &mut self.face_0,
38            Face::Face1 => &mut self.face_1,
39            Face::Face2 => &mut self.face_2,
40            Face::Face3 => &mut self.face_3,
41            Face::Face4 => &mut self.face_4,
42            Face::Face5 => &mut self.face_5,
43        }
44    }
45
46    /// Set the directory for the given face
47    pub fn set_dir(&mut self, face: Face, dir: PMDirectory) {
48        match face {
49            Face::Face0 => self.face_0 = dir,
50            Face::Face1 => self.face_1 = dir,
51            Face::Face2 => self.face_2 = dir,
52            Face::Face3 => self.face_3 = dir,
53            Face::Face4 => self.face_4 = dir,
54            Face::Face5 => self.face_5 = dir,
55        }
56    }
57}
58
59/// The S2PMTiles v1 header size in bytes
60pub const S2_PM_HEADER_SIZE_BYTES: usize = 262;
61/// The S2PMTiles v1 root directory size in bytes
62pub const S2_PM_ROOT_SIZE: usize = 98_304;
63
64/// S2PMTiles v3 header storing basic archive-level information.
65#[derive(Debug, Copy, Clone, Default, PartialEq)]
66pub struct S2PMHeader {
67    /// True if this is an S2PMTiles v1, otherwise PMTiles v3
68    pub is_s2: bool,
69    /// versioning used for the s2-pmtiles spec
70    pub version: u8,
71    /// the offset in the archive of the root directory for Face 0
72    pub root_directory_offset: u64,
73    /// the length of the root directory for Face 0
74    pub root_directory_length: u64,
75    /// the offset in the archive of the JSON metadata
76    pub metadata_offset: u64,
77    /// the length of the JSON metadata
78    pub metadata_length: u64,
79    /// the offset in the archive of the leaf directory for Face 0
80    pub leaf_directory_offset: u64,
81    /// the length of the leaf directory
82    pub leaf_directory_length: u64,
83    /// the offset in the archive of the tile data
84    pub data_offset: u64,
85    /// the length of the tile data
86    pub data_length: u64,
87    /// number of tiles addressed
88    pub n_addressed_tiles: u64,
89    /// number of tile entries
90    pub n_tile_entries: u64,
91    /// number of tile contents
92    pub n_tile_contents: u64,
93    /// if the archive is clustered or not
94    pub clustered: bool,
95    /// if the archive is compressed or not
96    /// NOTE: deprecated and only `Compression::None` is supported
97    pub internal_compression: CompressionFormat,
98    /// what kind of compression is used for the tile data
99    pub tile_compression: CompressionFormat,
100    /// what kind of compression is used for the metadata
101    pub tile_type: PMTileType,
102    /// the min zoom level
103    pub min_zoom: u8,
104    /// the max zoom level
105    pub max_zoom: u8,
106    /// the min longitude
107    pub min_longitude: f32,
108    /// the min latitude
109    pub min_latitude: f32,
110    /// the max longitude
111    pub max_longitude: f32,
112    /// the max latitude
113    pub max_latitude: f32,
114    /// the center zoom level
115    pub center_zoom: u8,
116    /// the center longitude
117    pub center_longitude: f32,
118    /// the center latitude
119    pub center_latitude: f32,
120    /// the offset in the archive of the root directory for Face 1
121    pub root_directory_offset1: u64,
122    /// the length of the root directory for Face 1
123    pub root_directory_length1: u64,
124    /// the offset in the archive of the root directory for Face 2
125    pub root_directory_offset2: u64,
126    /// the length of the root directory for Face 2
127    pub root_directory_length2: u64,
128    /// the offset in the archive of the root directory for Face 3
129    pub root_directory_offset3: u64,
130    /// the length of the root directory for Face 3
131    pub root_directory_length3: u64,
132    /// the offset in the archive of the root directory for Face 4
133    pub root_directory_offset4: u64,
134    /// the length of the root directory for Face 4
135    pub root_directory_length4: u64,
136    /// the offset in the archive of the root directory for Face 5
137    pub root_directory_offset5: u64,
138    /// the length of the root directory for Face 5
139    pub root_directory_length5: u64,
140    /// the offset in the archive of the leaf directory for Face 1
141    pub leaf_directory_offset1: u64,
142    /// the length of the leaf directory for Face 1
143    pub leaf_directory_length1: u64,
144    /// the offset in the archive of the leaf directory for Face 2
145    pub leaf_directory_offset2: u64,
146    /// the length of the leaf directory for Face 2
147    pub leaf_directory_length2: u64,
148    /// the offset in the archive of the leaf directory for Face 3
149    pub leaf_directory_offset3: u64,
150    /// the length of the leaf directory for Face 3
151    pub leaf_directory_length3: u64,
152    /// the offset in the archive of the leaf directory for Face 4
153    pub leaf_directory_offset4: u64,
154    /// the length of the leaf directory for Face 4
155    pub leaf_directory_length4: u64,
156    /// the offset in the archive of the leaf directory for Face 5
157    pub leaf_directory_offset5: u64,
158    /// the length of the leaf directory for Face 5
159    pub leaf_directory_length5: u64,
160}
161impl S2PMHeader {
162    /// Convert a buffer into a S2Header
163    pub fn from_bytes(buffer: &mut Buffer) -> Self {
164        let ess = buffer.get_u8_at(0);
165        let two = buffer.get_u8_at(1);
166        let is_s2 = ess == 83 && two == 50;
167        Self {
168            is_s2,
169            version: buffer.get_u8_at(7),
170            root_directory_offset: buffer.get_u64_at(8),
171            root_directory_length: buffer.get_u64_at(16),
172            metadata_offset: buffer.get_u64_at(24),
173            metadata_length: buffer.get_u64_at(32),
174            leaf_directory_offset: buffer.get_u64_at(40),
175            leaf_directory_length: buffer.get_u64_at(48),
176            data_offset: buffer.get_u64_at(56),
177            data_length: buffer.get_u64_at(64),
178            n_addressed_tiles: buffer.get_u64_at(72),
179            n_tile_entries: buffer.get_u64_at(80),
180            n_tile_contents: buffer.get_u64_at(88),
181            clustered: buffer.get_u8_at(96) == 1,
182            internal_compression: CompressionFormat::from(buffer.get_u8_at(97)),
183            tile_compression: CompressionFormat::from(buffer.get_u8_at(98)),
184            tile_type: PMTileType::from(buffer.get_u8_at(99)),
185            min_zoom: buffer.get_u8_at(100),
186            max_zoom: buffer.get_u8_at(101),
187            min_longitude: if is_s2 { 0.0 } else { (buffer.get_i32_at(102) as f32) / 10_000_000.0 },
188            min_latitude: if is_s2 { 0.0 } else { (buffer.get_i32_at(106) as f32) / 10_000_000.0 },
189            max_longitude: if is_s2 { 0.0 } else { (buffer.get_i32_at(110) as f32) / 10_000_000.0 },
190            max_latitude: if is_s2 { 0.0 } else { (buffer.get_i32_at(114) as f32) / 10_000_000.0 },
191            center_zoom: if is_s2 { 0 } else { buffer.get_u8_at(118) },
192            center_longitude: if is_s2 {
193                0.0
194            } else {
195                (buffer.get_i32_at(119) as f32) / 10_000_000.0
196            },
197            center_latitude: if is_s2 {
198                0.0
199            } else {
200                (buffer.get_i32_at(123) as f32) / 10_000_000.0
201            },
202            root_directory_offset1: if is_s2 { buffer.get_u64_at(102) } else { 0 },
203            root_directory_length1: if is_s2 { buffer.get_u64_at(110) } else { 0 },
204            root_directory_length2: if is_s2 { buffer.get_u64_at(118) } else { 0 },
205            root_directory_offset2: if is_s2 { buffer.get_u64_at(126) } else { 0 },
206            root_directory_offset3: if is_s2 { buffer.get_u64_at(134) } else { 0 },
207            root_directory_length3: if is_s2 { buffer.get_u64_at(142) } else { 0 },
208            root_directory_offset4: if is_s2 { buffer.get_u64_at(150) } else { 0 },
209            root_directory_length4: if is_s2 { buffer.get_u64_at(158) } else { 0 },
210            root_directory_offset5: if is_s2 { buffer.get_u64_at(166) } else { 0 },
211            root_directory_length5: if is_s2 { buffer.get_u64_at(174) } else { 0 },
212            leaf_directory_offset1: if is_s2 { buffer.get_u64_at(182) } else { 0 },
213            leaf_directory_length1: if is_s2 { buffer.get_u64_at(190) } else { 0 },
214            leaf_directory_offset2: if is_s2 { buffer.get_u64_at(198) } else { 0 },
215            leaf_directory_length2: if is_s2 { buffer.get_u64_at(206) } else { 0 },
216            leaf_directory_offset3: if is_s2 { buffer.get_u64_at(214) } else { 0 },
217            leaf_directory_length3: if is_s2 { buffer.get_u64_at(222) } else { 0 },
218            leaf_directory_offset4: if is_s2 { buffer.get_u64_at(230) } else { 0 },
219            leaf_directory_length4: if is_s2 { buffer.get_u64_at(238) } else { 0 },
220            leaf_directory_offset5: if is_s2 { buffer.get_u64_at(246) } else { 0 },
221            leaf_directory_length5: if is_s2 { buffer.get_u64_at(254) } else { 0 },
222        }
223    }
224
225    /// Convert a S2Header into a buffer
226    pub fn to_bytes(&self) -> Buffer {
227        let mut buffer = Buffer::default();
228
229        // default id
230        buffer.set_u8(b'S');
231        buffer.set_u8(b'2');
232        // Version number at position 7
233        buffer.set_u8_at(7, 1);
234
235        // Root directory offset and length at positions 8 and 16
236        buffer.set_u64_at(8, self.root_directory_offset);
237        buffer.set_u64_at(16, self.root_directory_length);
238
239        // JSON metadata offset and length at positions 24 and 32
240        buffer.set_u64_at(24, self.metadata_offset);
241        buffer.set_u64_at(32, self.metadata_length);
242
243        // Leaf directory offset and optional length at positions 40 and 48
244        buffer.set_u64_at(40, self.leaf_directory_offset);
245        buffer.set_u64_at(48, self.leaf_directory_length);
246
247        // Tile data offset and optional length at positions 56 and 64
248        buffer.set_u64_at(56, self.data_offset);
249        buffer.set_u64_at(64, self.data_length);
250
251        // Number of addressed tiles, tile entries, and tile contents at positions 72, 80, and 88
252        buffer.set_u64_at(72, self.n_addressed_tiles);
253        buffer.set_u64_at(80, self.n_tile_entries);
254        buffer.set_u64_at(88, self.n_tile_contents);
255
256        // Flags and types at positions 96 through 101
257        buffer.set_u8_at(96, if self.clustered { 1 } else { 0 });
258        buffer.set_u8_at(97, self.internal_compression.into());
259        buffer.set_u8_at(98, self.tile_compression.into());
260        buffer.set_u8_at(99, self.tile_type.into());
261        buffer.set_u8_at(100, self.min_zoom);
262        buffer.set_u8_at(101, self.max_zoom);
263
264        // set the remaining root directory offsets and lengths
265        buffer.set_u64_at(102, self.root_directory_offset1);
266        buffer.set_u64_at(110, self.root_directory_length1);
267        buffer.set_u64_at(118, self.root_directory_length2);
268        buffer.set_u64_at(126, self.root_directory_offset2);
269        buffer.set_u64_at(134, self.root_directory_offset3);
270        buffer.set_u64_at(142, self.root_directory_length3);
271        buffer.set_u64_at(150, self.root_directory_offset4);
272        buffer.set_u64_at(158, self.root_directory_length4);
273        buffer.set_u64_at(166, self.root_directory_offset5);
274        buffer.set_u64_at(174, self.root_directory_length5);
275
276        // set the remaining leaf directory offsets and lengths
277        buffer.set_u64_at(182, self.leaf_directory_offset1);
278        buffer.set_u64_at(190, self.leaf_directory_length1);
279        buffer.set_u64_at(198, self.leaf_directory_offset2);
280        buffer.set_u64_at(206, self.leaf_directory_length2);
281        buffer.set_u64_at(214, self.leaf_directory_offset3);
282        buffer.set_u64_at(222, self.leaf_directory_length3);
283        buffer.set_u64_at(230, self.leaf_directory_offset4);
284        buffer.set_u64_at(238, self.leaf_directory_length4);
285        buffer.set_u64_at(246, self.leaf_directory_offset5);
286        buffer.set_u64_at(254, self.leaf_directory_length5);
287
288        buffer
289    }
290
291    /// Get the root directory offset for a given face
292    pub fn get_root_offset(&self, face: Face) -> u64 {
293        match face {
294            Face::Face0 => self.root_directory_offset,
295            Face::Face1 => self.root_directory_offset1,
296            Face::Face2 => self.root_directory_offset2,
297            Face::Face3 => self.root_directory_offset3,
298            Face::Face4 => self.root_directory_offset4,
299            Face::Face5 => self.root_directory_offset5,
300        }
301    }
302
303    /// Get the root directory length for a given face
304    pub fn get_root_length(&self, face: Face) -> u64 {
305        match face {
306            Face::Face0 => self.root_directory_length,
307            Face::Face1 => self.root_directory_length1,
308            Face::Face2 => self.root_directory_length2,
309            Face::Face3 => self.root_directory_length3,
310            Face::Face4 => self.root_directory_length4,
311            Face::Face5 => self.root_directory_length5,
312        }
313    }
314}