diff --git a/CHANGELOG.md b/CHANGELOG.md index e0ffb962..11d885c9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,24 @@ +## 1.18.1 + +* Only warn once about invalid polygons in tippecanoe-decode + +## 1.18.0 + +* Fix compression of tiles in tile-join +* Calculate the tileset bounding box in tile-join from the tile boundaries + +## 1.17.7 + +* Enforce polygon winding and closure rules in tippecanoe-decode + +## 1.17.6 + +* Add tile-join options to set name, attribution, description + +## 1.17.5 + +* Preserve the tileset names from the source mbtiles in tile-join + ## 1.17.4 * Fix RFC 8142 support: Don't try to split *all* memory mapped files diff --git a/Makefile b/Makefile index 9e03e16e..a02adc8a 100644 --- a/Makefile +++ b/Makefile @@ -160,7 +160,7 @@ join-test: cmp tests/join-population/joined-i.mbtiles.json.check tests/join-population/joined-i.mbtiles.json cmp tests/join-population/merged.mbtiles.json.check tests/join-population/merged.mbtiles.json cmp tests/join-population/windows.mbtiles.json.check tests/join-population/windows.mbtiles.json - ./tile-join -f -l macarthur -o tests/join-population/just-macarthur.mbtiles tests/join-population/merged.mbtiles + ./tile-join -f -l macarthur -n "macarthur name" -N "macarthur description" -A "macarthur attribution" -o tests/join-population/just-macarthur.mbtiles tests/join-population/merged.mbtiles ./tile-join -f -L macarthur -o tests/join-population/no-macarthur.mbtiles tests/join-population/merged.mbtiles ./tippecanoe-decode tests/join-population/just-macarthur.mbtiles > tests/join-population/just-macarthur.mbtiles.json.check ./tippecanoe-decode tests/join-population/no-macarthur.mbtiles > tests/join-population/no-macarthur.mbtiles.json.check diff --git a/README.md b/README.md index dce516f3..d740d9da 100644 --- a/README.md +++ b/README.md @@ -431,6 +431,9 @@ The options are: * `-pk`: Don't skip tiles larger than 500K. * `-l` *layer*: Include the named layer in the output. You can specify multiple `-l` options to keep multiple layers. If you don't specify, they will all be retained. * `-L` *layer*: Remove the named layer from the output. You can specify multiple `-L` options to remove multiple layers. + * `-A` *attribution*: Set the attribution string. + * `-n` *name*: Set the tileset name. + * `-N` *description*: Set the tileset description. Because tile-join just copies the geometries to the new .mbtiles without processing them (except to rescale the extents if necessary), @@ -519,3 +522,4 @@ resolutions. * `-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 + * `-f`: Decode tiles even if polygon ring order or closure problems are detected diff --git a/decode.cpp b/decode.cpp index 28a1d898..30d67d41 100644 --- a/decode.cpp +++ b/decode.cpp @@ -20,13 +20,15 @@ int minzoom = 0; int maxzoom = 32; +bool force = false; void handle(std::string message, int z, unsigned x, unsigned y, int describe, std::set const &to_decode, bool pipeline) { int within = 0; mvt_tile tile; + bool was_compressed; try { - if (!tile.decode(message)) { + if (!tile.decode(message, was_compressed)) { fprintf(stderr, "Couldn't parse tile %d/%u/%u\n", z, x, y); exit(EXIT_FAILURE); } @@ -39,7 +41,11 @@ void handle(std::string message, int z, unsigned x, unsigned y, int describe, st printf("{ \"type\": \"FeatureCollection\""); if (describe) { - printf(", \"properties\": { \"zoom\": %d, \"x\": %d, \"y\": %d }", z, x, y); + printf(", \"properties\": { \"zoom\": %d, \"x\": %d, \"y\": %d", z, x, y); + if (!was_compressed) { + printf(", \"compressed\": false"); + } + printf(" }"); if (projection != projections) { printf(", \"crs\": { \"type\": \"name\", \"properties\": { \"name\": "); @@ -77,7 +83,7 @@ void handle(std::string message, int z, unsigned x, unsigned y, int describe, st } } - layer_to_geojson(stdout, layer, z, x, y, !pipeline, pipeline, pipeline, 0, 0, 0); + layer_to_geojson(stdout, layer, z, x, y, !pipeline, pipeline, pipeline, 0, 0, 0, !force); if (!pipeline) { if (describe) { @@ -253,7 +259,7 @@ int main(int argc, char **argv) { std::set to_decode; bool pipeline = false; - while ((i = getopt(argc, argv, "t:Z:z:l:c")) != -1) { + while ((i = getopt(argc, argv, "t:Z:z:l:cf")) != -1) { switch (i) { case 't': set_projection_or_exit(optarg); @@ -275,6 +281,10 @@ int main(int argc, char **argv) { pipeline = true; break; + case 'f': + force = true; + break; + default: usage(argv); } diff --git a/man/tippecanoe.1 b/man/tippecanoe.1 index 33830973..9160850f 100644 --- a/man/tippecanoe.1 +++ b/man/tippecanoe.1 @@ -505,6 +505,12 @@ The options are: \fB\fC\-l\fR \fIlayer\fP: Include the named layer in the output. You can specify multiple \fB\fC\-l\fR options to keep multiple layers. If you don't specify, they will all be retained. .IP \(bu 2 \fB\fC\-L\fR \fIlayer\fP: Remove the named layer from the output. You can specify multiple \fB\fC\-L\fR options to remove multiple layers. +.IP \(bu 2 +\fB\fC\-A\fR \fIattribution\fP: Set the attribution string. +.IP \(bu 2 +\fB\fC\-n\fR \fIname\fP: Set the tileset name. +.IP \(bu 2 +\fB\fC\-N\fR \fIdescription\fP: Set the tileset description. .RE .PP Because tile\-join just copies the geometries to the new .mbtiles without processing them @@ -612,4 +618,6 @@ resolutions. \fB\fC\-l\fR \fIlayer\fP: Decode only layers with the specified names. (Multiple \fB\fC\-l\fR options can be specified.) .IP \(bu 2 \fB\fC\-c\fR: Include each feature's layer and zoom level as part of its \fB\fCtippecanoe\fR object rather than as a FeatureCollection wrapper +.IP \(bu 2 +\fB\fC\-f\fR: Decode tiles even if polygon ring order or closure problems are detected .RE diff --git a/mvt.cpp b/mvt.cpp index bee74736..1f2c1aa5 100644 --- a/mvt.cpp +++ b/mvt.cpp @@ -82,7 +82,7 @@ int compress(std::string const &input, std::string &output) { return 0; } -bool mvt_tile::decode(std::string &message) { +bool mvt_tile::decode(std::string &message, bool &was_compressed) { layers.clear(); std::string src; @@ -90,8 +90,10 @@ bool mvt_tile::decode(std::string &message) { std::string uncompressed; decompress(message, uncompressed); src = uncompressed; + was_compressed = true; } else { src = message; + was_compressed = false; } protozero::pbf_reader reader(src); diff --git a/mvt.hpp b/mvt.hpp index 74c90ed4..052cf0d1 100644 --- a/mvt.hpp +++ b/mvt.hpp @@ -91,7 +91,7 @@ struct mvt_tile { std::vector layers; std::string encode(); - bool decode(std::string &message); + bool decode(std::string &message, bool &was_compressed); }; bool is_compressed(std::string const &data); diff --git a/plugin.cpp b/plugin.cpp index dfe0d087..10c40553 100644 --- a/plugin.cpp +++ b/plugin.cpp @@ -52,7 +52,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, false, 0, 0, 0); + layer_to_geojson(fp, (*(wa->layers))[i], wa->z, wa->x, wa->y, false, true, false, 0, 0, 0, false); } if (fclose(fp) != 0) { diff --git a/tests/join-population/joined-i.mbtiles.json b/tests/join-population/joined-i.mbtiles.json index d69dd187..6aa8bae1 100644 --- a/tests/join-population/joined-i.mbtiles.json +++ b/tests/join-population/joined-i.mbtiles.json @@ -1,12 +1,12 @@ { "type": "FeatureCollection", "properties": { -"bounds": "-122.334735,37.877356,-122.281639,37.898925", +"bounds": "-122.343750,37.857507,-122.255859,37.926868", "center": "-122.299805,37.892187,12", "description": "tests/join-population/tabblock_06001420.mbtiles", "format": "pbf", "json": "{\"vector_layers\": [ { \"id\": \"tabblock_06001420\", \"description\": \"\", \"minzoom\": 3, \"maxzoom\": 12, \"fields\": {\"ALAND10\": \"Number\", \"AWATER10\": \"Number\", \"BLOCKCE10\": \"String\", \"COUNTYFP10\": \"String\", \"FUNCSTAT10\": \"String\", \"INTPTLAT10\": \"String\", \"INTPTLON10\": \"String\", \"MTFCC10\": \"String\", \"NAME10\": \"String\", \"STATEFP10\": \"String\", \"TRACTCE10\": \"String\", \"UACE10\": \"String\", \"UATYP10\": \"String\", \"UR10\": \"String\", \"population\": \"Number\"} } ] }", "maxzoom": "12", "minzoom": "0", -"name": "tests/join-population/joined-i.mbtiles", +"name": "tests/join-population/tabblock_06001420.mbtiles", "type": "overlay", "version": "2" }, "features": [ diff --git a/tests/join-population/joined.mbtiles.json b/tests/join-population/joined.mbtiles.json index da4ed6c1..8aa68e29 100644 --- a/tests/join-population/joined.mbtiles.json +++ b/tests/join-population/joined.mbtiles.json @@ -1,12 +1,12 @@ { "type": "FeatureCollection", "properties": { -"bounds": "-122.334735,37.877356,-122.281639,37.898925", +"bounds": "-122.343750,37.857507,-122.255859,37.926868", "center": "-122.299805,37.892187,12", "description": "tests/join-population/tabblock_06001420.mbtiles", "format": "pbf", "json": "{\"vector_layers\": [ { \"id\": \"tabblock_06001420\", \"description\": \"\", \"minzoom\": 3, \"maxzoom\": 12, \"fields\": {\"ALAND10\": \"Number\", \"AWATER10\": \"Number\", \"BLOCKCE10\": \"String\", \"COUNTYFP10\": \"String\", \"FUNCSTAT10\": \"String\", \"INTPTLAT10\": \"String\", \"INTPTLON10\": \"String\", \"MTFCC10\": \"String\", \"NAME10\": \"String\", \"STATEFP10\": \"String\", \"TRACTCE10\": \"String\", \"UACE10\": \"String\", \"UATYP10\": \"String\", \"UR10\": \"String\", \"population\": \"Number\"} } ] }", "maxzoom": "12", "minzoom": "0", -"name": "tests/join-population/joined.mbtiles", +"name": "tests/join-population/tabblock_06001420.mbtiles", "type": "overlay", "version": "2" }, "features": [ diff --git a/tests/join-population/just-macarthur.mbtiles.json b/tests/join-population/just-macarthur.mbtiles.json index babe2001..a902f120 100644 --- a/tests/join-population/just-macarthur.mbtiles.json +++ b/tests/join-population/just-macarthur.mbtiles.json @@ -1,12 +1,13 @@ { "type": "FeatureCollection", "properties": { -"bounds": "-122.334735,37.695438,-122.104097,37.898925", +"attribution": "macarthur attribution", +"bounds": "-122.343750,37.695438,-122.104097,37.926868", "center": "-122.299805,37.892187,12", -"description": "tests/join-population/tabblock_06001420.mbtiles", +"description": "macarthur description", "format": "pbf", "json": "{\"vector_layers\": [ { \"id\": \"macarthur\", \"description\": \"\", \"minzoom\": 5, \"maxzoom\": 11, \"fields\": {\"FULLNAME\": \"String\", \"LINEARID\": \"String\", \"MTFCC\": \"String\", \"RTTYP\": \"String\"} } ] }", "maxzoom": "12", "minzoom": "0", -"name": "tests/join-population/just-macarthur.mbtiles", +"name": "macarthur name", "type": "overlay", "version": "2" }, "features": [ diff --git a/tests/join-population/merged.mbtiles.json b/tests/join-population/merged.mbtiles.json index ff5f3c8c..ea262268 100644 --- a/tests/join-population/merged.mbtiles.json +++ b/tests/join-population/merged.mbtiles.json @@ -1,12 +1,12 @@ { "type": "FeatureCollection", "properties": { -"bounds": "-122.334735,37.695438,-122.104097,37.898925", +"bounds": "-122.343750,37.695438,-122.104097,37.926868", "center": "-122.299805,37.892187,12", "description": "tests/join-population/tabblock_06001420.mbtiles", "format": "pbf", "json": "{\"vector_layers\": [ { \"id\": \"macarthur\", \"description\": \"\", \"minzoom\": 5, \"maxzoom\": 11, \"fields\": {\"FULLNAME\": \"String\", \"LINEARID\": \"String\", \"MTFCC\": \"String\", \"RTTYP\": \"String\"} }, { \"id\": \"tabblock_06001420\", \"description\": \"\", \"minzoom\": 3, \"maxzoom\": 12, \"fields\": {\"ALAND10\": \"Number\", \"AWATER10\": \"Number\", \"BLOCKCE10\": \"String\", \"COUNTYFP10\": \"String\", \"FUNCSTAT10\": \"String\", \"GEOID10\": \"String\", \"INTPTLAT10\": \"String\", \"INTPTLON10\": \"String\", \"MTFCC10\": \"String\", \"NAME10\": \"String\", \"STATEFP10\": \"String\", \"TRACTCE10\": \"String\", \"UACE10\": \"String\", \"UATYP10\": \"String\", \"UR10\": \"String\"} } ] }", "maxzoom": "12", "minzoom": "0", -"name": "tests/join-population/merged.mbtiles", +"name": "tests/join-population/macarthur.mbtiles + tests/join-population/macarthur2.mbtiles + tests/join-population/tabblock_06001420.mbtiles", "type": "overlay", "version": "2" }, "features": [ diff --git a/tests/join-population/no-macarthur.mbtiles.json b/tests/join-population/no-macarthur.mbtiles.json index 02884488..fa5a04c0 100644 --- a/tests/join-population/no-macarthur.mbtiles.json +++ b/tests/join-population/no-macarthur.mbtiles.json @@ -1,12 +1,12 @@ { "type": "FeatureCollection", "properties": { -"bounds": "-122.334735,37.695438,-122.104097,37.898925", +"bounds": "-122.343750,37.695438,-122.104097,37.926868", "center": "-122.299805,37.892187,12", "description": "tests/join-population/tabblock_06001420.mbtiles", "format": "pbf", "json": "{\"vector_layers\": [ { \"id\": \"tabblock_06001420\", \"description\": \"\", \"minzoom\": 3, \"maxzoom\": 12, \"fields\": {\"ALAND10\": \"Number\", \"AWATER10\": \"Number\", \"BLOCKCE10\": \"String\", \"COUNTYFP10\": \"String\", \"FUNCSTAT10\": \"String\", \"GEOID10\": \"String\", \"INTPTLAT10\": \"String\", \"INTPTLON10\": \"String\", \"MTFCC10\": \"String\", \"NAME10\": \"String\", \"STATEFP10\": \"String\", \"TRACTCE10\": \"String\", \"UACE10\": \"String\", \"UATYP10\": \"String\", \"UR10\": \"String\"} } ] }", "maxzoom": "12", "minzoom": "0", -"name": "tests/join-population/no-macarthur.mbtiles", +"name": "tests/join-population/macarthur.mbtiles + tests/join-population/macarthur2.mbtiles + tests/join-population/tabblock_06001420.mbtiles", "type": "overlay", "version": "2" }, "features": [ diff --git a/tests/join-population/windows.mbtiles.json b/tests/join-population/windows.mbtiles.json index 28740566..acdd7e95 100644 --- a/tests/join-population/windows.mbtiles.json +++ b/tests/join-population/windows.mbtiles.json @@ -1,12 +1,12 @@ { "type": "FeatureCollection", "properties": { -"bounds": "-122.294563,37.695438,-122.104097,37.833010", +"bounds": "-122.343750,37.439974,-121.992188,37.996163", "center": "-122.167969,37.833010,10", "description": "tests/join-population/macarthur.mbtiles", "format": "pbf", "json": "{\"vector_layers\": [ { \"id\": \"macarthur\", \"description\": \"\", \"minzoom\": 5, \"maxzoom\": 10, \"fields\": {\"FULLNAME\": \"String\", \"LINEARID\": \"String\", \"MTFCC\": \"String\", \"RTTYP\": \"String\"} } ] }", "maxzoom": "10", "minzoom": "5", -"name": "tests/join-population/windows.mbtiles", +"name": "tests/join-population/macarthur.mbtiles", "type": "overlay", "version": "2" }, "features": [ diff --git a/tests/overlap/out/-z0_-pC.json b/tests/overlap/out/-z0_-pC.json new file mode 100644 index 00000000..332ef696 --- /dev/null +++ b/tests/overlap/out/-z0_-pC.json @@ -0,0 +1,20 @@ +{ "type": "FeatureCollection", "properties": { +"bounds": "-135.000000,-31.000000,-3.000000,79.000000", +"center": "-3.000000,0.000000,0", +"description": "tests/overlap/out/-z0_-pC.json.check.mbtiles", +"format": "pbf", +"json": "{\"vector_layers\": [ { \"id\": \"in\", \"description\": \"\", \"minzoom\": 0, \"maxzoom\": 0, \"fields\": {} } ] }", +"maxzoom": "0", +"minzoom": "0", +"name": "tests/overlap/out/-z0_-pC.json.check.mbtiles", +"type": "overlay", +"version": "2" +}, "features": [ +{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0, "compressed": false }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "in", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -33.046875, 79.004962 ], [ -3.076172, 61.015725 ], [ -37.001953, 34.016242 ], [ -71.015625, 29.075375 ], [ -103.007812, 39.027719 ], [ -117.070312, 60.020952 ], [ -113.027344, 69.005675 ], [ -93.076172, 75.004940 ], [ -74.003906, 79.004962 ], [ -33.046875, 79.004962 ] ] ] } } +, +{ "type": "Feature", "properties": { }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -81.035156, 63.035039 ], [ -28.037109, 59.040555 ], [ -10.019531, 50.007739 ], [ -5.009766, 8.059230 ], [ -17.050781, -21.943046 ], [ -75.058594, -30.977609 ], [ -118.037109, -8.928487 ], [ -135.000000, 29.075375 ], [ -113.027344, 53.014783 ], [ -81.035156, 63.035039 ] ] ] } } +] } +] } +] } diff --git a/tile-join.cpp b/tile-join.cpp index 0543ceb1..aae612bd 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -32,8 +32,9 @@ struct stats { void handle(std::string message, int z, unsigned x, unsigned y, std::map &layermap, std::vector &header, std::map> &mapping, std::set &exclude, std::set &keep_layers, std::set &remove_layers, int ifmatched, mvt_tile &outtile) { mvt_tile tile; int features_added = 0; + bool was_compressed; - if (!tile.decode(message)) { + if (!tile.decode(message, was_compressed)) { fprintf(stderr, "Couldn't decompress tile %d/%u/%u\n", z, x, y); exit(EXIT_FAILURE); } @@ -402,7 +403,9 @@ void *join_worker(void *v) { } if (anything) { - std::string compressed = tile.encode(); + std::string pbf = tile.encode(); + std::string compressed; + compress(pbf, compressed); if (!pk && compressed.size() > 500000) { fprintf(stderr, "Tile %lld/%lld/%lld size is %lld, >500000. Skipping this tile\n.", ai->first.z, ai->first.x, ai->first.y, (long long) compressed.size()); @@ -464,7 +467,7 @@ void handle_tasks(std::map> &tasks, std::vector &layermap, sqlite3 *outdb, struct stats *st, std::vector &header, std::map> &mapping, std::set &exclude, int ifmatched, std::string &attribution, std::string &description, std::set &keep_layers, std::set &remove_layers) { +void decode(struct reader *readers, char *map, std::map &layermap, sqlite3 *outdb, struct stats *st, std::vector &header, std::map> &mapping, std::set &exclude, int ifmatched, std::string &attribution, std::string &description, std::set &keep_layers, std::set &remove_layers, std::string &name) { std::vector> layermaps; for (size_t i = 0; i < CPUS; i++) { layermaps.push_back(std::map()); @@ -472,11 +475,34 @@ void decode(struct reader *readers, char *map, std::map> tasks; + double minlat = INT_MAX; + double minlon = INT_MAX; + double maxlat = INT_MIN; + double maxlon = INT_MIN; + int zoom_for_bbox = -1; + while (readers != NULL && readers->zoom < 32) { reader *r = readers; readers = readers->next; r->next = NULL; + if (r->zoom != zoom_for_bbox) { + // Only use highest zoom for bbox calculation + // to avoid z0 always covering the world + + minlat = minlon = INT_MAX; + maxlat = maxlon = INT_MIN; + zoom_for_bbox = r->zoom; + } + + double lat1, lon1, lat2, lon2; + tile2lonlat(r->x, r->y, r->zoom, &lon1, &lat1); + tile2lonlat(r->x + 1, r->y + 1, r->zoom, &lon2, &lat2); + minlat = min(lat2, minlat); + minlon = min(lon1, minlon); + maxlat = max(lat1, maxlat); + maxlon = max(lon2, maxlon); + zxy tile = zxy(r->zoom, r->x, r->y); if (tasks.count(tile) == 0) { tasks.insert(std::pair>(tile, std::vector())); @@ -517,6 +543,11 @@ void decode(struct reader *readers, char *map, std::mapminlon = min(minlon, st->minlon); + st->maxlon = max(maxlon, st->maxlon); + st->minlat = min(minlat, st->minlat); + st->maxlat = max(maxlat, st->maxlat); + handle_tasks(tasks, layermaps, outdb, header, mapping, exclude, ifmatched, keep_layers, remove_layers); layermap = merge_layermaps(layermaps); @@ -558,15 +589,25 @@ void decode(struct reader *readers, char *map, std::mapstmt); } + if (sqlite3_prepare_v2(r->db, "SELECT value from metadata where name = 'name'", -1, &r->stmt, NULL) == SQLITE_OK) { + if (sqlite3_step(r->stmt) == SQLITE_ROW) { + if (name.size() == 0) { + name = std::string((char *) sqlite3_column_text(r->stmt, 0)); + } else { + name += " + " + std::string((char *) sqlite3_column_text(r->stmt, 0)); + } + } + sqlite3_finalize(r->stmt); + } if (sqlite3_prepare_v2(r->db, "SELECT value from metadata where name = 'bounds'", -1, &r->stmt, NULL) == SQLITE_OK) { if (sqlite3_step(r->stmt) == SQLITE_ROW) { const unsigned char *s = sqlite3_column_text(r->stmt, 0); - double minlon, minlat, maxlon, maxlat; - sscanf((char *) s, "%lf,%lf,%lf,%lf", &minlon, &minlat, &maxlon, &maxlat); - st->minlon = min(minlon, st->minlon); - st->maxlon = max(maxlon, st->maxlon); - st->minlat = min(minlat, st->minlat); - st->maxlat = max(maxlat, st->maxlat); + if (sscanf((char *) s, "%lf,%lf,%lf,%lf", &minlon, &minlat, &maxlon, &maxlat) == 4) { + st->minlon = min(minlon, st->minlon); + st->maxlon = max(maxlon, st->maxlon); + st->minlat = min(minlat, st->minlat); + st->maxlat = max(maxlat, st->maxlat); + } } sqlite3_finalize(r->stmt); } @@ -684,11 +725,13 @@ int main(int argc, char **argv) { std::set keep_layers; std::set remove_layers; + std::string set_name, set_description, set_attribution; + extern int optind; extern char *optarg; int i; - while ((i = getopt(argc, argv, "fo:c:x:ip:l:L:")) != -1) { + while ((i = getopt(argc, argv, "fo:c:x:ip:l:L:A:N:n:")) != -1) { switch (i) { case 'o': outfile = optarg; @@ -702,6 +745,18 @@ int main(int argc, char **argv) { ifmatched = 1; break; + case 'A': + set_attribution = optarg; + break; + + case 'n': + set_name = optarg; + break; + + case 'N': + set_description = optarg; + break; + case 'p': if (strcmp(optarg, "k") == 0) { pk = true; @@ -755,6 +810,7 @@ int main(int argc, char **argv) { std::map layermap; std::string attribution; std::string description; + std::string name; struct reader *readers = NULL; @@ -772,9 +828,19 @@ int main(int argc, char **argv) { *rr = r; } - decode(readers, csv, layermap, outdb, &st, header, mapping, exclude, ifmatched, attribution, description, keep_layers, remove_layers); + decode(readers, csv, layermap, outdb, &st, header, mapping, exclude, ifmatched, attribution, description, keep_layers, remove_layers, name); - mbtiles_write_metadata(outdb, NULL, outfile, st.minzoom, st.maxzoom, st.minlat, st.minlon, st.maxlat, st.maxlon, st.midlat, st.midlon, 0, attribution.size() != 0 ? attribution.c_str() : NULL, layermap, true, description.c_str()); + if (set_attribution.size() != 0) { + attribution = set_attribution; + } + if (set_description.size() != 0) { + description = set_description; + } + if (set_name.size() != 0) { + name = set_name; + } + + mbtiles_write_metadata(outdb, NULL, name.c_str(), st.minzoom, st.maxzoom, st.minlat, st.minlon, st.maxlat, st.maxlon, st.midlat, st.midlon, 0, attribution.size() != 0 ? attribution.c_str() : NULL, layermap, true, description.c_str()); mbtiles_close(outdb, argv); return 0; diff --git a/tile.cpp b/tile.cpp index bb86f9d1..e7121b1a 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1374,7 +1374,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, false, 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, false); } if (fclose(rpa->prefilter_fp) != 0) { diff --git a/version.hpp b/version.hpp index 65d04a14..74600d90 100644 --- a/version.hpp +++ b/version.hpp @@ -1 +1 @@ -#define VERSION "tippecanoe v1.17.4\n" +#define VERSION "tippecanoe v1.18.1\n" diff --git a/write_json.cpp b/write_json.cpp index e3660ceb..0c03d2b2 100644 --- a/write_json.cpp +++ b/write_json.cpp @@ -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, bool zoom, 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, bool complain) { for (size_t f = 0; f < layer.features.size(); f++) { mvt_feature const &feat = layer.features[f]; @@ -232,6 +232,21 @@ void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, rings[n].push_back(ops[i]); } } + + if (i + 1 >= ops.size() || ops[i + 1].op == VT_MOVETO) { + if (ops[i].op != VT_CLOSEPATH) { + static bool warned = false; + + if (!warned) { + fprintf(stderr, "Ring does not end with closepath (ends with %d)\n", ops[i].op); + if (complain) { + exit(EXIT_FAILURE); + } + + warned = true; + } + } + } } int outer = 0; @@ -262,6 +277,19 @@ void layer_to_geojson(FILE *fp, mvt_layer const &layer, unsigned z, unsigned x, int state = 0; for (size_t i = 0; i < rings.size(); i++) { + if (i == 0 && areas[i] < 0) { + static bool warned = false; + + if (!warned) { + fprintf(stderr, "Polygon begins with an inner ring\n"); + if (complain) { + exit(EXIT_FAILURE); + } + + warned = true; + } + } + if (areas[i] >= 0) { if (state != 0) { // new multipolygon diff --git a/write_json.hpp b/write_json.hpp index 6b4d6ad9..38a611df 100644 --- a/write_json.hpp +++ b/write_json.hpp @@ -1,2 +1,2 @@ -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 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, bool complain); void fprintq(FILE *f, const char *s);