Line data Source code
1 : #ifndef PMTILES_HPP
2 : #define PMTILES_HPP
3 :
4 : #include <string>
5 : #include <sstream>
6 : #include <vector>
7 : #include <tuple>
8 : #include <functional>
9 : #include <algorithm>
10 : #include <limits> // for std::numeric_limits<>
11 :
12 : namespace pmtiles {
13 :
14 : const uint8_t TILETYPE_UNKNOWN = 0x0;
15 : const uint8_t TILETYPE_MVT = 0x1;
16 : const uint8_t TILETYPE_PNG = 0x2;
17 : const uint8_t TILETYPE_JPEG = 0x3;
18 : const uint8_t TILETYPE_WEBP = 0x4;
19 :
20 : const uint8_t COMPRESSION_UNKNOWN = 0x0;
21 : const uint8_t COMPRESSION_NONE = 0x1;
22 : const uint8_t COMPRESSION_GZIP = 0x2;
23 : const uint8_t COMPRESSION_BROTLI = 0x3;
24 : const uint8_t COMPRESSION_ZSTD = 0x4;
25 :
26 : #ifdef PMTILES_MSB
27 : template<class T>
28 : inline void swap_byte_order_if_msb(T* ptr) {
29 : unsigned char* ptrBytes = reinterpret_cast<unsigned char*>(ptr);
30 : for (size_t i = 0; i < sizeof(T)/2; ++i) {
31 : std::swap(ptrBytes[i], ptrBytes[sizeof(T)-1-i]);
32 : }
33 : }
34 : #else
35 : template<class T>
36 1904 : inline void swap_byte_order_if_msb(T* /*ptr*/)
37 : {
38 1904 : }
39 : #endif
40 :
41 : template<class T>
42 323 : inline void copy_to_lsb(std::stringstream& ss, T val) {
43 323 : swap_byte_order_if_msb(&val);
44 323 : ss.write(reinterpret_cast<char*>(&val), sizeof(T));
45 323 : }
46 :
47 : template<>
48 152 : inline void copy_to_lsb<uint8_t>(std::stringstream& ss, uint8_t val) {
49 152 : ss.write(reinterpret_cast<char*>(&val), 1);
50 152 : }
51 :
52 : struct headerv3 {
53 : uint64_t root_dir_offset;
54 : uint64_t root_dir_bytes;
55 : uint64_t json_metadata_offset;
56 : uint64_t json_metadata_bytes;
57 : uint64_t leaf_dirs_offset;
58 : uint64_t leaf_dirs_bytes;
59 : uint64_t tile_data_offset;
60 : uint64_t tile_data_bytes;
61 : uint64_t addressed_tiles_count;
62 : uint64_t tile_entries_count;
63 : uint64_t tile_contents_count;
64 : bool clustered;
65 : uint8_t internal_compression;
66 : uint8_t tile_compression;
67 : uint8_t tile_type;
68 : uint8_t min_zoom;
69 : uint8_t max_zoom;
70 : int32_t min_lon_e7;
71 : int32_t min_lat_e7;
72 : int32_t max_lon_e7;
73 : int32_t max_lat_e7;
74 : uint8_t center_zoom;
75 : int32_t center_lon_e7;
76 : int32_t center_lat_e7;
77 :
78 : // WARNING: this is limited to little-endian
79 19 : std::string serialize() {
80 38 : std::stringstream ss;
81 19 : ss << "PMTiles";
82 19 : uint8_t version = 3;
83 19 : copy_to_lsb(ss, version);
84 19 : copy_to_lsb(ss, root_dir_offset);
85 19 : copy_to_lsb(ss, root_dir_bytes);
86 19 : copy_to_lsb(ss, json_metadata_offset);
87 19 : copy_to_lsb(ss, json_metadata_bytes);
88 19 : copy_to_lsb(ss, leaf_dirs_offset);
89 19 : copy_to_lsb(ss, leaf_dirs_bytes);
90 19 : copy_to_lsb(ss, tile_data_offset);
91 19 : copy_to_lsb(ss, tile_data_bytes);
92 19 : copy_to_lsb(ss, addressed_tiles_count);
93 19 : copy_to_lsb(ss, tile_entries_count);
94 19 : copy_to_lsb(ss, tile_contents_count);
95 :
96 19 : uint8_t clustered_val = 0x0;
97 19 : if (clustered) {
98 19 : clustered_val = 0x1;
99 : }
100 :
101 19 : copy_to_lsb(ss, clustered_val);
102 19 : copy_to_lsb(ss, internal_compression);
103 19 : copy_to_lsb(ss, tile_compression);
104 19 : copy_to_lsb(ss, tile_type);
105 19 : copy_to_lsb(ss, min_zoom);
106 19 : copy_to_lsb(ss, max_zoom);
107 19 : copy_to_lsb(ss, min_lon_e7);
108 19 : copy_to_lsb(ss, min_lat_e7);
109 19 : copy_to_lsb(ss, max_lon_e7);
110 19 : copy_to_lsb(ss, max_lat_e7);
111 19 : copy_to_lsb(ss, center_zoom);
112 19 : copy_to_lsb(ss, center_lon_e7);
113 19 : copy_to_lsb(ss, center_lat_e7);
114 :
115 38 : return ss.str();
116 : }
117 : };
118 :
119 : struct pmtiles_magic_number_exception : std::exception {
120 0 : const char *what() const noexcept override {
121 0 : return "pmtiles magic number exception";
122 : }
123 : };
124 :
125 : struct pmtiles_version_exception : std::exception {
126 0 : const char *what() const noexcept override {
127 0 : return "pmtiles version: must be 3";
128 : }
129 : };
130 :
131 : template<class T>
132 1581 : inline void copy_from_lsb(T* ptr, const std::string &s, size_t offset) {
133 1581 : s.copy(reinterpret_cast<char *>(ptr), sizeof(T), offset);
134 1581 : swap_byte_order_if_msb(ptr);
135 1581 : }
136 :
137 94 : inline headerv3 deserialize_header(const std::string &s) {
138 94 : if (s.substr(0, 7) != "PMTiles") {
139 1 : throw pmtiles_magic_number_exception{};
140 : }
141 93 : if (s.size() != 127 || s[7] != 0x3) {
142 0 : throw pmtiles_version_exception{};
143 : }
144 : headerv3 h;
145 93 : copy_from_lsb(&h.root_dir_offset, s, 8);
146 93 : copy_from_lsb(&h.root_dir_bytes, s, 16);
147 93 : copy_from_lsb(&h.json_metadata_offset, s, 24);
148 93 : copy_from_lsb(&h.json_metadata_bytes, s, 32);
149 93 : copy_from_lsb(&h.leaf_dirs_offset, s, 40);
150 93 : copy_from_lsb(&h.leaf_dirs_bytes, s, 48);
151 93 : copy_from_lsb(&h.tile_data_offset, s, 56);
152 93 : copy_from_lsb(&h.tile_data_bytes, s, 64);
153 93 : copy_from_lsb(&h.addressed_tiles_count, s, 72);
154 93 : copy_from_lsb(&h.tile_entries_count, s, 80);
155 93 : copy_from_lsb(&h.tile_contents_count, s, 88);
156 93 : if (s[96] == 0x1) {
157 93 : h.clustered = true;
158 : } else {
159 0 : h.clustered = false;
160 : }
161 93 : h.internal_compression = s[97];
162 93 : h.tile_compression = s[98];
163 93 : h.tile_type = s[99];
164 93 : h.min_zoom = s[100];
165 93 : h.max_zoom = s[101];
166 93 : copy_from_lsb(&h.min_lon_e7, s, 102);
167 93 : copy_from_lsb(&h.min_lat_e7, s, 106);
168 93 : copy_from_lsb(&h.max_lon_e7, s, 110);
169 93 : copy_from_lsb(&h.max_lat_e7, s, 114);
170 93 : h.center_zoom = s[118];
171 93 : copy_from_lsb(&h.center_lon_e7, s, 119);
172 93 : copy_from_lsb(&h.center_lat_e7, s, 123);
173 93 : return h;
174 : }
175 :
176 : struct zxy {
177 : uint8_t z;
178 : uint32_t x;
179 : uint32_t y;
180 :
181 2669 : zxy(uint8_t _z, int _x, int _y)
182 2669 : : z(_z), x(_x), y(_y) {
183 2669 : }
184 : };
185 :
186 : struct entryv3 {
187 : uint64_t tile_id;
188 : uint64_t offset;
189 : uint32_t length;
190 : uint32_t run_length;
191 :
192 10145 : entryv3()
193 10145 : : tile_id(0), offset(0), length(0), run_length(0) {
194 10145 : }
195 :
196 0 : entryv3(uint64_t _tile_id, uint64_t _offset, uint32_t _length, uint32_t _run_length)
197 0 : : tile_id(_tile_id), offset(_offset), length(_length), run_length(_run_length) {
198 0 : }
199 : };
200 :
201 : #ifdef PMTILES_NEED_ENTRYV3_CMP
202 : struct {
203 : bool operator()(entryv3 a, entryv3 b) const {
204 : return a.tile_id < b.tile_id;
205 : }
206 : } entryv3_cmp;
207 : #endif
208 :
209 : struct entry_zxy {
210 : uint8_t z;
211 : uint32_t x;
212 : uint32_t y;
213 : uint64_t offset;
214 : uint32_t length;
215 :
216 858 : entry_zxy(uint8_t _z, uint32_t _x, uint32_t _y, uint64_t _offset, uint32_t _length)
217 858 : : z(_z), x(_x), y(_y), offset(_offset), length(_length) {
218 858 : }
219 : };
220 :
221 : struct varint_too_long_exception : std::exception {
222 0 : const char *what() const noexcept override {
223 0 : return "varint too long exception";
224 : }
225 : };
226 :
227 : struct end_of_buffer_exception : std::exception {
228 0 : const char *what() const noexcept override {
229 0 : return "end of buffer exception";
230 : }
231 : };
232 :
233 : namespace {
234 : constexpr const int8_t max_varint_length = sizeof(uint64_t) * 8 / 7 + 1;
235 :
236 : // from https://github.com/mapbox/protozero/blob/master/include/protozero/varint.hpp
237 9638 : uint64_t decode_varint_impl(const char **data, const char *end) {
238 9638 : const auto *begin = reinterpret_cast<const int8_t *>(*data);
239 9638 : const auto *iend = reinterpret_cast<const int8_t *>(end);
240 9638 : const int8_t *p = begin;
241 9638 : uint64_t val = 0;
242 :
243 9638 : if (iend - begin >= max_varint_length) { // fast path
244 : do {
245 8888 : int64_t b = *p++;
246 8888 : val = ((uint64_t(b) & 0x7fU));
247 8888 : if (b >= 0) {
248 0 : break;
249 : }
250 8888 : b = *p++;
251 8888 : val |= ((uint64_t(b) & 0x7fU) << 7U);
252 8888 : if (b >= 0) {
253 7790 : break;
254 : }
255 1098 : b = *p++;
256 1098 : val |= ((uint64_t(b) & 0x7fU) << 14U);
257 1098 : if (b >= 0) {
258 244 : break;
259 : }
260 854 : b = *p++;
261 854 : val |= ((uint64_t(b) & 0x7fU) << 21U);
262 854 : if (b >= 0) {
263 806 : break;
264 : }
265 48 : b = *p++;
266 48 : val |= ((uint64_t(b) & 0x7fU) << 28U);
267 48 : if (b >= 0) {
268 48 : break;
269 : }
270 0 : b = *p++;
271 0 : val |= ((uint64_t(b) & 0x7fU) << 35U);
272 0 : if (b >= 0) {
273 0 : break;
274 : }
275 0 : b = *p++;
276 0 : val |= ((uint64_t(b) & 0x7fU) << 42U);
277 0 : if (b >= 0) {
278 0 : break;
279 : }
280 0 : b = *p++;
281 0 : val |= ((uint64_t(b) & 0x7fU) << 49U);
282 0 : if (b >= 0) {
283 0 : break;
284 : }
285 0 : b = *p++;
286 0 : val |= ((uint64_t(b) & 0x7fU) << 56U);
287 0 : if (b >= 0) {
288 0 : break;
289 : }
290 0 : b = *p++;
291 0 : val |= ((uint64_t(b) & 0x01U) << 63U);
292 0 : if (b >= 0) {
293 0 : break;
294 : }
295 0 : throw varint_too_long_exception{};
296 : } while (false);
297 : } else {
298 750 : unsigned int shift = 0;
299 1572 : while (p != iend && *p < 0) {
300 822 : val |= (uint64_t(*p++) & 0x7fU) << shift;
301 822 : shift += 7;
302 : }
303 750 : if (p == iend) {
304 0 : throw end_of_buffer_exception{};
305 : }
306 750 : val |= uint64_t(*p++) << shift;
307 : }
308 :
309 9638 : *data = reinterpret_cast<const char *>(p);
310 9638 : return val;
311 : }
312 :
313 40020 : uint64_t decode_varint(const char **data, const char *end) {
314 : // If this is a one-byte varint, decode it here.
315 40020 : if (end != *data && ((static_cast<uint64_t>(**data) & 0x80U) == 0)) {
316 30382 : const auto val = static_cast<uint64_t>(**data);
317 30382 : ++(*data);
318 30382 : return val;
319 : }
320 : // If this varint is more than one byte, defer to complete implementation.
321 9638 : return decode_varint_impl(data, end);
322 : }
323 :
324 34397 : void rotate(int64_t n, int64_t &x, int64_t &y, int64_t rx, int64_t ry) {
325 34397 : if (ry == 0) {
326 16742 : if (rx == 1) {
327 7291 : x = n - 1 - x;
328 7291 : y = n - 1 - y;
329 : }
330 16742 : int64_t t = x;
331 16742 : x = y;
332 16742 : y = t;
333 : }
334 34397 : }
335 :
336 2669 : zxy t_on_level(uint8_t z, uint64_t pos) {
337 2669 : int64_t n = 1LL << z;
338 2669 : int64_t rx, ry, s, t = pos;
339 2669 : int64_t tx = 0;
340 2669 : int64_t ty = 0;
341 :
342 24610 : for (s = 1; s < n; s *= 2) {
343 21941 : rx = 1LL & (t / 2);
344 21941 : ry = 1LL & (t ^ rx);
345 21941 : rotate(s, tx, ty, rx, ry);
346 21941 : tx += s * rx;
347 21941 : ty += s * ry;
348 21941 : t /= 4;
349 : }
350 2669 : return zxy(z, static_cast<int>(tx), static_cast<int>(ty));
351 : }
352 :
353 1035 : int write_varint(std::back_insert_iterator<std::string> data, uint64_t value) {
354 1035 : int n = 1;
355 :
356 1355 : while (value >= 0x80U) {
357 320 : *data++ = char((value & 0x7fU) | 0x80U);
358 320 : value >>= 7U;
359 320 : ++n;
360 : }
361 1035 : *data = char(value);
362 :
363 1035 : return n;
364 : }
365 :
366 : // TMS order
367 : struct {
368 0 : bool operator()(entry_zxy a, entry_zxy b) const {
369 0 : if (a.z != b.z) {
370 0 : return a.z < b.z;
371 : }
372 0 : if (a.x != b.x) {
373 0 : return a.x < b.x;
374 : }
375 0 : return a.y > b.y;
376 : }
377 : } colmajor_cmp;
378 :
379 : // use a 0 length entry as a null value.
380 0 : entryv3 find_tile(const std::vector<entryv3> &entries, uint64_t tile_id) {
381 0 : int m = 0;
382 0 : int n = static_cast<int>(entries.size()) - 1;
383 0 : while (m <= n) {
384 0 : int k = (n + m) >> 1;
385 0 : if (tile_id > entries[k].tile_id) {
386 0 : m = k + 1;
387 0 : } else if (tile_id < entries[k].tile_id) {
388 0 : n = k - 1;
389 : } else {
390 0 : return entries[k];
391 : }
392 : }
393 :
394 0 : if (n >= 0) {
395 0 : if (entries[n].run_length == 0) {
396 0 : return entries[n];
397 : }
398 0 : if (tile_id - entries[n].tile_id < entries[n].run_length) {
399 0 : return entries[n];
400 : }
401 : }
402 :
403 0 : return entryv3{0, 0, 0, 0};
404 : }
405 :
406 : } // end anonymous namespace
407 :
408 2669 : inline zxy tileid_to_zxy(uint64_t tileid) {
409 2669 : uint64_t acc = 0;
410 24610 : for (uint8_t t_z = 0; t_z < 32; t_z++) {
411 24610 : uint64_t num_tiles = (1LL << t_z) * (1LL << t_z);
412 24610 : if (acc + num_tiles > tileid) {
413 2669 : return t_on_level(t_z, tileid - acc);
414 : }
415 21941 : acc += num_tiles;
416 : }
417 0 : throw std::overflow_error("tile zoom exceeds 64-bit limit");
418 : }
419 :
420 1833 : inline uint64_t zxy_to_tileid(uint8_t z, uint32_t x, uint32_t y) {
421 1833 : if (z > 31) {
422 0 : throw std::overflow_error("tile zoom exceeds 64-bit limit");
423 : }
424 1833 : if (x > (1U << z) - 1U || y > (1U << z) - 1U) {
425 2 : throw std::overflow_error("tile x/y outside zoom level bounds");
426 : }
427 1831 : uint64_t acc = 0;
428 14287 : for (uint8_t t_z = 0; t_z < z; t_z++) acc += (1LL << t_z) * (1LL << t_z);
429 1831 : int64_t n = 1LL << z;
430 1831 : int64_t rx, ry, s, d = 0;
431 1831 : int64_t tx = x;
432 1831 : int64_t ty = y;
433 14287 : for (s = n / 2; s > 0; s /= 2) {
434 12456 : rx = (tx & s) > 0;
435 12456 : ry = (ty & s) > 0;
436 12456 : d += s * s * ((3LL * rx) ^ ry);
437 12456 : rotate(s, tx, ty, rx, ry);
438 : }
439 1831 : return acc + d;
440 : }
441 :
442 : // returns an uncompressed byte buffer
443 19 : inline std::string serialize_directory(const std::vector<entryv3> &entries) {
444 19 : std::string data;
445 :
446 19 : write_varint(std::back_inserter(data), entries.size());
447 :
448 19 : uint64_t last_id = 0;
449 273 : for (auto const &entry : entries) {
450 254 : write_varint(std::back_inserter(data), entry.tile_id - last_id);
451 254 : last_id = entry.tile_id;
452 : }
453 :
454 273 : for (auto const &entry : entries) {
455 254 : write_varint(std::back_inserter(data), entry.run_length);
456 : }
457 :
458 273 : for (auto const &entry : entries) {
459 254 : write_varint(std::back_inserter(data), entry.length);
460 : }
461 :
462 273 : for (size_t i = 0; i < entries.size(); i++) {
463 254 : if (i > 0 && entries[i].offset == entries[i - 1].offset + entries[i - 1].length) {
464 191 : write_varint(std::back_inserter(data), 0);
465 : } else {
466 63 : write_varint(std::back_inserter(data), entries[i].offset + 1);
467 : }
468 : }
469 :
470 19 : return data;
471 : }
472 :
473 : struct malformed_directory_exception : std::exception {
474 0 : const char *what() const noexcept override {
475 0 : return "malformed directory exception";
476 : }
477 : };
478 :
479 : // takes an uncompressed byte buffer
480 456 : inline std::vector<entryv3> deserialize_directory(const std::string &decompressed) {
481 456 : const char *t = decompressed.data();
482 456 : const char *end = t + decompressed.size();
483 :
484 456 : const uint64_t num_entries_64bit = decode_varint(&t, end);
485 : // Sanity check to avoid excessive memory allocation attempt:
486 : // each directory entry takes at least 4 bytes
487 456 : if (num_entries_64bit / 4U > decompressed.size()) {
488 0 : throw malformed_directory_exception();
489 : }
490 456 : const size_t num_entries = static_cast<size_t>(num_entries_64bit);
491 :
492 456 : std::vector<entryv3> result;
493 456 : result.resize(num_entries);
494 :
495 456 : uint64_t last_id = 0;
496 10347 : for (size_t i = 0; i < num_entries; i++) {
497 9891 : const uint64_t val = decode_varint(&t, end);
498 9891 : if (val > std::numeric_limits<uint64_t>::max() - last_id) {
499 0 : throw malformed_directory_exception();
500 : }
501 9891 : const uint64_t tile_id = last_id + val;
502 9891 : result[i].tile_id = tile_id;
503 9891 : last_id = tile_id;
504 : }
505 :
506 10347 : for (size_t i = 0; i < num_entries; i++) {
507 9891 : const uint64_t val = decode_varint(&t, end);
508 9891 : if (val > std::numeric_limits<uint32_t>::max()) {
509 0 : throw malformed_directory_exception();
510 : }
511 9891 : result[i].run_length = static_cast<uint32_t>(val);
512 : }
513 :
514 10347 : for (size_t i = 0; i < num_entries; i++) {
515 9891 : const uint64_t val = decode_varint(&t, end);
516 9891 : if (val > std::numeric_limits<uint32_t>::max()) {
517 0 : throw malformed_directory_exception();
518 : }
519 9891 : result[i].length = static_cast<uint32_t>(val);
520 : }
521 :
522 10347 : for (size_t i = 0; i < num_entries; i++) {
523 9891 : uint64_t tmp = decode_varint(&t, end);
524 :
525 9891 : if (i > 0 && tmp == 0) {
526 8328 : if (result[i - 1].offset > std::numeric_limits<uint64_t>::max() - result[i - 1].length) {
527 0 : throw malformed_directory_exception();
528 : }
529 8328 : result[i].offset = result[i - 1].offset + result[i - 1].length;
530 : } else {
531 1563 : result[i].offset = tmp - 1;
532 : }
533 : }
534 :
535 : // assert the directory has been fully consumed
536 456 : if (t != end) {
537 0 : throw malformed_directory_exception();
538 : }
539 :
540 912 : return result;
541 : }
542 :
543 0 : inline std::tuple<std::string, std::string, int> build_root_leaves(const std::function<std::string(const std::string &, uint8_t)> mycompress, uint8_t compression, const std::vector<pmtiles::entryv3> &entries, int leaf_size) {
544 0 : std::vector<pmtiles::entryv3> root_entries;
545 0 : std::string leaves_bytes;
546 0 : int num_leaves = 0;
547 0 : for (size_t i = 0; i < entries.size(); i += leaf_size) {
548 0 : num_leaves++;
549 0 : size_t end = i + leaf_size;
550 0 : if (i + leaf_size > entries.size()) {
551 0 : end = entries.size();
552 : }
553 0 : std::vector<pmtiles::entryv3> subentries = {entries.begin() + i, entries.begin() + end};
554 0 : auto uncompressed_leaf = pmtiles::serialize_directory(subentries);
555 0 : auto compressed_leaf = mycompress(uncompressed_leaf, compression);
556 0 : root_entries.emplace_back(entries[i].tile_id, leaves_bytes.size(), static_cast<uint32_t>(compressed_leaf.size()), 0);
557 0 : leaves_bytes += compressed_leaf;
558 : }
559 0 : auto uncompressed_root = pmtiles::serialize_directory(root_entries);
560 0 : auto compressed_root = mycompress(uncompressed_root, compression);
561 0 : return std::make_tuple(compressed_root, leaves_bytes, num_leaves);
562 : }
563 :
564 19 : inline std::tuple<std::string, std::string, int> make_root_leaves(const std::function<std::string(const std::string &, uint8_t)> mycompress, uint8_t compression, const std::vector<pmtiles::entryv3> &entries) {
565 38 : auto test_bytes = pmtiles::serialize_directory(entries);
566 38 : auto compressed = mycompress(test_bytes, compression);
567 19 : if (compressed.size() <= 16384 - 127) {
568 38 : return std::make_tuple(compressed, "", 0);
569 : }
570 0 : int leaf_size = 4096;
571 : while (true) {
572 0 : std::string root_bytes;
573 0 : std::string leaves_bytes;
574 : int num_leaves;
575 0 : std::tie(root_bytes, leaves_bytes, num_leaves) = build_root_leaves(mycompress, compression, entries, leaf_size);
576 0 : if (root_bytes.length() < 16384 - 127) {
577 0 : return std::make_tuple(root_bytes, leaves_bytes, num_leaves);
578 : }
579 0 : leaf_size *= 2;
580 0 : }
581 : }
582 :
583 : inline void collect_entries(const std::function<std::string(const std::string &, uint8_t)> decompress, std::vector<entry_zxy> &tile_entries, const char *pmtiles_map, const headerv3 &h, uint64_t dir_offset, uint64_t dir_len) {
584 : std::string dir_s{pmtiles_map + dir_offset, static_cast<size_t>(dir_len)};
585 : std::string decompressed_dir = decompress(dir_s, h.internal_compression);
586 :
587 : auto dir_entries = pmtiles::deserialize_directory(decompressed_dir);
588 : for (auto const &entry : dir_entries) {
589 : if (entry.run_length == 0) {
590 : collect_entries(decompress, tile_entries, pmtiles_map, h, h.leaf_dirs_offset + entry.offset, entry.length);
591 : } else {
592 : for (uint64_t i = entry.tile_id; i < entry.tile_id + entry.run_length; i++) {
593 : pmtiles::zxy zxy = pmtiles::tileid_to_zxy(i);
594 : tile_entries.emplace_back(zxy.z, zxy.x, zxy.y, h.tile_data_offset + entry.offset, entry.length);
595 : }
596 : }
597 : }
598 : }
599 :
600 : inline std::vector<entry_zxy> entries_tms(const std::function<std::string(const std::string &, uint8_t)> decompress, const char *pmtiles_map) {
601 : std::string header_s{pmtiles_map, 127};
602 : auto header = pmtiles::deserialize_header(header_s);
603 :
604 : std::vector<entry_zxy> tile_entries;
605 :
606 : collect_entries(decompress, tile_entries, pmtiles_map, header, header.root_dir_offset, header.root_dir_bytes);
607 : std::sort(tile_entries.begin(), tile_entries.end(), colmajor_cmp);
608 : return tile_entries;
609 : }
610 :
611 : inline std::pair<uint64_t, uint32_t> get_tile(const std::function<std::string(const std::string &, uint8_t)> decompress, const char *pmtiles_map, uint8_t z, uint32_t x, uint32_t y) {
612 : uint64_t tile_id = pmtiles::zxy_to_tileid(z, x, y);
613 :
614 : std::string header_s{pmtiles_map, 127};
615 : auto h = pmtiles::deserialize_header(header_s);
616 :
617 : uint64_t dir_offset = h.root_dir_offset;
618 : if (h.root_dir_bytes > std::numeric_limits<uint32_t>::max()) {
619 : throw malformed_directory_exception();
620 : }
621 : uint32_t dir_length = static_cast<uint32_t>(h.root_dir_bytes);
622 : for (int depth = 0; depth <= 3; depth++) {
623 : std::string dir_s{pmtiles_map + dir_offset, dir_length};
624 : std::string decompressed_dir = decompress(dir_s, h.internal_compression);
625 : auto dir_entries = pmtiles::deserialize_directory(decompressed_dir);
626 : auto entry = find_tile(dir_entries, tile_id);
627 :
628 : if (entry.length > 0) {
629 : if (entry.run_length > 0) {
630 : return std::make_pair(h.tile_data_offset + entry.offset, entry.length);
631 : } else {
632 : dir_offset = h.leaf_dirs_offset + entry.offset;
633 : dir_length = entry.length;
634 : }
635 : } else {
636 : return std::make_pair(0, 0);
637 : }
638 : }
639 :
640 : return std::make_pair(0, 0);
641 : }
642 :
643 : } // namespace pmtiles
644 : #endif
|