diff --git a/.gitignore b/.gitignore index fc77c550..24efe134 100644 --- a/.gitignore +++ b/.gitignore @@ -42,6 +42,9 @@ unit tests/**/*.mbtiles tests/**/*.check tests/**/*.geobuf +tests/mlt/dir-out/ +tests/mlt/roundtrip-dir/ +tests/mlt/overzoom.pbf mlt-build/ diff --git a/Makefile b/Makefile index 2311830b..006f5f14 100644 --- a/Makefile +++ b/Makefile @@ -57,8 +57,9 @@ H = $(wildcard *.h) $(wildcard *.hpp) C = $(wildcard *.c) $(wildcard *.cpp) INCLUDES = -I/usr/local/include -I. -Iclipper2/include -MLT_INCLUDES = -Imaplibre-tile-spec/cpp/include -isystem maplibre-tile-spec/cpp/vendor/fsst -MLT_LIBS = mlt-build/libmlt-cpp-encoder.a mlt-build/libfsst-lib.a mlt-build/fastpfor/libFastPFOR.a +MLT_INCLUDES = -isystem maplibre-tile-spec/cpp/include -isystem maplibre-tile-spec/cpp/vendor/fsst +MLT_DECODE_LIBS = mlt-build/libmlt-cpp.a mlt-build/libfsst-lib.a mlt-build/fastpfor/libFastPFOR.a +MLT_LIBS = mlt-build/libmlt-cpp-encoder.a $(MLT_DECODE_LIBS) MLT_BUILD_STAMP = mlt-build/.built LIBS = -L/usr/local/lib @@ -72,30 +73,30 @@ $(MLT_BUILD_STAMP): maplibre-tile-spec/cpp/CMakeLists.txt -DMLT_WITH_TOOLS=OFF \ -DCMAKE_CXX_STANDARD=20 \ $(if $(VERBOSE),,--log-level=WARNING) > /dev/null - cmake --build mlt-build --target mlt-cpp-encoder $(if $(VERBOSE),,-- -s) > /dev/null + cmake --build mlt-build --target mlt-cpp mlt-cpp-encoder $(if $(VERBOSE),,-- -s) > /dev/null touch $@ $(MLT_LIBS): $(MLT_BUILD_STAMP) -tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o mlt.o serial.o main.o platform.o text.o dirtiles.o pmtiles_file.o plugin.o read_json.o write_json.o geobuf.o flatgeobuf.o evaluator.o geocsv.o csv.o geojson-loop.o json_logger.o visvalingam.o compression.o clip.o sort.o attribute.o thread.o shared_borders.o clipper2/src/clipper.engine.o $(MLT_LIBS) +tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o mlt.o mlt_decode.o serial.o main.o platform.o text.o dirtiles.o pmtiles_file.o plugin.o read_json.o write_json.o geobuf.o flatgeobuf.o evaluator.o geocsv.o csv.o geojson-loop.o json_logger.o visvalingam.o compression.o clip.o sort.o attribute.o thread.o shared_borders.o clipper2/src/clipper.engine.o $(MLT_LIBS) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread tippecanoe-enumerate: enumerate.o $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lsqlite3 -tippecanoe-decode: decode.o projection.o mvt.o write_json.o text.o jsonpull/jsonpull.o dirtiles.o pmtiles_file.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 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 +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) $(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 projection.o clip.o attribute.o jsonpull/jsonpull.o evaluator.o read_json.o clipper2/src/clipper.engine.o +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) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread -tippecanoe-overzoom: overzoom.o mvt.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 +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) $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread -include $(wildcard *.d) @@ -103,7 +104,7 @@ tippecanoe-overzoom: overzoom.o mvt.o clip.o evaluator.o jsonpull/jsonpull.o tex %.o: %.c $(CC) -MMD $(PG) $(INCLUDES) $(FINAL_FLAGS) $(CFLAGS) -c -o $@ $< -mlt.o: mlt.cpp mlt-build/libmlt-cpp-encoder.a +mlt.o mlt_decode.o: %.o: %.cpp $(MLT_BUILD_STAMP) $(CXX) -MMD $(PG) $(INCLUDES) $(MLT_INCLUDES) $(FINAL_FLAGS) $(CXXFLAGS) -std=c++20 -c -o $@ $< %.o: %.cpp @@ -118,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 +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 ./unit suffixes = json json.gz @@ -626,6 +627,29 @@ mlt-test: tippecanoe rm -f tests/mlt/points.mbtiles tests/mlt/points-mvt.mbtiles tests/mlt/points-tess.mbtiles rm -rf tests/mlt/dir-out +mlt-decode-test: tippecanoe tippecanoe-decode tile-join tippecanoe-overzoom + # tippecanoe-decode reads MLT tiles out of an mbtiles file + ./tippecanoe -q --output-format=mlt -z5 -f -o tests/mlt/roundtrip.mbtiles tests/mlt/roundtrip.geojson + ./tippecanoe-decode -x generator -x generator_options tests/mlt/roundtrip.mbtiles > tests/mlt/roundtrip.mbtiles.json.check + cmp tests/mlt/roundtrip.mbtiles.json.check tests/mlt/roundtrip.mbtiles.json + # tile-join reads MLT tiles and writes MVT tiles + ./tile-join -f -o tests/mlt/joined.mbtiles tests/mlt/roundtrip.mbtiles + ./tippecanoe-decode -x generator -x generator_options tests/mlt/joined.mbtiles > tests/mlt/joined.mbtiles.json.check + cmp tests/mlt/joined.mbtiles.json.check tests/mlt/joined.mbtiles.json + # tippecanoe-decode reads MLT tiles out of a tile directory + rm -rf tests/mlt/roundtrip-dir + ./tippecanoe -q --output-format=mlt -z5 -f -e tests/mlt/roundtrip-dir tests/mlt/roundtrip.geojson + ./tippecanoe-decode -x generator -x generator_options tests/mlt/roundtrip-dir > tests/mlt/roundtrip-dir.json.check + cmp tests/mlt/roundtrip-dir.json.check tests/mlt/roundtrip-dir.json + # tippecanoe-overzoom reads a single MLT tile + ./tippecanoe-overzoom -o tests/mlt/overzoom.pbf tests/mlt/roundtrip-dir/2/0/1.mlt 2/0/1 4/2/6 + ./tippecanoe-decode tests/mlt/overzoom.pbf 4 2 6 > tests/mlt/overzoom.json.check + cmp tests/mlt/overzoom.json.check tests/mlt/overzoom.json + rm -f tests/mlt/roundtrip.mbtiles tests/mlt/roundtrip.mbtiles.json.check + rm -f tests/mlt/joined.mbtiles tests/mlt/joined.mbtiles.json.check + rm -f tests/mlt/roundtrip-dir.json.check tests/mlt/overzoom.pbf tests/mlt/overzoom.json.check + rm -rf tests/mlt/roundtrip-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 4bc32f97..4f0cde9a 100644 --- a/README.md +++ b/README.md @@ -553,7 +553,7 @@ the same layer, enclose them in an `all` expression so they will all be evaluate * `-pf` or `--no-feature-limit`: Don't limit tiles to 200,000 features * `-pk` or `--no-tile-size-limit`: Don't limit tiles to 500K bytes * `-pC` or `--no-tile-compression`: Don't compress the vector tile data. If you are getting "Unimplemented type 3" error messages from a renderer, it is probably because it expects uncompressed tiles using this option rather than the normal gzip-compressed tiles. - * `--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)). + * `--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)). MLT tilesets can be read back by `tippecanoe-decode`, `tile-join`, and `tippecanoe-overzoom`. Because MLT attribute columns have a single type for the whole layer, while MVT attribute values are individually typed, an attribute whose values are a mix of numbers and strings (or of booleans and numbers) is encoded as strings. * `--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. @@ -776,6 +776,10 @@ all the sources are read and their combined contents are written to the new 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. + The options are: ### Output tileset @@ -906,6 +910,10 @@ or on an individual tile: tippecanoe-decode file.mbtiles zoom x y tippecanoe-decode file.vector.pbf zoom x y +Tiles can be either Mapbox Vector Tiles or MapLibre Tiles; the encoding is detected +from the tile data, so no option is needed to decode a tileset that was written with +`--output-format=mlt`. + Unless you use `-c`, the output is a set of nested FeatureCollections identifying each tile and layer separately. Note that the same features generally appear at all zooms, so the output for the file will have many copies of the same features at different @@ -1006,6 +1014,9 @@ reads tile `inz/inx/iny` of `in.mvt.gz` and produces tile `outz/outx/outy` of `o reads tile `inz/inx/iny` of `in.mvt.gz`, tile `in2z/in2x/in2y` of `in2.mvt.gz`, and tile `in3z/in3x/in3y` of `in3.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. + ### Options * `-b` *buffer*: Set the tile buffer in the output tile (default 5) diff --git a/dirtiles.cpp b/dirtiles.cpp index 38184990..9089b165 100644 --- a/dirtiles.cpp +++ b/dirtiles.cpp @@ -76,12 +76,18 @@ static bool numeric(const char *s) { return true; } -static bool pbfname(const char *s) { +// If this is the name of a tile within a tile directory, returns the +// extension that it uses, or NULL if it isn't a tile at all. +static const char *tile_extension(const char *s) { while (*s >= '0' && *s <= '9') { s++; } - return strcmp(s, ".pbf") == 0 || strcmp(s, ".mvt") == 0 || strcmp(s, ".mlt") == 0; + if (strcmp(s, ".pbf") == 0 || strcmp(s, ".mvt") == 0 || strcmp(s, ".mlt") == 0) { + return s; + } + + return NULL; } void check_dir(const char *dir, char **argv, bool force, bool forcetable) { @@ -154,12 +160,11 @@ std::vector enumerate_dirtiles(const char *fname, int minzoom, int maxzoom) struct dirent *dp3; while ((dp3 = readdir(d3)) != NULL) { - if (pbfname(dp3->d_name)) { + const char *ext = tile_extension(dp3->d_name); + if (ext != NULL) { int ty = atoi(dp3->d_name); zxy tile(tz, tx, ty); - if (strstr(dp3->d_name, ".mvt") != NULL) { - tile.extension = ".mvt"; - } + tile.extension = ext; tiles.push_back(tile); } @@ -207,7 +212,7 @@ void dir_erase_zoom(const char *fname, int zoom) { struct dirent *dp3; while ((dp3 = readdir(d3)) != NULL) { - if (pbfname(dp3->d_name)) { + if (tile_extension(dp3->d_name) != NULL) { std::string y = x + "/" + dp3->d_name; if (unlink(y.c_str()) != 0) { perror(y.c_str()); diff --git a/mlt.hpp b/mlt.hpp index d5e25639..3bb08bc3 100644 --- a/mlt.hpp +++ b/mlt.hpp @@ -6,4 +6,12 @@ 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 +// Mapbox Vector Tile data? +bool is_mlt(const std::string &message); + +// Decode a MapLibre Tile into the equivalent vector tile, returning false +// (and complaining to stderr) if it can't be parsed. +bool decode_mlt(const std::string &message, mvt_tile &out); + #endif diff --git a/mlt_decode.cpp b/mlt_decode.cpp new file mode 100644 index 00000000..4e501c8f --- /dev/null +++ b/mlt_decode.cpp @@ -0,0 +1,291 @@ +#include "mlt.hpp" + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace { + +bool read_varint(const char *&p, const char *end, unsigned long long &out) { + out = 0; + int shift = 0; + + while (p < end) { + unsigned char c = (unsigned char) *p++; + if (shift > 63) { + return false; + } + out |= ((unsigned long long) (c & 0x7F)) << shift; + if ((c & 0x80) == 0) { + return true; + } + shift += 7; + } + + return false; +} + +long long round_coord(float v) { + return (long long) std::llround(v); +} + +// Append a sequence of coordinates as a moveto followed by linetos. Rings +// come out of the MLT decoder explicitly closed, but MVT rings are closed +// implicitly by the closepath operation, so the repeated final point is +// dropped. +void add_line(mvt_feature &feature, const mlt::CoordVec &coords, bool ring) { + size_t n = coords.size(); + + if (ring && n > 1 && coords.front() == coords.back()) { + n--; + } + + for (size_t i = 0; i < n; i++) { + feature.geometry.emplace_back(i == 0 ? mvt_moveto : mvt_lineto, + round_coord(coords[i].x), round_coord(coords[i].y)); + } + + if (ring && n > 0) { + feature.geometry.emplace_back(mvt_closepath, 0, 0); + } +} + +void convert_geometry(const mlt::geometry::Geometry &geom, mvt_feature &feature) { + using GeometryType = mlt::metadata::tileset::GeometryType; + namespace geometry = mlt::geometry; + + switch (geom.type) { + case GeometryType::POINT: { + const auto &point = static_cast(geom); + feature.type = mvt_point; + feature.geometry.emplace_back(mvt_moveto, + round_coord(point.getCoordinate().x), + round_coord(point.getCoordinate().y)); + break; + } + + case GeometryType::MULTIPOINT: { + const auto &multipoint = static_cast(geom); + feature.type = mvt_point; + for (const auto &coord : multipoint.getCoordinates()) { + feature.geometry.emplace_back(mvt_moveto, round_coord(coord.x), round_coord(coord.y)); + } + break; + } + + case GeometryType::LINESTRING: { + const auto &linestring = static_cast(geom); + feature.type = mvt_linestring; + add_line(feature, linestring.getCoordinates(), false); + break; + } + + case GeometryType::MULTILINESTRING: { + const auto &multilinestring = static_cast(geom); + feature.type = mvt_linestring; + for (const auto &linestring : multilinestring.getLineStrings()) { + add_line(feature, linestring, false); + } + break; + } + + case GeometryType::POLYGON: { + const auto &polygon = static_cast(geom); + feature.type = mvt_polygon; + for (const auto &ring : polygon.getRings()) { + add_line(feature, ring, true); + } + break; + } + + case GeometryType::MULTIPOLYGON: { + const auto &multipolygon = static_cast(geom); + feature.type = mvt_polygon; + for (const auto &polygon : multipolygon.getPolygons()) { + for (const auto &ring : polygon) { + add_line(feature, ring, true); + } + } + break; + } + } +} + +// Converts one MLT property into the equivalent MVT value, returning false +// for properties that MVT has no way to represent, which are left off the +// feature instead. +struct property_converter { + mvt_value &out; + const std::shared_ptr &pool; + + bool operator()(std::nullptr_t) const { + return false; + } + + bool operator()(bool v) const { + out.type = mvt_bool; + out.numeric_value.bool_value = v; + return true; + } + + bool operator()(std::int32_t v) const { + out.type = mvt_int; + out.numeric_value.int_value = v; + return true; + } + + bool operator()(std::int64_t v) const { + out.type = mvt_int; + out.numeric_value.int_value = v; + return true; + } + + bool operator()(std::uint32_t v) const { + out.type = mvt_uint; + out.numeric_value.uint_value = v; + return true; + } + + bool operator()(std::uint64_t v) const { + out.type = mvt_uint; + out.numeric_value.uint_value = v; + return true; + } + + bool operator()(float v) const { + out.type = mvt_float; + out.numeric_value.float_value = v; + return true; + } + + bool operator()(double v) const { + out.type = mvt_double; + out.numeric_value.double_value = v; + return true; + } + + bool operator()(std::string_view v) const { + out.s = pool; + out.set_string_value(v); + return true; + } + + template + bool operator()(const std::optional &v) const { + if (v.has_value()) { + return (*this)(*v); + } + return false; + } +}; + +void convert_layer(const mlt::Layer &in, mvt_layer &out, const std::shared_ptr &pool) { + out.version = 2; + out.name = in.getName(); + out.extent = in.getExtent(); + + // The property columns are held in an unordered map, so sort the names + // to keep the attributes of the decoded tile in a stable order. + std::vector> columns; + columns.reserve(in.getProperties().size()); + for (const auto &property : in.getProperties()) { + columns.emplace_back(&property.first, &property.second); + } + std::sort(columns.begin(), columns.end(), [](auto const &a, auto const &b) { + return *a.first < *b.first; + }); + + out.features.reserve(in.getFeatures().size()); + + for (const auto &in_feature : in.getFeatures()) { + mvt_feature feature; + + if (in_feature.getID()) { + feature.id = *in_feature.getID(); + feature.has_id = true; + } + + convert_geometry(in_feature.getGeometry(), feature); + + for (const auto &column : columns) { + auto property = column.second->getProperty(in_feature.getIndex()); + if (!property) { + continue; + } + + mvt_value value; + if (std::visit(property_converter{value, pool}, *property)) { + out.tag(feature, *column.first, value); + } + } + + out.features.push_back(std::move(feature)); + } +} + +} // namespace + +bool is_mlt(const std::string &message) { + // An MLT tile is a sequence of layers, each of which is a varint byte + // count followed by a varint tag whose only defined value is 1. + // + // An MVT tile is a protobuf whose only field is the repeated layer + // field 3, so it begins with the byte 0x1a followed by the byte count + // of a layer, which can never be as short as the single byte that + // would be needed to masquerade as an MLT layer tag. + + const char *p = message.data(); + const char *end = p + message.size(); + + unsigned long long layer_length; + if (!read_varint(p, end, layer_length)) { + return false; + } + if (layer_length < 2 || layer_length > (unsigned long long) (end - p)) { + return false; + } + + unsigned long long layer_tag; + if (!read_varint(p, end, layer_tag)) { + return false; + } + + return layer_tag == 1; +} + +bool decode_mlt(const std::string &message, mvt_tile &out) { + try { + mlt::Decoder decoder(true); + mlt::MapLibreTile tile = decoder.decode(mlt::DataView(message.data(), message.size())); + + std::shared_ptr pool = std::make_shared(); + + out.layers.clear(); + out.layers.reserve(tile.getLayers().size()); + + for (const auto &layer : tile.getLayers()) { + mvt_layer out_layer; + convert_layer(layer, out_layer, pool); + out.layers.push_back(std::move(out_layer)); + } + + return true; + } catch (std::exception const &e) { + fprintf(stderr, "MLT decoding error: %s\n", e.what()); + return false; + } +} diff --git a/mvt.cpp b/mvt.cpp index 18f239d2..df81b596 100644 --- a/mvt.cpp +++ b/mvt.cpp @@ -8,6 +8,7 @@ #include #include #include "mvt.hpp" +#include "mlt.hpp" #include "geometry.hpp" #include "protozero/varint.hpp" #include "protozero/pbf_reader.hpp" @@ -127,6 +128,10 @@ bool mvt_tile::decode(const std::string &message, bool &was_compressed) { was_compressed = false; } + if (is_mlt(src)) { + return decode_mlt(src, *this); + } + protozero::pbf_reader reader(src); std::shared_ptr string_pool = std::make_shared(); diff --git a/tests/mlt/joined.mbtiles.json b/tests/mlt/joined.mbtiles.json new file mode 100644 index 00000000..d1144cb8 --- /dev/null +++ b/tests/mlt/joined.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/roundtrip.mbtiles", +"format": "pbf", +"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/roundtrip.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/tests/mlt/overzoom.json b/tests/mlt/overzoom.json new file mode 100644 index 00000000..861ea827 --- /dev/null +++ b/tests/mlt/overzoom.json @@ -0,0 +1,13 @@ +{ "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.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 ] ] ] ] } } +] } +] } diff --git a/tests/mlt/roundtrip-dir.json b/tests/mlt/roundtrip-dir.json new file mode 100644 index 00000000..e1a34c42 --- /dev/null +++ b/tests/mlt/roundtrip-dir.json @@ -0,0 +1,104 @@ +{ "type": "FeatureCollection", "properties": { +"antimeridian_adjusted_bounds": "-122.900000,37.200000,-122.350000,37.950000", +"bounds": "-122.900000,37.200000,-122.350000,37.950000", +"center": "-122.350000,37.200000,5", +"description": "tests/mlt/roundtrip-dir", +"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\":7,\"geometry\":\"Point\",\"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/roundtrip-dir", +"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/tests/mlt/roundtrip.geojson b/tests/mlt/roundtrip.geojson new file mode 100644 index 00000000..4055015d --- /dev/null +++ b/tests/mlt/roundtrip.geojson @@ -0,0 +1,9 @@ +{"type":"FeatureCollection","features":[ +{"type":"Feature","id":1,"properties":{"name":"point","kind":"point","count":100,"ratio":1.5,"active":true,"nested":{"a":1,"b":"two"}},"geometry":{"type":"Point","coordinates":[-122.4,37.8]}}, +{"type":"Feature","id":2,"properties":{"name":"multipoint","kind":"point","count":-200,"ratio":-0.25,"active":false},"geometry":{"type":"MultiPoint","coordinates":[[-122.5,37.9],[-122.45,37.85],[-122.55,37.95]]}}, +{"type":"Feature","id":3,"properties":{"name":"line","kind":"line","count":300,"ratio":2},"geometry":{"type":"LineString","coordinates":[[-122.6,37.7],[-122.5,37.75],[-122.4,37.7]]}}, +{"type":"Feature","id":4,"properties":{"name":"multiline","kind":"line","count":4000000000,"ratio":0.125},"geometry":{"type":"MultiLineString","coordinates":[[[-122.7,37.6],[-122.6,37.65]],[[-122.5,37.6],[-122.4,37.65]]]}}, +{"type":"Feature","id":5,"properties":{"name":"polygon with hole","kind":"polygon","count":500,"ratio":3.25,"active":true},"geometry":{"type":"Polygon","coordinates":[[[-122.8,37.4],[-122.4,37.4],[-122.4,37.55],[-122.8,37.55],[-122.8,37.4]],[[-122.7,37.45],[-122.7,37.5],[-122.5,37.5],[-122.5,37.45],[-122.7,37.45]]]}}, +{"type":"Feature","id":6,"properties":{"name":"multipolygon","kind":"polygon","count":600},"geometry":{"type":"MultiPolygon","coordinates":[[[[-122.9,37.2],[-122.7,37.2],[-122.7,37.3],[-122.9,37.3],[-122.9,37.2]]],[[[-122.6,37.2],[-122.4,37.2],[-122.4,37.3],[-122.6,37.3],[-122.6,37.2]],[[-122.55,37.22],[-122.55,37.28],[-122.45,37.28],[-122.45,37.22],[-122.55,37.22]]]]}}, +{"type":"Feature","properties":{"name":"no id","kind":"point","unicode":"påíñtß 堤防"},"geometry":{"type":"Point","coordinates":[-122.35,37.75]}} +]} diff --git a/tests/mlt/roundtrip.mbtiles.json b/tests/mlt/roundtrip.mbtiles.json new file mode 100644 index 00000000..070c72bc --- /dev/null +++ b/tests/mlt/roundtrip.mbtiles.json @@ -0,0 +1,104 @@ +{ "type": "FeatureCollection", "properties": { +"antimeridian_adjusted_bounds": "-122.900000,37.200000,-122.350000,37.950000", +"bounds": "-122.900000,37.200000,-122.350000,37.950000", +"center": "-122.350000,37.200000,5", +"description": "tests/mlt/roundtrip.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\":7,\"geometry\":\"Point\",\"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/roundtrip.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 ] ] ] ] } } +] } +] } +] }