Add option for newline-delimited output format to tippecanoe-decode

This commit is contained in:
Eric Fischer 2017-03-28 16:25:40 -07:00
parent eaff7b93c5
commit 834ba19277
9 changed files with 4962 additions and 69 deletions

View File

@ -116,8 +116,10 @@ decode-test:
mkdir -p tests/muni/decode
./tippecanoe -z11 -Z11 -f -o tests/muni/decode/multi.mbtiles tests/muni/*.json
./tippecanoe-decode -l subway tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.json.check
./tippecanoe-decode -c tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.pipeline.json.check
cmp tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles.json
rm -f tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles
cmp tests/muni/decode/multi.mbtiles.pipeline.json.check tests/muni/decode/multi.mbtiles.pipeline.json
rm -f tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles tests/muni/decode/multi.mbtiles.pipeline.json.check
pbf-test:
./tippecanoe-decode tests/pbf/11-328-791.vector.pbf 11 328 791 > tests/pbf/11-328-791.vector.pbf.out

View File

@ -440,3 +440,4 @@ resolutions.
* -z _maxzoom_: Specify the highest zoom level to decode from the tileset
* -Z _minzoom_: Specify the lowest zoom level to decode from the tileset
* -l _layer_: Decode only layers with the specified names. (Multiple `-l` options can be specified.)
* -c: Include each feature's layer and zoom level as part of its `tippecanoe` object rather than as a FeatureCollection wrapper

View File

@ -21,7 +21,7 @@
int minzoom = 0;
int maxzoom = 32;
void handle(std::string message, int z, unsigned x, unsigned y, int describe, std::set<std::string> const &to_decode) {
void handle(std::string message, int z, unsigned x, unsigned y, int describe, std::set<std::string> const &to_decode, bool pipeline) {
int within = 0;
mvt_tile tile;
@ -35,19 +35,21 @@ void handle(std::string message, int z, unsigned x, unsigned y, int describe, st
exit(EXIT_FAILURE);
}
printf("{ \"type\": \"FeatureCollection\"");
if (!pipeline) {
printf("{ \"type\": \"FeatureCollection\"");
if (describe) {
printf(", \"properties\": { \"zoom\": %d, \"x\": %d, \"y\": %d }", z, x, y);
if (describe) {
printf(", \"properties\": { \"zoom\": %d, \"x\": %d, \"y\": %d }", z, x, y);
if (projection != projections) {
printf(", \"crs\": { \"type\": \"name\", \"properties\": { \"name\": ");
fprintq(stdout, projection->alias);
printf(" } }");
if (projection != projections) {
printf(", \"crs\": { \"type\": \"name\", \"properties\": { \"name\": ");
fprintq(stdout, projection->alias);
printf(" } }");
}
}
}
printf(", \"features\": [\n");
printf(", \"features\": [\n");
}
bool first_layer = true;
for (size_t l = 0; l < tile.layers.size(); l++) {
@ -57,33 +59,39 @@ void handle(std::string message, int z, unsigned x, unsigned y, int describe, st
continue;
}
if (describe) {
if (!first_layer) {
printf(",\n");
if (!pipeline) {
if (describe) {
if (!first_layer) {
printf(",\n");
}
printf("{ \"type\": \"FeatureCollection\"");
printf(", \"properties\": { \"layer\": ");
fprintq(stdout, layer.name.c_str());
printf(", \"version\": %d, \"extent\": %lld", layer.version, layer.extent);
printf(" }");
printf(", \"features\": [\n");
first_layer = false;
within = 0;
}
printf("{ \"type\": \"FeatureCollection\"");
printf(", \"properties\": { \"layer\": ");
fprintq(stdout, layer.name.c_str());
printf(", \"version\": %d, \"extent\": %lld", layer.version, layer.extent);
printf(" }");
printf(", \"features\": [\n");
first_layer = false;
within = 0;
}
layer_to_geojson(stdout, layer, z, x, y, true, false, 0, 0, 0);
layer_to_geojson(stdout, layer, z, x, y, !pipeline, pipeline, pipeline, 0, 0, 0);
if (describe) {
printf("] }\n");
if (!pipeline) {
if (describe) {
printf("] }\n");
}
}
}
printf("] }\n");
if (!pipeline) {
printf("] }\n");
}
}
void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> const &to_decode) {
void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> const &to_decode, bool pipeline) {
sqlite3 *db;
int oz = z;
unsigned ox = x, oy = y;
@ -98,7 +106,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
if (strcmp(map, "SQLite format 3") != 0) {
if (z >= 0) {
std::string s = std::string(map, st.st_size);
handle(s, z, x, y, 1, to_decode);
handle(s, z, x, y, 1, to_decode, pipeline);
munmap(map, st.st_size);
return;
} else {
@ -126,32 +134,35 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
}
if (z < 0) {
printf("{ \"type\": \"FeatureCollection\", \"properties\": {\n");
const char *sql2 = "SELECT name, value from metadata order by name;";
sqlite3_stmt *stmt2;
if (sqlite3_prepare_v2(db, sql2, -1, &stmt2, NULL) != SQLITE_OK) {
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
exit(EXIT_FAILURE);
}
int within = 0;
while (sqlite3_step(stmt2) == SQLITE_ROW) {
if (within) {
printf(",\n");
if (!pipeline) {
printf("{ \"type\": \"FeatureCollection\", \"properties\": {\n");
const char *sql2 = "SELECT name, value from metadata order by name;";
sqlite3_stmt *stmt2;
if (sqlite3_prepare_v2(db, sql2, -1, &stmt2, NULL) != SQLITE_OK) {
fprintf(stderr, "%s: select failed: %s\n", fname, sqlite3_errmsg(db));
exit(EXIT_FAILURE);
}
within = 1;
const unsigned char *name = sqlite3_column_text(stmt2, 0);
const unsigned char *value = sqlite3_column_text(stmt2, 1);
while (sqlite3_step(stmt2) == SQLITE_ROW) {
if (within) {
printf(",\n");
}
within = 1;
fprintq(stdout, (char *) name);
printf(": ");
fprintq(stdout, (char *) value);
const unsigned char *name = sqlite3_column_text(stmt2, 0);
const unsigned char *value = sqlite3_column_text(stmt2, 1);
fprintq(stdout, (char *) name);
printf(": ");
fprintq(stdout, (char *) value);
}
sqlite3_finalize(stmt2);
}
sqlite3_finalize(stmt2);
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;";
sqlite3_stmt *stmt;
if (sqlite3_prepare_v2(db, sql, -1, &stmt, NULL) != SQLITE_OK) {
@ -162,14 +173,18 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
sqlite3_bind_int(stmt, 1, minzoom);
sqlite3_bind_int(stmt, 2, maxzoom);
printf("\n}, \"features\": [\n");
if (!pipeline) {
printf("\n}, \"features\": [\n");
}
within = 0;
while (sqlite3_step(stmt) == SQLITE_ROW) {
if (within) {
printf(",\n");
if (!pipeline) {
if (within) {
printf(",\n");
}
within = 1;
}
within = 1;
int len = sqlite3_column_bytes(stmt, 0);
int tz = sqlite3_column_int(stmt, 1);
@ -178,10 +193,12 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
ty = (1LL << tz) - 1 - ty;
const char *s = (const char *) sqlite3_column_blob(stmt, 0);
handle(std::string(s, len), tz, tx, ty, 1, to_decode);
handle(std::string(s, len), tz, tx, ty, 1, to_decode, pipeline);
}
printf("] }\n");
if (!pipeline) {
printf("] }\n");
}
sqlite3_finalize(stmt);
} else {
@ -206,7 +223,7 @@ void decode(char *fname, int z, unsigned x, unsigned y, std::set<std::string> co
fprintf(stderr, "%s: Warning: using tile %d/%u/%u instead of %d/%u/%u\n", fname, z, x, y, oz, ox, oy);
}
handle(std::string(s, len), z, x, y, 0, to_decode);
handle(std::string(s, len), z, x, y, 0, to_decode, pipeline);
handled = 1;
}
@ -234,8 +251,9 @@ int main(int argc, char **argv) {
extern char *optarg;
int i;
std::set<std::string> to_decode;
bool pipeline = false;
while ((i = getopt(argc, argv, "t:Z:z:l:")) != -1) {
while ((i = getopt(argc, argv, "t:Z:z:l:c")) != -1) {
switch (i) {
case 't':
set_projection_or_exit(optarg);
@ -253,15 +271,19 @@ int main(int argc, char **argv) {
to_decode.insert(optarg);
break;
case 'c':
pipeline = true;
break;
default:
usage(argv);
}
}
if (argc == optind + 4) {
decode(argv[optind], atoi(argv[optind + 1]), atoi(argv[optind + 2]), atoi(argv[optind + 3]), to_decode);
decode(argv[optind], atoi(argv[optind + 1]), atoi(argv[optind + 2]), atoi(argv[optind + 3]), to_decode, pipeline);
} else if (argc == optind + 1) {
decode(argv[optind], -1, -1, -1, to_decode);
decode(argv[optind], -1, -1, -1, to_decode, pipeline);
} else {
usage(argv);
}

View File

@ -162,6 +162,8 @@ which may not be what you want.
\-an or \-\-drop\-smallest\-as\-needed: Dynamically drop the smallest features (physically smallest: the shortest lines or the smallest polygons) from each zoom level to keep large tiles under the 500K size limit. This option will not work for point features.
.IP \(bu 2
\-aL or \-\-grid\-low\-zooms: At all zoom levels below \fImaxzoom\fP, snap all lines and polygons to a stairstep grid instead of allowing diagonals. You will also want to specify a tile resolution, probably \fB\fC\-D8\fR\&. This option provides a way to display continuous parcel, gridded, or binned data at low zooms without overwhelming the tiles with tiny polygons, since features will either get stretched out to the grid unit or lost entirely, depending on how they happened to be aligned in the original data.
.IP \(bu 2
\-aw or \-\-detect\-longitude\-wraparound: Detect when adjacent points within a feature jump to the other side of the world, and try to fix the geometry.
.RE
.SS Doing less
.RS
@ -178,7 +180,7 @@ which may not be what you want.
.IP \(bu 2
\-pi or \-\-preserve\-input\-order: Preserve the original input order of features as the drawing order instead of ordering geographically. (This is implemented as a restoration of the original order at the end, so that dot\-dropping is still geographic, which means it also undoes \-ao).
.IP \(bu 2
\-pp or \-\-no\-polygon\-splitting: Don't split complex polygons (over 700 vertices after simplification) into multiple features.
\-pp or \-\-no\-polygon\-splitting: This no longer has any effect.
.IP \(bu 2
\-pc or \-\-no\-clipping: Don't clip features to the size of the tile. If a feature overlaps the tile's bounds or buffer at all, it is included completely. Be careful: this can produce very large tilesets, especially with large polygons.
.IP \(bu 2
@ -336,9 +338,6 @@ have their probability diffused, so that some of them will be drawn as a square
this minimum size and others will not be drawn at all, preserving the total area that
all of them should have had together.
.PP
Any polygons that have over 700 vertices after line simplification will be split into
multiple features so they can be rendered efficiently, unless you use \-pp to prevent this.
.PP
Features in the same tile that share the same type and attributes are coalesced
together into a single geometry if you use \fB\fC\-\-coalesce\fR\&. You are strongly encouraged to use \-x to exclude
any unnecessary properties to reduce wasted file size.
@ -521,4 +520,6 @@ resolutions.
\-Z \fIminzoom\fP: Specify the lowest zoom level to decode from the tileset
.IP \(bu 2
\-l \fIlayer\fP: Decode only layers with the specified names. (Multiple \fB\fC\-l\fR options can be specified.)
.IP \(bu 2
\-c: Include each feature's layer and zoom level as part of its \fB\fCtippecanoe\fR object rather than as a FeatureCollection wrapper
.RE

View File

@ -51,7 +51,7 @@ void *run_writer(void *a) {
}
for (size_t i = 0; i < wa->layers->size(); i++) {
layer_to_geojson(fp, (*(wa->layers))[i], wa->z, wa->x, wa->y, false, true, 0, 0, 0);
layer_to_geojson(fp, (*(wa->layers))[i], wa->z, wa->x, wa->y, false, true, false, 0, 0, 0);
}
if (fclose(fp) != 0) {

File diff suppressed because it is too large Load Diff

View File

@ -1372,7 +1372,7 @@ void *run_prefilter(void *v) {
decode_meta(sf.m, sf.keys, sf.values, rpa->stringpool + rpa->pool_off[sf.segment], tmp_layer, tmp_feature);
tmp_layer.features.push_back(tmp_feature);
layer_to_geojson(rpa->prefilter_fp, tmp_layer, 0, 0, 0, false, true, sf.index, sf.seq, sf.extent);
layer_to_geojson(rpa->prefilter_fp, tmp_layer, 0, 0, 0, false, true, false, sf.index, sf.seq, sf.extent);
}
if (fclose(rpa->prefilter_fp) != 0) {

View File

@ -24,7 +24,7 @@ struct lonlat {
}
};
void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, unsigned y, bool comma, bool name, unsigned long long index, long long sequence, long long extent) {
void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, unsigned y, bool comma, bool name, bool zoom, unsigned long long index, long long sequence, long long extent) {
for (size_t f = 0; f < layer.features.size(); f++) {
mvt_feature const &feat = layer.features[f];
@ -38,7 +38,7 @@ void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x,
fprintf(fp, ", \"id\": %llu", feat.id);
}
if (name || index != 0 || sequence != 0 || extent != 0) {
if (name || zoom || index != 0 || sequence != 0 || extent != 0) {
bool need_comma = false;
fprintf(fp, ", \"tippecanoe\": { ");
@ -52,6 +52,15 @@ void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x,
need_comma = true;
}
if (zoom) {
if (need_comma) {
fprintf(fp, ", ");
}
fprintf(fp, "\"minzoom\": %u, ", z);
fprintf(fp, "\"maxzoom\": %u", z);
need_comma = true;
}
if (index != 0) {
if (need_comma) {
fprintf(fp, ", ");

View File

@ -1,2 +1,2 @@
void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, unsigned y, bool comma, bool name, unsigned long long index, long long sequence, long long extent);
void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, unsigned y, bool comma, bool name, bool zoom, unsigned long long index, long long sequence, long long extent);
void fprintq(FILE *f, const char *s);