1use crate::{
2 parsers::Writer,
3 readers::{
4 PMDirectory, PMEntry, PMHeader, PMTilePos, PMTileType, S2_PM_HEADER_SIZE_BYTES,
5 S2_PM_ROOT_SIZE, S2PMEntries, S2PMHeader,
6 },
7 util::CompressionFormat,
8 writers::TileWriter,
9};
10use alloc::{vec, vec::Vec};
11use s2_tilejson::Metadata;
12use s2json::Face;
13
14#[derive(Debug, Clone, Default)]
16struct OptimizedDirectory {
17 pub root_bytes: Vec<u8>,
19 pub leaves_bytes: Vec<u8>,
21 #[allow(dead_code)]
23 pub num_leaves: u64,
24}
25impl OptimizedDirectory {
26 pub fn optimize_directories(
28 directory: &mut PMDirectory,
29 target_root_length: usize,
30 ) -> OptimizedDirectory {
31 directory.entries.sort_by(|a, b| a.tile_id.cmp(&b.tile_id));
32 let test_bytes = directory.serialize();
33 if test_bytes.len() < target_root_length {
34 OptimizedDirectory { root_bytes: test_bytes, leaves_bytes: Vec::new(), num_leaves: 0 }
35 } else {
36 let mut leaf_size = 4096;
37 loop {
38 let build = OptimizedDirectory::build_root_leaves(directory, leaf_size);
39 if build.root_bytes.len() < target_root_length {
40 return build;
41 }
42 leaf_size *= 2;
43 }
44 }
45 }
46
47 pub fn build_root_leaves(directory: &PMDirectory, leaf_size: usize) -> OptimizedDirectory {
49 let mut root_entries = PMDirectory::default();
50 let mut leaves_bytes = Vec::<u8>::new();
51 let mut num_leaves = 0;
52
53 let mut i = 0;
54 let entries = &directory.entries;
55 while i < entries.len() {
56 num_leaves += 1;
57 let mut end = i + leaf_size;
58 if i + leaf_size > entries.len() {
59 end = entries.len();
60 }
61 let new_dir_slice = PMDirectory::new(entries[i..end].to_vec());
62 let serialized = new_dir_slice.serialize();
63 let entry = PMEntry {
64 tile_id: entries[i].tile_id,
65 offset: leaves_bytes.len() as u64,
66 length: serialized.len() as u32,
67 run_length: 0,
68 };
69 root_entries.entries.push(entry);
70 leaves_bytes.extend(serialized);
71 i += leaf_size;
72 }
73
74 OptimizedDirectory { root_bytes: root_entries.serialize(), leaves_bytes, num_leaves }
75 }
76}
77
78pub trait DataWriter: core::fmt::Debug {
80 fn write_data(&mut self, data: &[u8], offset: u64);
82 fn append_data(&mut self, data: &[u8]);
84 fn take(&self) -> Vec<u8>;
86}
87
88#[derive(Debug)]
126pub struct PMTilesWriter<W: Writer> {
127 tile_entries: PMDirectory,
128 s2tile_entries: S2PMEntries,
129 offset: u64,
130 addressed_tiles: u64,
131 clustered: bool,
132 compression: CompressionFormat,
133 writer: W,
134}
135impl<W: Writer> PMTilesWriter<W> {
136 pub fn new(writer: W, compression: CompressionFormat) -> Self {
140 let mut writer = PMTilesWriter {
141 tile_entries: PMDirectory::default(),
142 s2tile_entries: S2PMEntries::default(),
143 offset: 0,
144 addressed_tiles: 0,
145 clustered: false,
146 compression,
147 writer,
148 };
149 writer.writer.append(&vec![0u8; S2_PM_ROOT_SIZE]);
150 writer
151 }
152
153 pub fn take(&mut self) -> Vec<u8> {
155 self.writer.take()
156 }
157
158 pub fn write_tile(&mut self, tile_id: u64, data: &[u8], face: Option<Face>) {
160 let length = data.len();
161 let tile_entries = match face {
162 None => &mut self.tile_entries,
163 Some(f) => self.s2tile_entries.get_mut(f),
164 };
165 if !tile_entries.is_empty() && tile_id < tile_entries.last().unwrap().tile_id {
166 self.clustered = false;
167 }
168
169 let offset = self.offset;
170 self.writer.append(data);
171 tile_entries.insert(PMEntry { tile_id, offset, length: length as u32, run_length: 1 });
172 self.offset += length as u64;
173
174 self.addressed_tiles += 1;
175 }
176
177 fn _commit(&mut self, metadata: &Metadata) {
179 if !self.tile_entries.is_empty() {
180 self.commit_wm(metadata);
181 } else {
182 self.commit_s2(metadata);
183 }
184
185 self.writer.flush();
186 }
187
188 fn commit_wm(&mut self, metadata: &Metadata) {
190 let meta_buffer = serde_json::to_vec(metadata).unwrap();
192
193 let od: OptimizedDirectory = OptimizedDirectory::optimize_directories(
195 &mut self.tile_entries,
196 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
197 );
198 let OptimizedDirectory { root_bytes, leaves_bytes, .. } = od;
199
200 let root_directory_offset = S2_PM_HEADER_SIZE_BYTES as u64;
202 let root_directory_length = root_bytes.len() as u64;
203 let metadata_offset = root_directory_offset + root_directory_length;
204 let metadata_length = meta_buffer.len() as u64;
205 let leaf_directory_offset = self.offset + S2_PM_ROOT_SIZE as u64;
206 let leaf_directory_length = leaves_bytes.len() as u64;
207 self.offset += leaves_bytes.len() as u64;
208
209 self.writer.append(&leaves_bytes);
211 let min_zoom = PMTilePos::from_id(self.tile_entries.first().unwrap().tile_id).zoom;
213 let max_zoom = PMTilePos::from_id(self.tile_entries.last().unwrap().tile_id).zoom;
214
215 let header = PMHeader {
217 version: 3,
218 root_directory_offset,
219 root_directory_length,
220 metadata_offset,
221 metadata_length,
222 leaf_directory_offset,
223 leaf_directory_length,
224 data_offset: S2_PM_ROOT_SIZE as u64,
225 data_length: self.offset,
226 n_addressed_tiles: self.addressed_tiles,
227 n_tile_entries: self.tile_entries.len() as u64,
228 n_tile_contents: 0,
229 clustered: self.clustered,
230 internal_compression: CompressionFormat::None,
231 tile_compression: self.compression,
232 tile_type: PMTileType::Unknown,
233 min_zoom,
234 max_zoom,
235 ..Default::default()
236 };
237 let serialized_header = header.to_bytes().take();
238
239 self.writer.write(&serialized_header, 0);
241 self.writer.write(&root_bytes, root_directory_offset);
242 self.writer.write(&meta_buffer, metadata_offset);
243 }
244
245 fn commit_s2(&mut self, metadata: &Metadata) {
247 let meta_buffer = serde_json::to_vec(metadata).unwrap();
249
250 let od = OptimizedDirectory::optimize_directories(
252 self.s2tile_entries.get_mut(Face::Face0),
253 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
254 );
255 let OptimizedDirectory { root_bytes, leaves_bytes, .. } = od;
256 let od1 = OptimizedDirectory::optimize_directories(
257 self.s2tile_entries.get_mut(Face::Face1),
258 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
259 );
260 let OptimizedDirectory { root_bytes: root_bytes1, leaves_bytes: leaves_bytes1, .. } = od1;
261 let od2 = OptimizedDirectory::optimize_directories(
262 self.s2tile_entries.get_mut(Face::Face2),
263 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
264 );
265 let OptimizedDirectory { root_bytes: root_bytes2, leaves_bytes: leaves_bytes2, .. } = od2;
266 let od3 = OptimizedDirectory::optimize_directories(
267 self.s2tile_entries.get_mut(Face::Face3),
268 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
269 );
270 let OptimizedDirectory { root_bytes: root_bytes3, leaves_bytes: leaves_bytes3, .. } = od3;
271 let od4 = OptimizedDirectory::optimize_directories(
272 self.s2tile_entries.get_mut(Face::Face4),
273 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
274 );
275 let OptimizedDirectory { root_bytes: root_bytes4, leaves_bytes: leaves_bytes4, .. } = od4;
276 let od5 = OptimizedDirectory::optimize_directories(
277 self.s2tile_entries.get_mut(Face::Face5),
278 S2_PM_ROOT_SIZE - S2_PM_HEADER_SIZE_BYTES - meta_buffer.len(),
279 );
280 let OptimizedDirectory { root_bytes: root_bytes5, leaves_bytes: leaves_bytes5, .. } = od5;
281
282 let root_directory_offset = S2_PM_HEADER_SIZE_BYTES as u64;
285 let root_directory_length = root_bytes.len() as u64;
286 let root_directory_offset1 = root_directory_offset + root_directory_length;
287 let root_directory_length1 = root_bytes1.len() as u64;
288 let root_directory_offset2 = root_directory_offset1 + root_directory_length1;
289 let root_directory_length2 = root_bytes2.len() as u64;
290 let root_directory_offset3 = root_directory_offset2 + root_directory_length2;
291 let root_directory_length3 = root_bytes3.len() as u64;
292 let root_directory_offset4 = root_directory_offset3 + root_directory_length3;
293 let root_directory_length4 = root_bytes4.len() as u64;
294 let root_directory_offset5 = root_directory_offset4 + root_directory_length4;
295 let root_directory_length5 = root_bytes5.len() as u64;
296 let metadata_offset = root_directory_offset5 + root_directory_length5;
298 let metadata_length = meta_buffer.len() as u64;
299 let leaf_directory_offset = self.offset + S2_PM_ROOT_SIZE as u64;
301 let leaf_directory_length = leaves_bytes.len() as u64;
302 self.offset += leaf_directory_length;
303 self.writer.append(&leaves_bytes);
304 let leaf_directory_offset1 = self.offset + S2_PM_ROOT_SIZE as u64;
305 let leaf_directory_length1 = leaves_bytes1.len() as u64;
306 self.offset += leaf_directory_length1;
307 self.writer.append(&leaves_bytes1);
308 let leaf_directory_offset2 = self.offset + S2_PM_ROOT_SIZE as u64;
309 let leaf_directory_length2 = leaves_bytes2.len() as u64;
310 self.offset += leaf_directory_length2;
311 self.writer.append(&leaves_bytes2);
312 let leaf_directory_offset3 = self.offset + S2_PM_ROOT_SIZE as u64;
313 let leaf_directory_length3 = leaves_bytes3.len() as u64;
314 self.offset += leaf_directory_length3;
315 self.writer.append(&leaves_bytes3);
316 let leaf_directory_offset4 = self.offset + S2_PM_ROOT_SIZE as u64;
317 let leaf_directory_length4 = leaves_bytes4.len() as u64;
318 self.offset += leaf_directory_length4;
319 self.writer.append(&leaves_bytes4);
320 let leaf_directory_offset5 = self.offset + S2_PM_ROOT_SIZE as u64;
321 let leaf_directory_length5 = leaves_bytes5.len() as u64;
322 self.offset += leaf_directory_length5;
323 self.writer.append(&leaves_bytes5);
324
325 self.writer.append(&leaves_bytes);
327 let header = S2PMHeader {
329 is_s2: true,
330 version: 3,
331 root_directory_offset,
332 root_directory_length,
333 root_directory_offset1,
334 root_directory_length1,
335 root_directory_offset2,
336 root_directory_length2,
337 root_directory_offset3,
338 root_directory_length3,
339 root_directory_offset4,
340 root_directory_length4,
341 root_directory_offset5,
342 root_directory_length5,
343 metadata_offset,
344 metadata_length,
345 leaf_directory_offset,
346 leaf_directory_length,
347 leaf_directory_offset1,
348 leaf_directory_length1,
349 leaf_directory_offset2,
350 leaf_directory_length2,
351 leaf_directory_offset3,
352 leaf_directory_length3,
353 leaf_directory_offset4,
354 leaf_directory_length4,
355 leaf_directory_offset5,
356 leaf_directory_length5,
357 data_offset: S2_PM_ROOT_SIZE as u64,
358 data_length: self.offset,
359 n_addressed_tiles: self.addressed_tiles,
360 n_tile_entries: self.tile_entries.len() as u64,
361 n_tile_contents: 0,
362 clustered: self.clustered,
363 internal_compression: CompressionFormat::None,
364 tile_compression: self.compression,
365 tile_type: PMTileType::Unknown,
366 ..Default::default()
367 };
368 let serialized_header = header.to_bytes().take();
369
370 self.writer.write(&serialized_header, 0);
372 self.writer.write(&root_bytes, root_directory_offset);
373 self.writer.write(&root_bytes1, root_directory_offset1);
374 self.writer.write(&root_bytes2, root_directory_offset2);
375 self.writer.write(&root_bytes3, root_directory_offset3);
376 self.writer.write(&root_bytes4, root_directory_offset4);
377 self.writer.write(&root_bytes5, root_directory_offset5);
378 self.writer.write(&meta_buffer, metadata_offset);
379 }
380}
381
382impl<W: Writer> TileWriter for PMTilesWriter<W> {
383 fn write_tile_wm(&mut self, zoom: u8, x: u32, y: u32, data: Vec<u8>) {
384 let tile_id = PMTilePos::new(zoom, x as u64, y as u64).to_id();
385 self.write_tile(tile_id, &data, None);
386 }
387
388 fn write_tile_s2(&mut self, face: Face, zoom: u8, x: u32, y: u32, data: Vec<u8>) {
389 let tile_id = PMTilePos::new(zoom, x as u64, y as u64).to_id();
390 self.write_tile(tile_id, &data, Some(face));
391 }
392
393 fn commit(&mut self, metadata: Metadata, tile_compression: Option<CompressionFormat>) {
394 if let Some(tile_compression) = tile_compression {
395 self.compression = tile_compression;
396 }
397 self._commit(&metadata);
398 }
399}