diff --git a/.gitignore b/.gitignore index 24efe134..c03cdc1e 100644 --- a/.gitignore +++ b/.gitignore @@ -45,6 +45,9 @@ tests/**/*.geobuf tests/mlt/dir-out/ tests/mlt/roundtrip-dir/ tests/mlt/overzoom.pbf +tests/mlt/join-out-dir/ +tests/mlt/mvt-source-dir/ +tests/mlt/overzoom-out.mlt mlt-build/ diff --git a/Makefile b/Makefile index 006f5f14..44cf94bf 100644 --- a/Makefile +++ b/Makefile @@ -87,16 +87,16 @@ tippecanoe-enumerate: enumerate.o tippecanoe-decode: decode.o projection.o mvt.o mlt_decode.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o pmtiles_file.o $(MLT_DECODE_LIBS) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -tile-join: tile-join.o platform.o projection.o mbtiles.o mvt.o mlt_decode.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o pmtiles_file.o clip.o attribute.o thread.o read_json.o clipper2/src/clipper.engine.o $(MLT_DECODE_LIBS) +tile-join: tile-join.o platform.o projection.o mbtiles.o mvt.o mlt.o mlt_decode.o memfile.o dirtiles.o jsonpull/jsonpull.o text.o evaluator.o csv.o write_json.o pmtiles_file.o clip.o attribute.o thread.o read_json.o clipper2/src/clipper.engine.o $(MLT_LIBS) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread tippecanoe-json-tool: jsontool.o jsonpull/jsonpull.o csv.o text.o geojson-loop.o $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread -unit: unit.o text.o sort.o mvt.o mlt_decode.o projection.o clip.o attribute.o jsonpull/jsonpull.o evaluator.o read_json.o clipper2/src/clipper.engine.o $(MLT_DECODE_LIBS) +unit: unit.o text.o sort.o mvt.o mlt.o mlt_decode.o projection.o clip.o attribute.o jsonpull/jsonpull.o evaluator.o read_json.o clipper2/src/clipper.engine.o $(MLT_LIBS) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread -tippecanoe-overzoom: overzoom.o mvt.o mlt_decode.o clip.o evaluator.o jsonpull/jsonpull.o text.o attribute.o read_json.o projection.o read_json.o clipper2/src/clipper.engine.o $(MLT_DECODE_LIBS) +tippecanoe-overzoom: overzoom.o mvt.o mlt.o mlt_decode.o clip.o evaluator.o jsonpull/jsonpull.o text.o attribute.o read_json.o projection.o read_json.o clipper2/src/clipper.engine.o $(MLT_LIBS) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread -include $(wildcard *.d) @@ -119,7 +119,7 @@ indent: TESTS = $(wildcard tests/*/out/*.json) SPACE = $(NULL) $(NULL) -test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test parallel-test pbf-test join-test enumerate-test decode-test join-filter-test unit json-tool-test allow-existing-test csv-test layer-json-test pmtiles-test decode-pmtiles-test overzoom-test flatgeobuf-test mlt-test mlt-decode-test +test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) raw-tiles-test parallel-test pbf-test join-test enumerate-test decode-test join-filter-test unit json-tool-test allow-existing-test csv-test layer-json-test pmtiles-test decode-pmtiles-test overzoom-test flatgeobuf-test mlt-test mlt-decode-test mlt-output-test ./unit suffixes = json json.gz @@ -650,6 +650,29 @@ mlt-decode-test: tippecanoe tippecanoe-decode tile-join tippecanoe-overzoom rm -f tests/mlt/roundtrip-dir.json.check tests/mlt/overzoom.pbf tests/mlt/overzoom.json.check rm -rf tests/mlt/roundtrip-dir +mlt-output-test: tippecanoe tippecanoe-decode tile-join tippecanoe-overzoom + ./tippecanoe -q -z5 -f -o tests/mlt/mvt-source.mbtiles tests/mlt/roundtrip.geojson + # tile-join writes MLT tiles to an mbtiles file, whatever the sources are encoded in + ./tile-join -f --output-format=mlt -o tests/mlt/join-out.mbtiles tests/mlt/mvt-source.mbtiles + ./tippecanoe-decode -x generator -x generator_options tests/mlt/join-out.mbtiles > tests/mlt/join-out.mbtiles.json.check + cmp tests/mlt/join-out.mbtiles.json.check tests/mlt/join-out.mbtiles.json + # tile-join writes MLT tiles to a tile directory + rm -rf tests/mlt/join-out-dir + ./tile-join -f --output-format=mlt -e tests/mlt/join-out-dir tests/mlt/mvt-source.mbtiles + @test $$(find tests/mlt/join-out-dir -name '*.mlt' | wc -l) -gt 0 || (echo "FAIL: No .mlt files in tile-join directory output" && exit 1) + @test $$(find tests/mlt/join-out-dir -name '*.pbf' | wc -l) -eq 0 || (echo "FAIL: .pbf files in tile-join MLT directory output" && exit 1) + # tippecanoe-overzoom writes MLT tiles + rm -rf tests/mlt/mvt-source-dir + ./tippecanoe -q -z5 -f -e tests/mlt/mvt-source-dir tests/mlt/roundtrip.geojson + # The same features come out whichever direction the formats are converted in, + # so this is compared against the standard for overzooming an MLT tile to MVT + ./tippecanoe-overzoom --output-format=mlt -o tests/mlt/overzoom-out.mlt tests/mlt/mvt-source-dir/2/0/1.pbf 2/0/1 4/2/6 + ./tippecanoe-decode tests/mlt/overzoom-out.mlt 4 2 6 > tests/mlt/overzoom-out.json.check + cmp tests/mlt/overzoom-out.json.check tests/mlt/overzoom.json + rm -f tests/mlt/mvt-source.mbtiles tests/mlt/join-out.mbtiles tests/mlt/join-out.mbtiles.json.check + rm -f tests/mlt/overzoom-out.mlt tests/mlt/overzoom-out.json.check + rm -rf tests/mlt/join-out-dir tests/mlt/mvt-source-dir + # Use this target to regenerate the standards that the tests are compared against # after making a change that legitimately changes their output diff --git a/README.md b/README.md index 4f0cde9a..6bb6f8a4 100644 --- a/README.md +++ b/README.md @@ -777,8 +777,8 @@ mbtiles output. If they define the same layers or the same tiles, the layers or tiles are merged. Sources may contain either Mapbox Vector Tiles or MapLibre Tiles, in any combination, -since the encoding is detected from the tile data. The output is always written as -Mapbox Vector Tiles. +since the encoding is detected from the tile data. The output is written as Mapbox +Vector Tiles unless `--output-format=mlt` is specified. The options are: @@ -829,6 +829,9 @@ The options are: * `-pk` or `--no-tile-size-limit`: Don't skip tiles larger than 500K. * `-pC` or `--no-tile-compression`: Don't compress the vector tile data. + * `--output-format=`*format*: Set the tile encoding format. Supported values: `mvt` (default, Mapbox Vector Tiles) or `mlt` ([MapLibre Tiles](https://github.com/maplibre/maplibre-tile-spec)). Applies regardless of what the sources are encoded in. + * `--pretessellate`: When using `--output-format=mlt`, pre-triangulate polygon geometries. Only applies to layers where all features are polygons. + * `--no-mlt-feature-sort`: When using `--output-format=mlt`, disable within-tile spatial sorting of features by Hilbert curve index. Sorting is on by default. * `-pg` or `--no-tile-stats`: Don't generate the `tilestats` row in the tileset metadata. Uploads without [tilestats](https://github.com/mapbox/mapbox-geostats) will take longer to process. * `--tile-stats-attributes-limit=`*count*: Include `tilestats` information about at most *count* attributes instead of the default 1000. * `--tile-stats-sample-values-limit=`*count*: Calculate `tilestats` attribute statistics based on *count* values instead of the default 1000. @@ -1015,7 +1018,8 @@ reads tile `inz/inx/iny` of `in.mvt.gz`, tile `in2z/in2x/in2y` of `in2.mvt.gz`, and produces tile `outz/outx/outy` of `out.mvt.gz` from them. The input tiles may be either Mapbox Vector Tiles or MapLibre Tiles, since the encoding -is detected from the tile data. The output tile is always a Mapbox Vector Tile. +is detected from the tile data. The output tile is a Mapbox Vector Tile unless +`--output-format=mlt` is specified. ### Options @@ -1026,3 +1030,6 @@ is detected from the tile data. The output tile is always a Mapbox Vector Tile. * `-m`: If a tile was created with the `--retain-points-multiplier` option, thin the tile back down to its normal feature count during overzooming. The first feature from each cluster will be retained, unless `-j` is used to specify a filter, in which case the first matching filter from each cluster will be retained instead. * `--preserve-input-order`: Restore a set of filtered features to its original input order * `--accumulate-attribute`: Behaves as in `tippecanoe` to sum attributes from the features of a multiplier cluster that are not included in the final output. The attributes from features that are filtered away with `-j` are *not* accumulated onto the output feature. + * `--output-format=`*format*: Set the tile encoding format. Supported values: `mvt` (default, Mapbox Vector Tiles) or `mlt` ([MapLibre Tiles](https://github.com/maplibre/maplibre-tile-spec)). Applies regardless of what the input tiles are encoded in. + * `--pretessellate`: When using `--output-format=mlt`, pre-triangulate polygon geometries. Only applies to layers where all features are polygons. + * `--no-mlt-feature-sort`: When using `--output-format=mlt`, disable within-tile spatial sorting of features by Hilbert curve index. Sorting is on by default. diff --git a/clip.cpp b/clip.cpp index 72a1fb3a..7230da4a 100644 --- a/clip.cpp +++ b/clip.cpp @@ -10,6 +10,7 @@ #include "errors.hpp" #include "compression.hpp" #include "mvt.hpp" +#include "mlt.hpp" #include "evaluator.hpp" #include "serial.hpp" #include "attribute.hpp" @@ -1228,7 +1229,8 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int n std::vector const &unused2, std::string const &unused3, std::string const &unused, size_t feature_limit, std::vector const &unused4, - bool deduplicate_by_id) { + bool deduplicate_by_id, + int tile_format) { std::vector decoded; for (auto const &t : tiles) { @@ -1254,7 +1256,7 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int n decoded.push_back(out); } - return overzoom(decoded, nz, nx, ny, detail_or_unspecified, buffer, keep, exclude, exclude_prefix, do_compress, next_overzoomed_tiles, demultiply, filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, unused2, unused3, unused, feature_limit, unused4, deduplicate_by_id); + return overzoom(decoded, nz, nx, ny, detail_or_unspecified, buffer, keep, exclude, exclude_prefix, do_compress, next_overzoomed_tiles, demultiply, filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, unused2, unused3, unused, feature_limit, unused4, deduplicate_by_id, tile_format); } // like a minimal serial_feature, but with mvt_feature-style attributes @@ -1464,7 +1466,8 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int std::vector const &unused2, std::string const &unused3, std::string const &unused, size_t feature_limit, std::vector const &unused4, - bool deduplicate_by_id) { + bool deduplicate_by_id, + int tile_format) { mvt_tile outtile; key_pool key_pool; @@ -1737,7 +1740,7 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int } if (outtile.layers.size() > 0) { - std::string pbf = outtile.encode(); + std::string pbf = encode_tile(outtile, tile_format); std::string compressed; if (do_compress) { diff --git a/geometry.hpp b/geometry.hpp index 454cea61..5079a31b 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -148,7 +148,8 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int std::vector const &bins, std::string const &bin_by_id_list, std::string const &accumulate_numeric, size_t feature_limit, std::vector const &clipbboxes, - bool deduplicate_by_id); + bool deduplicate_by_id, + int tile_format = OUTPUT_MVT); std::string overzoom(std::vector const &tiles, int nz, int nx, int ny, int detail, int buffer, @@ -164,7 +165,8 @@ std::string overzoom(std::vector const &tiles, int nz, int nx, int n std::vector const &bins, std::string const &bin_by_id_list, std::string const &accumulate_numeric, size_t feature_limit, std::vector const &clipbboxes, - bool deduplicate_by_id); + bool deduplicate_by_id, + int tile_format = OUTPUT_MVT); draw center_of_mass_mp(const drawvec &dv); diff --git a/main.cpp b/main.cpp index 675045dd..46062427 100644 --- a/main.cpp +++ b/main.cpp @@ -50,6 +50,7 @@ #include "projection.hpp" #include "memfile.hpp" #include "main.hpp" +#include "mlt.hpp" #include "geojson.hpp" #include "geobuf.hpp" #include "flatgeobuf.hpp" @@ -104,9 +105,6 @@ bool drop_by_attribute_descending = false; std::vector order_by; bool order_reverse; bool order_by_size = false; -int output_format = OUTPUT_MVT; -bool mlt_sort_features = true; -bool mlt_pretessellate = false; int prevent[256]; int additional[256]; @@ -2827,7 +2825,7 @@ std::pair read_input(std::vector &sources, char *fname, i ai->second.maxzoom = maxzoom; } - const char *tile_format = (output_format == OUTPUT_MLT) ? "mlt" : "pbf"; + const char *tile_format = tile_format_name(output_format); metadata m = make_metadata(fname, minzoom, maxzoom, minlat, minlon, maxlat, maxlon, minlat2, minlon2, maxlat2, maxlon2, midlat, midlon, attribution, merged_lm, tile_format, description, !prevent[P_TILE_STATS], attribute_descriptions, "tippecanoe", commandline, strategies, basezoom, droprate, retain_points_multiplier); if (outdb != NULL) { mbtiles_write_metadata(outdb, m, forcetable); @@ -3335,14 +3333,7 @@ int main(int argc, char **argv) { } else if (strcmp(opt, "maximum-string-attribute-length") == 0) { maximum_string_attribute_length = atoll_require(optarg, "Maximum string attribute length"); } else if (strcmp(opt, "output-format") == 0) { - if (strcmp(optarg, "mvt") == 0 || strcmp(optarg, "pbf") == 0) { - output_format = OUTPUT_MVT; - } else if (strcmp(optarg, "mlt") == 0) { - output_format = OUTPUT_MLT; - } else { - fprintf(stderr, "%s: --output-format must be 'mvt' or 'mlt'\n", argv[0]); - exit(EXIT_ARGS); - } + set_output_format(argv, optarg); } else if (strcmp(opt, "pretessellate") == 0) { mlt_pretessellate = true; } else if (strcmp(opt, "no-mlt-feature-sort") == 0) { diff --git a/main.hpp b/main.hpp index 474c1eb9..368016e9 100644 --- a/main.hpp +++ b/main.hpp @@ -76,11 +76,4 @@ bool progress_time(); #define MAX_ZOOM 24 -#define OUTPUT_MVT 0 -#define OUTPUT_MLT 1 - -extern int output_format; -extern bool mlt_sort_features; -extern bool mlt_pretessellate; - #endif diff --git a/mlt.cpp b/mlt.cpp index 4e74e606..2ae35b60 100644 --- a/mlt.cpp +++ b/mlt.cpp @@ -1,9 +1,12 @@ #include "mlt.hpp" +#include "errors.hpp" #include #include #include +#include +#include #include #include #include @@ -11,6 +14,37 @@ using Vertex = mlt::Encoder::Vertex; +int output_format = OUTPUT_MVT; +bool mlt_sort_features = true; +bool mlt_pretessellate = false; + +void set_output_format(char **argv, const char *format) { + if (strcmp(format, "mvt") == 0 || strcmp(format, "pbf") == 0) { + output_format = OUTPUT_MVT; + } else if (strcmp(format, "mlt") == 0) { + output_format = OUTPUT_MLT; + } else { + fprintf(stderr, "%s: --output-format must be 'mvt' or 'mlt'\n", argv[0]); + exit(EXIT_ARGS); + } +} + +const char *tile_format_name(int format) { + return (format == OUTPUT_MLT) ? "mlt" : "pbf"; +} + +const char *tile_format_extension(int format) { + return (format == OUTPUT_MLT) ? ".mlt" : ".pbf"; +} + +std::string encode_tile(mvt_tile &tile, int format) { + if (format == OUTPUT_MLT) { + return encode_as_mlt(tile, mlt_sort_features, mlt_pretessellate); + } else { + return tile.encode(); + } +} + // An MLT property column has a single type for the whole layer, while MVT // values each carry their own type, so a type that can hold every value of // an attribute has to be chosen before the layer can be converted. diff --git a/mlt.hpp b/mlt.hpp index 3bb08bc3..8369afbf 100644 --- a/mlt.hpp +++ b/mlt.hpp @@ -4,6 +4,26 @@ #include #include "mvt.hpp" +// The tile format to write, and the MLT encoder options to write it with, +// as selected on the command line. +extern int output_format; +extern bool mlt_sort_features; +extern bool mlt_pretessellate; + +// Set output_format from an --output-format argument, complaining and +// exiting if it names a format we can't write. +void set_output_format(char **argv, const char *format); + +// What this tile format is called in tileset metadata +const char *tile_format_name(int format); + +// What tiles of this format are named in a tile directory +const char *tile_format_extension(int format); + +// Encode a tile in the specified format, using whichever MLT encoder +// options were selected on the command line +std::string encode_tile(mvt_tile &tile, int format); + std::string encode_as_mlt(const mvt_tile &tile, bool sort_features, bool pretessellate); // Does this (already decompressed) tile look like MapLibre Tile rather than diff --git a/mvt.hpp b/mvt.hpp index 8f24fc68..2ddba5e6 100644 --- a/mvt.hpp +++ b/mvt.hpp @@ -17,6 +17,10 @@ struct mvt_value; struct mvt_layer; +// Which of the two vector tile encodings a tile is written in +#define OUTPUT_MVT 0 +#define OUTPUT_MLT 1 + enum mvt_operation { mvt_moveto = 1, mvt_lineto = 2, diff --git a/overzoom.cpp b/overzoom.cpp index cdef1dd8..d90af39d 100644 --- a/overzoom.cpp +++ b/overzoom.cpp @@ -5,6 +5,7 @@ #include #include "errors.hpp" #include "mvt.hpp" +#include "mlt.hpp" #include "geometry.hpp" #include "evaluator.hpp" #include "attribute.hpp" @@ -85,6 +86,9 @@ int main(int argc, char **argv) { {"source-tile", required_argument, 0, 't'}, {"no-tile-compression", no_argument, 0, 'd' & 0x1F}, {"deduplicate-by-id", no_argument, 0, 'i' & 0x1F}, + {"output-format", required_argument, 0, 'f' & 0x1F}, + {"pretessellate", no_argument, 0, 'p' & 0x1F}, + {"no-mlt-feature-sort", no_argument, 0, 'r' & 0x1F}, {0, 0, 0, 0}, }; @@ -172,6 +176,18 @@ int main(int argc, char **argv) { break; } + case 'f' & 0x1F: + set_output_format(argv, optarg); + break; + + case 'p' & 0x1F: + mlt_pretessellate = true; + break; + + case 'r' & 0x1F: + mlt_sort_features = false; + break; + default: fprintf(stderr, "Unrecognized flag -%c\n", i); usage(argv); @@ -264,7 +280,7 @@ int main(int argc, char **argv) { its.push_back(std::move(t)); } - out = overzoom(its, nz, nx, ny, detail, buffer, keep, exclude, exclude_prefix, do_compress, NULL, demultiply, json_filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, std::vector(), "", "", SIZE_MAX, std::vector(), deduplicate_by_id); + out = overzoom(its, nz, nx, ny, detail, buffer, keep, exclude, exclude_prefix, do_compress, NULL, demultiply, json_filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, std::vector(), "", "", SIZE_MAX, std::vector(), deduplicate_by_id, output_format); } FILE *f = fopen(outfile, "wb"); diff --git a/tests/mlt/join-out.mbtiles.json b/tests/mlt/join-out.mbtiles.json new file mode 100644 index 00000000..1e7ca41d --- /dev/null +++ b/tests/mlt/join-out.mbtiles.json @@ -0,0 +1,104 @@ +{ "type": "FeatureCollection", "properties": { +"antimeridian_adjusted_bounds": "-122.915039,37.160317,-122.343750,37.961523", +"bounds": "-122.915039,37.160317,-122.343750,37.961523", +"center": "-122.350000,37.200000,5", +"description": "tests/mlt/mvt-source.mbtiles", +"format": "mlt", +"json": "{\"vector_layers\":[{\"id\":\"roundtrip\",\"description\":\"\",\"minzoom\":0,\"maxzoom\":5,\"fields\":{\"active\":\"Boolean\",\"count\":\"Number\",\"kind\":\"String\",\"name\":\"String\",\"nested\":\"String\",\"ratio\":\"Number\",\"unicode\":\"String\"}}],\"tilestats\":{\"layerCount\":1,\"layers\":[{\"layer\":\"roundtrip\",\"count\":33,\"geometry\":\"LineString\",\"attributeCount\":7,\"attributes\":[{\"attribute\":\"active\",\"count\":2,\"type\":\"boolean\",\"values\":[false,true]},{\"attribute\":\"count\",\"count\":6,\"type\":\"number\",\"values\":[-200,100,300,4000000000,500,600],\"min\":-200,\"max\":4000000000},{\"attribute\":\"kind\",\"count\":3,\"type\":\"string\",\"values\":[\"line\",\"point\",\"polygon\"]},{\"attribute\":\"name\",\"count\":7,\"type\":\"string\",\"values\":[\"line\",\"multiline\",\"multipoint\",\"multipolygon\",\"no id\",\"point\",\"polygon with hole\"]},{\"attribute\":\"nested\",\"count\":1,\"type\":\"string\",\"values\":[\"{\\\"a\\\":1,\\\"b\\\":\\\"two\\\"}\"]},{\"attribute\":\"ratio\",\"count\":5,\"type\":\"number\",\"values\":[-0.25,0.125,1.5,2,3.25],\"min\":-0.25,\"max\":3.25},{\"attribute\":\"unicode\",\"count\":1,\"type\":\"string\",\"values\":[\"påíñtß 堤防\"]}]}]}}", +"maxzoom": "5", +"minzoom": "0", +"name": "tests/mlt/mvt-source.mbtiles", +"strategies": "[{\"dropped_by_rate\":2,\"tiny_polygons\":1},{\"dropped_by_rate\":2},{\"dropped_by_rate\":2},{\"dropped_by_rate\":2},{\"dropped_by_rate\":1}]", +"type": "overlay", +"version": "2" +}, "features": [ +{ "type": "FeatureCollection", "properties": { "zoom": 0, "x": 0, "y": 0 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "roundtrip", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "id": 2, "properties": { "active": false, "count": -200, "kind": "point", "name": "multipoint", "ratio": -0.25 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -122.519531, 37.926868 ], [ -122.431641, 37.857507 ], [ -122.519531, 37.926868 ] ] } } +, +{ "type": "Feature", "id": 4, "properties": { "count": 4000000000, "kind": "line", "name": "multiline", "ratio": 0.125 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.695312, 37.579413 ], [ -122.607422, 37.649034 ] ], [ [ -122.519531, 37.579413 ], [ -122.431641, 37.649034 ] ] ] } } +, +{ "type": "Feature", "id": 3, "properties": { "count": 300, "kind": "line", "name": "line", "ratio": 2 }, "geometry": { "type": "LineString", "coordinates": [ [ -122.607422, 37.718590 ], [ -122.431641, 37.718590 ] ] } } +, +{ "type": "Feature", "id": 5, "properties": { "active": true, "count": 500, "kind": "polygon", "name": "polygon with hole", "ratio": 3.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.431641, 37.579413 ], [ -122.431641, 37.370157 ], [ -122.783203, 37.370157 ], [ -122.783203, 37.579413 ], [ -122.431641, 37.579413 ] ], [ [ -122.519531, 37.439974 ], [ -122.519531, 37.509726 ], [ -122.695312, 37.509726 ], [ -122.519531, 37.439974 ] ] ] } } +, +{ "type": "Feature", "id": 6, "properties": { "count": 600, "kind": "polygon", "name": "multipolygon" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.343750, 37.300275 ], [ -122.343750, 37.160317 ], [ -122.519531, 37.160317 ], [ -122.519531, 37.300275 ], [ -122.343750, 37.300275 ] ] ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 1, "x": 0, "y": 0 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "roundtrip", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "id": 2, "properties": { "active": false, "count": -200, "kind": "point", "name": "multipoint", "ratio": -0.25 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -122.519531, 37.892196 ], [ -122.431641, 37.857507 ], [ -122.563477, 37.961523 ] ] } } +, +{ "type": "Feature", "id": 4, "properties": { "count": 4000000000, "kind": "line", "name": "multiline", "ratio": 0.125 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.695312, 37.614231 ], [ -122.607422, 37.649034 ] ], [ [ -122.519531, 37.614231 ], [ -122.387695, 37.649034 ] ] ] } } +, +{ "type": "Feature", "id": 3, "properties": { "count": 300, "kind": "line", "name": "line", "ratio": 2 }, "geometry": { "type": "LineString", "coordinates": [ [ -122.607422, 37.683820 ], [ -122.519531, 37.753344 ], [ -122.387695, 37.683820 ] ] } } +, +{ "type": "Feature", "id": 5, "properties": { "active": true, "count": 500, "kind": "polygon", "name": "polygon with hole", "ratio": 3.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.387695, 37.544577 ], [ -122.387695, 37.405074 ], [ -122.783203, 37.405074 ], [ -122.783203, 37.544577 ], [ -122.387695, 37.544577 ] ], [ [ -122.695312, 37.439974 ], [ -122.519531, 37.439974 ], [ -122.519531, 37.509726 ], [ -122.695312, 37.509726 ], [ -122.695312, 37.439974 ] ] ] } } +, +{ "type": "Feature", "id": 6, "properties": { "count": 600, "kind": "polygon", "name": "multipolygon" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.695312, 37.300275 ], [ -122.695312, 37.195331 ], [ -122.915039, 37.195331 ], [ -122.915039, 37.300275 ], [ -122.695312, 37.300275 ] ] ], [ [ [ -122.387695, 37.300275 ], [ -122.387695, 37.195331 ], [ -122.607422, 37.195331 ], [ -122.607422, 37.300275 ], [ -122.387695, 37.300275 ] ], [ [ -122.563477, 37.230328 ], [ -122.431641, 37.230328 ], [ -122.431641, 37.265310 ], [ -122.563477, 37.265310 ], [ -122.563477, 37.230328 ] ] ] ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 2, "x": 0, "y": 1 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "roundtrip", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "id": 2, "properties": { "active": false, "count": -200, "kind": "point", "name": "multipoint", "ratio": -0.25 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -122.497559, 37.892196 ], [ -122.453613, 37.857507 ], [ -122.541504, 37.944198 ] ] } } +, +{ "type": "Feature", "id": 4, "properties": { "count": 4000000000, "kind": "line", "name": "multiline", "ratio": 0.125 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.695312, 37.596824 ], [ -122.607422, 37.649034 ] ], [ [ -122.497559, 37.596824 ], [ -122.387695, 37.649034 ] ] ] } } +, +{ "type": "Feature", "id": 3, "properties": { "count": 300, "kind": "line", "name": "line", "ratio": 2 }, "geometry": { "type": "LineString", "coordinates": [ [ -122.607422, 37.701207 ], [ -122.497559, 37.753344 ], [ -122.387695, 37.701207 ] ] } } +, +{ "type": "Feature", "id": 5, "properties": { "active": true, "count": 500, "kind": "polygon", "name": "polygon with hole", "ratio": 3.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.387695, 37.544577 ], [ -122.387695, 37.405074 ], [ -122.805176, 37.405074 ], [ -122.805176, 37.544577 ], [ -122.387695, 37.544577 ] ], [ [ -122.695312, 37.457418 ], [ -122.497559, 37.457418 ], [ -122.497559, 37.492294 ], [ -122.695312, 37.492294 ], [ -122.695312, 37.457418 ] ] ] } } +, +{ "type": "Feature", "id": 6, "properties": { "count": 600, "kind": "polygon", "name": "multipolygon" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.695312, 37.300275 ], [ -122.695312, 37.195331 ], [ -122.893066, 37.195331 ], [ -122.893066, 37.300275 ], [ -122.695312, 37.300275 ] ] ], [ [ [ -122.387695, 37.300275 ], [ -122.387695, 37.195331 ], [ -122.607422, 37.195331 ], [ -122.607422, 37.300275 ], [ -122.387695, 37.300275 ] ], [ [ -122.541504, 37.212832 ], [ -122.453613, 37.212832 ], [ -122.453613, 37.282795 ], [ -122.541504, 37.282795 ], [ -122.541504, 37.212832 ] ] ] ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 3, "x": 1, "y": 3 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "roundtrip", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "id": 2, "properties": { "active": false, "count": -200, "kind": "point", "name": "multipoint", "ratio": -0.25 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -122.497559, 37.900865 ], [ -122.453613, 37.848833 ], [ -122.552490, 37.952861 ] ] } } +, +{ "type": "Feature", "id": 4, "properties": { "count": 4000000000, "kind": "line", "name": "multiline", "ratio": 0.125 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.695312, 37.596824 ], [ -122.596436, 37.649034 ] ], [ [ -122.497559, 37.596824 ], [ -122.398682, 37.649034 ] ] ] } } +, +{ "type": "Feature", "id": 3, "properties": { "count": 300, "kind": "line", "name": "line", "ratio": 2 }, "geometry": { "type": "LineString", "coordinates": [ [ -122.596436, 37.701207 ], [ -122.497559, 37.744657 ], [ -122.398682, 37.701207 ] ] } } +, +{ "type": "Feature", "id": 5, "properties": { "active": true, "count": 500, "kind": "polygon", "name": "polygon with hole", "ratio": 3.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.398682, 37.544577 ], [ -122.398682, 37.396346 ], [ -122.794189, 37.396346 ], [ -122.794189, 37.544577 ], [ -122.398682, 37.544577 ] ], [ [ -122.695312, 37.448697 ], [ -122.497559, 37.448697 ], [ -122.497559, 37.501010 ], [ -122.695312, 37.501010 ], [ -122.695312, 37.448697 ] ] ] } } +, +{ "type": "Feature", "id": 6, "properties": { "count": 600, "kind": "polygon", "name": "multipolygon" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.695312, 37.300275 ], [ -122.695312, 37.195331 ], [ -122.904053, 37.195331 ], [ -122.904053, 37.300275 ], [ -122.695312, 37.300275 ] ] ], [ [ [ -122.398682, 37.300275 ], [ -122.398682, 37.195331 ], [ -122.596436, 37.195331 ], [ -122.596436, 37.300275 ], [ -122.398682, 37.300275 ] ], [ [ -122.552490, 37.221580 ], [ -122.453613, 37.221580 ], [ -122.453613, 37.282795 ], [ -122.552490, 37.282795 ], [ -122.552490, 37.221580 ] ] ] ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 4, "x": 2, "y": 6 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "roundtrip", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "id": 2, "properties": { "active": false, "count": -200, "kind": "point", "name": "multipoint", "ratio": -0.25 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -122.497559, 37.900865 ], [ -122.448120, 37.848833 ], [ -122.546997, 37.948529 ] ] } } +, +{ "type": "Feature", "properties": { "kind": "point", "name": "no id", "unicode": "påíñtß 堤防" }, "geometry": { "type": "Point", "coordinates": [ -122.349243, 37.749001 ] } } +, +{ "type": "Feature", "id": 4, "properties": { "count": 4000000000, "kind": "line", "name": "multiline", "ratio": 0.125 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.700806, 37.596824 ], [ -122.596436, 37.649034 ] ], [ [ -122.497559, 37.596824 ], [ -122.398682, 37.649034 ] ] ] } } +, +{ "type": "Feature", "id": 3, "properties": { "count": 300, "kind": "line", "name": "line", "ratio": 2 }, "geometry": { "type": "LineString", "coordinates": [ [ -122.596436, 37.696861 ], [ -122.497559, 37.749001 ], [ -122.398682, 37.696861 ] ] } } +, +{ "type": "Feature", "id": 5, "properties": { "active": true, "count": 500, "kind": "polygon", "name": "polygon with hole", "ratio": 3.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.398682, 37.548933 ], [ -122.398682, 37.400710 ], [ -122.799683, 37.400710 ], [ -122.799683, 37.548933 ], [ -122.398682, 37.548933 ] ], [ [ -122.700806, 37.448697 ], [ -122.497559, 37.448697 ], [ -122.497559, 37.501010 ], [ -122.700806, 37.501010 ], [ -122.700806, 37.448697 ] ] ] } } +, +{ "type": "Feature", "id": 6, "properties": { "count": 600, "kind": "polygon", "name": "multipolygon" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.700806, 37.300275 ], [ -122.700806, 37.199706 ], [ -122.898560, 37.199706 ], [ -122.898560, 37.300275 ], [ -122.700806, 37.300275 ] ] ], [ [ [ -122.398682, 37.300275 ], [ -122.398682, 37.199706 ], [ -122.596436, 37.199706 ], [ -122.596436, 37.300275 ], [ -122.398682, 37.300275 ] ], [ [ -122.546997, 37.217206 ], [ -122.448120, 37.217206 ], [ -122.448120, 37.278424 ], [ -122.546997, 37.278424 ], [ -122.546997, 37.217206 ] ] ] ] } } +] } +] } +, +{ "type": "FeatureCollection", "properties": { "zoom": 5, "x": 5, "y": 12 }, "features": [ +{ "type": "FeatureCollection", "properties": { "layer": "roundtrip", "version": 2, "extent": 4096 }, "features": [ +{ "type": "Feature", "id": 2, "properties": { "active": false, "count": -200, "kind": "point", "name": "multipoint", "ratio": -0.25 }, "geometry": { "type": "MultiPoint", "coordinates": [ [ -122.500305, 37.900865 ], [ -122.450867, 37.851001 ], [ -122.549744, 37.950695 ] ] } } +, +{ "type": "Feature", "id": 1, "properties": { "active": true, "count": 100, "kind": "point", "name": "point", "nested": "{\"a\":1,\"b\":\"two\"}", "ratio": 1.5 }, "geometry": { "type": "Point", "coordinates": [ -122.398682, 37.798933 ] } } +, +{ "type": "Feature", "properties": { "kind": "point", "name": "no id", "unicode": "påíñtß 堤防" }, "geometry": { "type": "Point", "coordinates": [ -122.349243, 37.749001 ] } } +, +{ "type": "Feature", "id": 4, "properties": { "count": 4000000000, "kind": "line", "name": "multiline", "ratio": 0.125 }, "geometry": { "type": "MultiLineString", "coordinates": [ [ [ -122.700806, 37.599000 ], [ -122.599182, 37.649034 ] ], [ [ -122.500305, 37.599000 ], [ -122.398682, 37.649034 ] ] ] } } +, +{ "type": "Feature", "id": 3, "properties": { "count": 300, "kind": "line", "name": "line", "ratio": 2 }, "geometry": { "type": "LineString", "coordinates": [ [ -122.599182, 37.699034 ], [ -122.500305, 37.749001 ], [ -122.398682, 37.699034 ] ] } } +, +{ "type": "Feature", "id": 5, "properties": { "active": true, "count": 500, "kind": "polygon", "name": "polygon with hole", "ratio": 3.25 }, "geometry": { "type": "Polygon", "coordinates": [ [ [ -122.398682, 37.548933 ], [ -122.398682, 37.400710 ], [ -122.799683, 37.400710 ], [ -122.799683, 37.548933 ], [ -122.398682, 37.548933 ] ], [ [ -122.700806, 37.450877 ], [ -122.500305, 37.450877 ], [ -122.500305, 37.501010 ], [ -122.700806, 37.501010 ], [ -122.700806, 37.450877 ] ] ] } } +, +{ "type": "Feature", "id": 6, "properties": { "count": 600, "kind": "polygon", "name": "multipolygon" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -122.700806, 37.300275 ], [ -122.700806, 37.199706 ], [ -122.901306, 37.199706 ], [ -122.901306, 37.300275 ], [ -122.700806, 37.300275 ] ] ], [ [ [ -122.398682, 37.300275 ], [ -122.398682, 37.199706 ], [ -122.599182, 37.199706 ], [ -122.599182, 37.300275 ], [ -122.398682, 37.300275 ] ], [ [ -122.549744, 37.219393 ], [ -122.450867, 37.219393 ], [ -122.450867, 37.280609 ], [ -122.549744, 37.280609 ], [ -122.549744, 37.219393 ] ] ] ] } } +] } +] } +] } diff --git a/tile-join.cpp b/tile-join.cpp index 347f7a10..ef25cc7c 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -24,6 +24,7 @@ #include #include #include "mvt.hpp" +#include "mlt.hpp" #include "projection.hpp" #include "mbtiles.hpp" #include "geometry.hpp" @@ -871,7 +872,7 @@ void *join_worker(void *v) { } if (anything) { - std::string pbf = outtile.encode(); + std::string pbf = encode_tile(outtile, output_format); std::string compressed; if (!pC) { @@ -958,7 +959,7 @@ void dispatch_tasks(std::map> &tasks, std::vector< if (outdb != NULL) { mbtiles_write_tile(outdb, ai->first.z, ai->first.x, ai->first.y, ai->second.data(), ai->second.size()); } else if (outdir != NULL) { - dir_write_tile(outdir, ai->first.z, ai->first.x, ai->first.y, ai->second); + dir_write_tile(outdir, ai->first.z, ai->first.x, ai->first.y, ai->second, tile_format_extension(output_format)); } } } @@ -1335,6 +1336,9 @@ int main(int argc, char **argv) { {"tile-stats-sample-values-limit", required_argument, 0, '~'}, {"tile-stats-values-limit", required_argument, 0, '~'}, {"unidecode-data", required_argument, 0, '~'}, + {"output-format", required_argument, 0, '~'}, + {"pretessellate", no_argument, 0, '~'}, + {"no-mlt-feature-sort", no_argument, 0, '~'}, {0, 0, 0, 0}, }; @@ -1515,6 +1519,12 @@ int main(int argc, char **argv) { exclude_all_tile_attributes = true; } else if (strcmp(opt, "exclude-all-tile-geometries") == 0) { exclude_all_tile_geometries = true; + } else if (strcmp(opt, "output-format") == 0) { + set_output_format(argv, optarg); + } else if (strcmp(opt, "pretessellate") == 0) { + mlt_pretessellate = true; + } else if (strcmp(opt, "no-mlt-feature-sort") == 0) { + mlt_sort_features = false; } else { fprintf(stderr, "%s: Unrecognized option --%s\n", argv[0], opt); exit(EXIT_ARGS); @@ -1636,7 +1646,7 @@ int main(int argc, char **argv) { st.maxlon2 = st.maxlon; } - metadata m = make_metadata(name.c_str(), st.minzoom, st.maxzoom, st.minlat, st.minlon, st.maxlat, st.maxlon, st.minlat2, st.minlon2, st.maxlat2, st.maxlon2, st.midlat, st.midlon, attribution.size() != 0 ? attribution.c_str() : NULL, layermap, "pbf", description.c_str(), !pg, attribute_descriptions, "tile-join", generator_options, strategies, st.maxzoom, 2.5, 1); + metadata m = make_metadata(name.c_str(), st.minzoom, st.maxzoom, st.minlat, st.minlon, st.maxlat, st.maxlon, st.minlat2, st.minlon2, st.maxlat2, st.maxlon2, st.midlat, st.midlon, attribution.size() != 0 ? attribution.c_str() : NULL, layermap, tile_format_name(output_format), description.c_str(), !pg, attribute_descriptions, "tile-join", generator_options, strategies, st.maxzoom, 2.5, 1); if (outdb != NULL) { mbtiles_write_metadata(outdb, m, true); diff --git a/tile.cpp b/tile.cpp index cce0ef47..f68688a7 100644 --- a/tile.cpp +++ b/tile.cpp @@ -2854,12 +2854,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch } std::string compressed; - std::string pbf; - if (output_format == OUTPUT_MLT) { - pbf = encode_as_mlt(tile, mlt_sort_features, mlt_pretessellate); - } else { - pbf = tile.encode(); - } + std::string pbf = encode_tile(tile, output_format); tile.layers.clear(); @@ -3034,8 +3029,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch if (outdb != NULL) { mbtiles_write_tile(outdb, z, tx, ty, compressed.data(), compressed.size()); } else if (outdir != NULL) { - const char *tile_ext = (output_format == OUTPUT_MLT) ? ".mlt" : ".pbf"; - dir_write_tile(outdir, z, tx, ty, compressed, tile_ext); + dir_write_tile(outdir, z, tx, ty, compressed, tile_format_extension(output_format)); } if (pthread_mutex_unlock(&db_lock) != 0) {