Skip to content

Commit af1a7ed

Browse files
authored
Once features can't possibly fit in a tile, stop trying (#9)
* Stop adding features to a tile if it can't possibly work * Add --integer and --fraction options to tippecanoe-decode * Carry the strategies field from tileset metadata through tile-join * Update changelog * Assign different codes to different kinds of error exits
1 parent ed31b9a commit af1a7ed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+783
-489
lines changed

CHANGELOG.md

+7-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1+
## 2.6.2
2+
3+
* Stop adding features to a tile if it can't possibly work, to limit memory use
4+
* Add --integer and --fraction options to tippecanoe-decode
5+
* Carry `strategies` field from tileset metadata through tile-join
6+
17
## 2.6.1
28

3-
Upgrade protozero to version 1.7.1
9+
* Upgrade protozero to version 1.7.1
410

511
## 2.6.0
612

Makefile

+4
Original file line numberDiff line numberDiff line change
@@ -160,10 +160,14 @@ decode-test:
160160
mkdir -p tests/muni/decode
161161
./tippecanoe -q -z11 -Z11 -f -o tests/muni/decode/multi.mbtiles tests/muni/*.json
162162
./tippecanoe-decode -x generator -l subway tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.json.check
163+
./tippecanoe-decode -x generator -l subway --integer tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.integer.json.check
164+
./tippecanoe-decode -x generator -l subway --fraction tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.fraction.json.check
163165
./tippecanoe-decode -x generator -c tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.pipeline.json.check
164166
./tippecanoe-decode -x generator tests/muni/decode/multi.mbtiles 11 327 791 > tests/muni/decode/multi.mbtiles.onetile.json.check
165167
./tippecanoe-decode -x generator --stats tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.stats.json.check
166168
cmp tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles.json
169+
cmp tests/muni/decode/multi.mbtiles.integer.json.check tests/muni/decode/multi.mbtiles.integer.json
170+
cmp tests/muni/decode/multi.mbtiles.fraction.json.check tests/muni/decode/multi.mbtiles.fraction.json
167171
cmp tests/muni/decode/multi.mbtiles.pipeline.json.check tests/muni/decode/multi.mbtiles.pipeline.json
168172
cmp tests/muni/decode/multi.mbtiles.onetile.json.check tests/muni/decode/multi.mbtiles.onetile.json
169173
cmp tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.stats.json

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -880,6 +880,8 @@ resolutions.
880880
* `-c` or `--tag-layer-and-zoom`: Include each feature's layer and zoom level as part of its `tippecanoe` object rather than as a FeatureCollection wrapper
881881
* `-S` or `--stats`: Just report statistics about each tile's size and the number of features in it, as a JSON structure.
882882
* `-f` or `--force`: Decode tiles even if polygon ring order or closure problems are detected
883+
* `-I` or `--integer`: Report coordinates in integer tile coordinates
884+
* `-F` or `--fraction`: Report coordinates as a fraction of the tile extent
883885

884886
tippecanoe-json-tool
885887
====================

csv.cpp

+5-4
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "csv.hpp"
22
#include "text.hpp"
3+
#include "errors.hpp"
34

45
std::vector<std::string> csv_split(const char *s) {
56
std::vector<std::string> ret;
@@ -68,15 +69,15 @@ void readcsv(const char *fn, std::vector<std::string> &header, std::map<std::str
6869
FILE *f = fopen(fn, "r");
6970
if (f == NULL) {
7071
perror(fn);
71-
exit(EXIT_FAILURE);
72+
exit(EXIT_OPEN);
7273
}
7374

7475
std::string s;
7576
if ((s = csv_getline(f)).size() > 0) {
7677
std::string err = check_utf8(s);
7778
if (err != "") {
7879
fprintf(stderr, "%s: %s\n", fn, err.c_str());
79-
exit(EXIT_FAILURE);
80+
exit(EXIT_UTF8);
8081
}
8182

8283
header = csv_split(s.c_str());
@@ -89,7 +90,7 @@ void readcsv(const char *fn, std::vector<std::string> &header, std::map<std::str
8990
std::string err = check_utf8(s);
9091
if (err != "") {
9192
fprintf(stderr, "%s: %s\n", fn, err.c_str());
92-
exit(EXIT_FAILURE);
93+
exit(EXIT_UTF8);
9394
}
9495

9596
std::vector<std::string> line = csv_split(s.c_str());
@@ -105,7 +106,7 @@ void readcsv(const char *fn, std::vector<std::string> &header, std::map<std::str
105106

106107
if (fclose(f) != 0) {
107108
perror("fclose");
108-
exit(EXIT_FAILURE);
109+
exit(EXIT_CLOSE);
109110
}
110111
}
111112

decode.cpp

+45-27
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
#include "write_json.hpp"
2323
#include "jsonpull/jsonpull.h"
2424
#include "dirtiles.hpp"
25+
#include "errors.hpp"
2526

2627
int minzoom = 0;
2728
int maxzoom = 32;
@@ -85,18 +86,18 @@ void do_stats(mvt_tile &tile, size_t size, bool compressed, int z, unsigned x, u
8586
state.json_write_newline();
8687
}
8788

88-
void handle(std::string message, int z, unsigned x, unsigned y, std::set<std::string> const &to_decode, bool pipeline, bool stats, json_writer &state) {
89+
void handle(std::string message, int z, unsigned x, unsigned y, std::set<std::string> const &to_decode, bool pipeline, bool stats, json_writer &state, int coordinate_mode) {
8990
mvt_tile tile;
9091
bool was_compressed;
9192

9293
try {
9394
if (!tile.decode(message, was_compressed)) {
9495
fprintf(stderr, "Couldn't parse tile %d/%u/%u\n", z, x, y);
95-
exit(EXIT_FAILURE);
96+
exit(EXIT_MVT);
9697
}
9798
} catch (std::exception const &e) {
9899
fprintf(stderr, "PBF decoding error in tile %d/%u/%u\n", z, x, y);
99-
exit(EXIT_FAILURE);
100+
exit(EXIT_PROTOBUF);
100101
}
101102

102103
if (stats) {
@@ -159,7 +160,7 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::set<std::st
159160

160161
if (layer.extent <= 0) {
161162
fprintf(stderr, "Impossible layer extent %lld in mbtiles\n", layer.extent);
162-
exit(EXIT_FAILURE);
163+
exit(EXIT_IMPOSSIBLE);
163164
}
164165

165166
if (to_decode.size() != 0 && !to_decode.count(layer.name)) {
@@ -202,10 +203,16 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::set<std::st
202203
// X and Y are unsigned, so no need to check <0
203204
if (x > (1ULL << z) || y > (1ULL << z)) {
204205
fprintf(stderr, "Impossible tile %d/%u/%u\n", z, x, y);
205-
exit(EXIT_FAILURE);
206+
exit(EXIT_IMPOSSIBLE);
206207
}
207208

208-
layer_to_geojson(layer, z, x, y, !pipeline, pipeline, pipeline, false, 0, 0, 0, !force, state);
209+
double scale = 0;
210+
if (coordinate_mode == 1) { // fraction
211+
scale = layer.extent;
212+
} else if (coordinate_mode == 2) { // integer
213+
scale = 1;
214+
}
215+
layer_to_geojson(layer, z, x, y, !pipeline, pipeline, pipeline, false, 0, 0, 0, !force, state, scale);
209216

210217
if (!pipeline) {
211218
if (true) {
@@ -223,7 +230,7 @@ void handle(std::string message, int z, unsigned x, unsigned y, std::set<std::st
223230
}
224231
}
225232

226-
void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> const &to_decode, bool pipeline, bool stats, std::set<std::string> const &exclude_meta) {
233+
void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> const &to_decode, bool pipeline, bool stats, std::set<std::string> const &exclude_meta, int coordinate_mode) {
227234
sqlite3 *db = NULL;
228235
bool isdir = false;
229236
int oz = z;
@@ -240,12 +247,12 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
240247
if (strcmp(map, "SQLite format 3") != 0) {
241248
if (z >= 0) {
242249
std::string s = std::string(map, st.st_size);
243-
handle(s, z, x, y, to_decode, pipeline, stats, state);
250+
handle(s, z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
244251
munmap(map, st.st_size);
245252
return;
246253
} else {
247254
fprintf(stderr, "Must specify zoom/x/y to decode a single pbf file\n");
248-
exit(EXIT_FAILURE);
255+
exit(EXIT_ARGS);
249256
}
250257
}
251258
}
@@ -256,7 +263,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
256263
}
257264
if (close(fd) != 0) {
258265
perror("close");
259-
exit(EXIT_FAILURE);
266+
exit(EXIT_CLOSE);
260267
}
261268
} else {
262269
perror(fname);
@@ -272,13 +279,13 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
272279
} else {
273280
if (sqlite3_open(fname, &db) != SQLITE_OK) {
274281
fprintf(stderr, "%s: %s\n", fname, sqlite3_errmsg(db));
275-
exit(EXIT_FAILURE);
282+
exit(EXIT_OPEN);
276283
}
277284

278285
char *err = NULL;
279286
if (sqlite3_exec(db, "PRAGMA integrity_check;", NULL, NULL, &err) != SQLITE_OK) {
280287
fprintf(stderr, "%s: integrity_check: %s\n", fname, err);
281-
exit(EXIT_FAILURE);
288+
exit(EXIT_SQLITE);
282289
}
283290
}
284291

@@ -299,7 +306,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
299306
sqlite3_stmt *stmt2;
300307
if (sqlite3_prepare_v2(db, sql2, -1, &stmt2, NULL) != SQLITE_OK) {
301308
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
302-
exit(EXIT_FAILURE);
309+
exit(EXIT_SQLITE);
303310
}
304311

305312
while (sqlite3_step(stmt2) == SQLITE_ROW) {
@@ -308,7 +315,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
308315

309316
if (name == NULL || value == NULL) {
310317
fprintf(stderr, "Corrupt mbtiles file: null metadata\n");
311-
exit(EXIT_FAILURE);
318+
exit(EXIT_SQLITE);
312319
}
313320

314321
if (exclude_meta.count((char *) name) == 0) {
@@ -361,7 +368,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
361368
FILE *f = fopen(fn.c_str(), "rb");
362369
if (f == NULL) {
363370
perror(fn.c_str());
364-
exit(EXIT_FAILURE);
371+
exit(EXIT_OPEN);
365372
}
366373

367374
std::string s;
@@ -372,14 +379,14 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
372379
}
373380
fclose(f);
374381

375-
handle(s, tiles[i].z, tiles[i].x, tiles[i].y, to_decode, pipeline, stats, state);
382+
handle(s, tiles[i].z, tiles[i].x, tiles[i].y, to_decode, pipeline, stats, state, coordinate_mode);
376383
}
377384
} else {
378385
const char *sql = "SELECT tile_data, zoom_level, tile_column, tile_row from tiles where zoom_level between ? and ? order by zoom_level, tile_column, tile_row;";
379386
sqlite3_stmt *stmt;
380387
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
381388
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
382-
exit(EXIT_FAILURE);
389+
exit(EXIT_SQLITE);
383390
}
384391

385392
sqlite3_bind_int(stmt, 1, minzoom);
@@ -407,18 +414,18 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
407414

408415
if (tz < 0 || tz >= 32) {
409416
fprintf(stderr, "Impossible zoom level %d in mbtiles\n", tz);
410-
exit(EXIT_FAILURE);
417+
exit(EXIT_IMPOSSIBLE);
411418
}
412419

413420
ty = (1LL << tz) - 1 - ty;
414421
const char *s = (const char *) sqlite3_column_blob(stmt, 0);
415422

416423
if (s == NULL) {
417424
fprintf(stderr, "Corrupt mbtiles file: null entry in tiles table\n");
418-
exit(EXIT_FAILURE);
425+
exit(EXIT_SQLITE);
419426
}
420427

421-
handle(std::string(s, len), tz, tx, ty, to_decode, pipeline, stats, state);
428+
handle(std::string(s, len), tz, tx, ty, to_decode, pipeline, stats, state, coordinate_mode);
422429
}
423430

424431
sqlite3_finalize(stmt);
@@ -443,7 +450,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
443450
sqlite3_stmt *stmt;
444451
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
445452
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
446-
exit(EXIT_FAILURE);
453+
exit(EXIT_SQLITE);
447454
}
448455

449456
sqlite3_bind_int(stmt, 1, z);
@@ -456,14 +463,14 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
456463

457464
if (s == NULL) {
458465
fprintf(stderr, "Corrupt mbtiles file: null entry in tiles table\n");
459-
exit(EXIT_FAILURE);
466+
exit(EXIT_SQLITE);
460467
}
461468

462469
if (z != oz) {
463470
fprintf(stderr, "%s: Warning: using tile %d/%u/%u instead of %d/%u/%u\n", fname, z, x, y, oz, ox, oy);
464471
}
465472

466-
handle(std::string(s, len), z, x, y, to_decode, pipeline, stats, state);
473+
handle(std::string(s, len), z, x, y, to_decode, pipeline, stats, state, coordinate_mode);
467474
handled = 1;
468475
}
469476

@@ -477,13 +484,13 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
477484

478485
if (sqlite3_close(db) != SQLITE_OK) {
479486
fprintf(stderr, "%s: could not close database: %s\n", fname, sqlite3_errmsg(db));
480-
exit(EXIT_FAILURE);
487+
exit(EXIT_CLOSE);
481488
}
482489
}
483490

484491
void usage(char **argv) {
485492
fprintf(stderr, "Usage: %s [-s projection] [-Z minzoom] [-z maxzoom] [-l layer ...] file.mbtiles [zoom x y]\n", argv[0]);
486-
exit(EXIT_FAILURE);
493+
exit(EXIT_ARGS);
487494
}
488495

489496
int main(int argc, char **argv) {
@@ -494,9 +501,12 @@ int main(int argc, char **argv) {
494501
bool pipeline = false;
495502
bool stats = false;
496503
std::set<std::string> exclude_meta;
504+
int coordinate_mode = 0;
497505

498506
struct option long_options[] = {
499507
{"projection", required_argument, 0, 's'},
508+
{"fractional-coordinates", no_argument, 0, 'F'},
509+
{"integer-coordinates", no_argument, 0, 'I'},
500510
{"maximum-zoom", required_argument, 0, 'z'},
501511
{"minimum-zoom", required_argument, 0, 'Z'},
502512
{"layer", required_argument, 0, 'l'},
@@ -527,6 +537,14 @@ int main(int argc, char **argv) {
527537
set_projection_or_exit(optarg);
528538
break;
529539

540+
case 'F':
541+
coordinate_mode = 1;
542+
break;
543+
544+
case 'I':
545+
coordinate_mode = 2;
546+
break;
547+
530548
case 'z':
531549
maxzoom = atoi(optarg);
532550
break;
@@ -561,9 +579,9 @@ int main(int argc, char **argv) {
561579
}
562580

563581
if (argc == optind + 4) {
564-
decode(argv[optind], atoi(argv[optind + 1]), atoi(argv[optind + 2]), atoi(argv[optind + 3]), to_decode, pipeline, stats, exclude_meta);
582+
decode(argv[optind], atoi(argv[optind + 1]), atoi(argv[optind + 2]), atoi(argv[optind + 3]), to_decode, pipeline, stats, exclude_meta, coordinate_mode);
565583
} else if (argc == optind + 1) {
566-
decode(argv[optind], -1, -1, -1, to_decode, pipeline, stats, exclude_meta);
584+
decode(argv[optind], -1, -1, -1, to_decode, pipeline, stats, exclude_meta, coordinate_mode);
567585
} else {
568586
usage(argv);
569587
}

0 commit comments

Comments
 (0)