diff --git a/CHANGELOG.md b/CHANGELOG.md index ba4664b64..d250fca66 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,8 @@ +# 2.33.0 + +* Further reduce memory usage of --no-simplification-of-shared-nodes by calculating the list of shared nodes globally using temporary files rather than in memory for each individual tile +* Make --no-simplification-of-shared-nodes behave for LineStrings as it does for Polygons, preventing simplification only at crossings, convergences, and divergences, not at every point along collinear segments + # 2.32.1 * Reduce memory usage of --no-simplification-of-shared-nodes for polygons diff --git a/Makefile b/Makefile index 9b73b275a..7cd602ff1 100644 --- a/Makefile +++ b/Makefile @@ -58,7 +58,7 @@ C = $(wildcard *.c) $(wildcard *.cpp) INCLUDES = -I/usr/local/include -I. LIBS = -L/usr/local/lib -tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.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 +tippecanoe: geojson.o jsonpull/jsonpull.o tile.o pool.o mbtiles.o geometry.o projection.o memfile.o mvt.o serial.o main.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 $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread tippecanoe-enumerate: enumerate.o @@ -73,7 +73,7 @@ tile-join: tile-join.o projection.o mbtiles.o mvt.o memfile.o dirtiles.o jsonpul 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 +unit: unit.o text.o sort.o $(CXX) $(PG) $(LIBS) $(FINAL_FLAGS) $(CXXFLAGS) -o $@ $^ $(LDFLAGS) -lm -lz -lsqlite3 -lpthread tippecanoe-overzoom: overzoom.o mvt.o clip.o @@ -169,7 +169,7 @@ parallel-test: $(eval SHELL:=$(ADVSHELL)) cmp tests/parallel/linear-file.json tests/parallel/parallel-pipes.json rm tests/parallel/*.mbtiles tests/parallel/*.json -raw-tiles-test: +raw-tiles-test: tippecanoe tippecanoe-decode tile-join ./tippecanoe -q -f -e tests/raw-tiles/raw-tiles -r1 -pC tests/raw-tiles/hackspots.geojson ./tippecanoe-decode -x generator tests/raw-tiles/raw-tiles > tests/raw-tiles/raw-tiles.json.check cmp tests/raw-tiles/raw-tiles.json.check tests/raw-tiles/raw-tiles.json @@ -187,7 +187,7 @@ raw-tiles-test: cmp tests/raw-tiles/nothing.json.check tests/raw-tiles/nothing.json rm -r tests/raw-tiles/nothing tests/raw-tiles/nothing.json.check -pmtiles-test: +pmtiles-test: tippecanoe tippecanoe-decode tile-join ./tippecanoe -q -f -o tests/pmtiles/hackspots.pmtiles -r1 -pC tests/raw-tiles/hackspots.geojson ./tippecanoe-decode -x generator tests/pmtiles/hackspots.pmtiles > tests/pmtiles/hackspots.json.check cmp tests/pmtiles/hackspots.json.check tests/pmtiles/hackspots.json @@ -224,7 +224,7 @@ pmtiles-test: cmp tests/raw-tiles/nothing.json.check tests/raw-tiles/nothing.json rm -r tests/raw-tiles/nothing.pmtiles tests/raw-tiles/nothing.json.check -decode-test: +decode-test: tippecanoe tippecanoe-decode mkdir -p tests/muni/decode ./tippecanoe -q -z11 -Z11 -f -o tests/muni/decode/multi.mbtiles tests/muni/*.json ./tippecanoe-decode -x generator -l subway tests/muni/decode/multi.mbtiles > tests/muni/decode/multi.mbtiles.json.check @@ -241,7 +241,7 @@ decode-test: cmp tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.stats.json rm -f tests/muni/decode/multi.mbtiles.json.check tests/muni/decode/multi.mbtiles tests/muni/decode/multi.mbtiles.pipeline.json.check tests/muni/decode/multi.mbtiles.stats.json.check tests/muni/decode/multi.mbtiles.onetile.json.check -decode-pmtiles-test: +decode-pmtiles-test: tippecanoe tippecanoe-decode mkdir -p tests/muni/decode ./tippecanoe -q -z11 -Z11 -f -o tests/muni/decode/multi.pmtiles tests/muni/*.json ./tippecanoe-decode -x generator -l subway tests/muni/decode/multi.pmtiles | sed 's/pmtiles/mbtiles/g' > tests/muni/decode/multi.pmtiles.json.check @@ -258,7 +258,7 @@ decode-pmtiles-test: cmp tests/muni/decode/multi.pmtiles.stats.json.check tests/muni/decode/multi.mbtiles.stats.json rm -f tests/muni/decode/multi.pmtiles.json.check tests/muni/decode/multi.pmtiles tests/muni/decode/multi.pmtiles.pipeline.json.check tests/muni/decode/multi.pmtiles.stats.json.check tests/muni/decode/multi.pmtiles.onetile.json.check -pbf-test: +pbf-test: tippecanoe-decode ./tippecanoe-decode -x generator tests/pbf/11-328-791.vector.pbf 11 328 791 > tests/pbf/11-328-791.vector.pbf.out cmp tests/pbf/11-328-791.json tests/pbf/11-328-791.vector.pbf.out rm tests/pbf/11-328-791.vector.pbf.out @@ -266,7 +266,7 @@ pbf-test: cmp tests/pbf/11-328-791.3857.json tests/pbf/11-328-791.3857.vector.pbf.out rm tests/pbf/11-328-791.3857.vector.pbf.out -enumerate-test: +enumerate-test: tippecanoe tippecanoe-enumerate ./tippecanoe -q -z5 -f -o tests/ne_110m_admin_0_countries/out/enum.mbtiles tests/ne_110m_admin_0_countries/in.json.gz ./tippecanoe-enumerate tests/ne_110m_admin_0_countries/out/enum.mbtiles > tests/ne_110m_admin_0_countries/out/enum.check cmp tests/ne_110m_admin_0_countries/out/enum.check tests/ne_110m_admin_0_countries/out/enum @@ -288,7 +288,7 @@ overzoom-test: tippecanoe-overzoom cmp tests/pbf/14-2616-6331.pbf /dev/null rm tests/pbf/14-2616-6331.pbf -join-test: tile-join +join-test: tippecanoe tippecanoe-decode tile-join ./tippecanoe -q -f -z12 -o tests/join-population/tabblock_06001420.mbtiles -YALAND10:'Land area' -L'{"file": "tests/join-population/tabblock_06001420.json", "description": "population"}' ./tippecanoe -q -f -Z5 -z10 -o tests/join-population/macarthur.mbtiles -l macarthur tests/join-population/macarthur.json ./tile-join -q -f -Z6 -z9 -o tests/join-population/macarthur-6-9.mbtiles tests/join-population/macarthur.mbtiles @@ -423,7 +423,7 @@ join-test: tile-join cmp tests/ne_110m_ocean/join/joined.mbtiles.json.check tests/ne_110m_ocean/join/joined.mbtiles.json rm -f tests/ne_110m_ocean/join/ocean.mbtiles tests/ne_110m_ocean/join/countries.mbtiles tests/ne_110m_ocean/join/joined.mbtiles tests/ne_110m_ocean/join/joined.mbtiles.json.check -join-filter-test: +join-filter-test: tippecanoe tippecanoe-decode tile-join # Comes out different from the direct tippecanoe run because null attributes are lost ./tippecanoe -q -z0 -f -o tests/feature-filter/out/all.mbtiles tests/feature-filter/in.json ./tile-join -q -J tests/feature-filter/filter -f -o tests/feature-filter/out/filtered.mbtiles tests/feature-filter/out/all.mbtiles @@ -446,7 +446,7 @@ json-tool-test: tippecanoe-json-tool rm -f tests/join-population/tabblock_06001420.json.sort tests/join-population/tabblock_06001420.json.sort.joined rm -f tests/join-population/tabblock_06001420-null.json.sort.joined -allow-existing-test: +allow-existing-test: tippecanoe # Make a tileset ./tippecanoe -q -Z0 -z0 -f -o tests/allow-existing/both.mbtiles tests/coalesce-tract/tl_2010_06001_tract10.json # Writing to existing should fail @@ -475,7 +475,7 @@ allow-existing-test: if ./tippecanoe -q -Z10 -z11 -F -o tests/allow-existing/both.pmtiles tests/coalesce-tract/tl_2010_06001_tract10.json; then exit 1; else exit 0; fi rm -r tests/allow-existing/both.pmtiles tests/allow-existing/both.dir.json.check tests/allow-existing/both.dir tests/allow-existing/both.mbtiles.json.check tests/allow-existing/both.mbtiles -csv-test: +csv-test: tippecanoe tippecanoe-decode # Reading from named CSV ./tippecanoe -q -zg -f -o tests/csv/out.mbtiles tests/csv/ne_110m_populated_places_simple.csv ./tippecanoe-decode -x generator -x generator_options tests/csv/out.mbtiles > tests/csv/out.mbtiles.json.check @@ -492,7 +492,7 @@ csv-test: cmp tests/csv/out.mbtiles.json.check tests/csv/out.mbtiles.json rm -f tests/csv/out.mbtiles.json.check tests/csv/out.mbtiles -layer-json-test: +layer-json-test: tippecanoe tippecanoe-decode # GeoJSON with description and named layer ./tippecanoe -q -z0 -r1 -yNAME -f -o tests/layer-json/out.mbtiles -L'{"file":"tests/ne_110m_populated_places/in.json", "description":"World cities", "layer":"places"}' ./tippecanoe-decode -x generator -x generator_options tests/layer-json/out.mbtiles > tests/layer-json/out.mbtiles.json.check diff --git a/README.md b/README.md index c894c3405..6a1c85570 100644 --- a/README.md +++ b/README.md @@ -492,7 +492,7 @@ the same layer, enclose them in an `all` expression so they will all be evaluate * `-ps` or `--no-line-simplification`: Don't simplify lines and polygons * `-pS` or `--simplify-only-low-zooms`: Don't simplify lines and polygons at maxzoom (but do simplify at lower zooms) * `--simplification-at-maximum-zoom=`_scale_: Use the specified _scale_ at maxzoom instead of the standard simplification scale (which still applies at lower zooms) - * `-pn` or `--no-simplification-of-shared-nodes`: Don't simplify away nodes that appear in more than one feature or are used multiple times within the same feature, so that the intersection node will not be lost from intersecting roads. (This will not be effective if you also use `--coalesce`.) For polygons, the vertices where polygon rings meet will be detected, and the shared edges between the polygons will be simplified identically if possible. Use this instead of `--detect-shared-borders`. + * `-pn` or `--no-simplification-of-shared-nodes`: Don't simplify away nodes at which LineStrings or Polygon rings converge, diverge, or cross. (This will not be effective if you also use `--coalesce`.) In between intersection nodes, LineString segments or polygon edges will be simplified identically in each feature if possible. Use this instead of `--detect-shared-borders`. * `-pt` or `--no-tiny-polygon-reduction`: Don't combine the area of very small polygons into small squares that represent their combined area. * `-pT` or `--no-tiny-polygon-reduction-at-maximum-zoom`: Combine the area of very small polygons into small squares that represent their combined area only at zoom levels below the maximum. * `--tiny-polygon-size=`_size_: Use the specified _size_ for tiny polygons instead of the default 2. Anything above 6 or so will lead to visible artifacts with the default tile detail. diff --git a/geometry.cpp b/geometry.cpp index 13a6c3305..07ff15290 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -485,7 +485,7 @@ drawvec impose_tile_boundaries(drawvec &geom, long long extent) { return out; } -drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds, double simplification, size_t retain, drawvec const &shared_nodes) { +drawvec simplify_lines(drawvec &geom, int z, int tx, int ty, int detail, bool mark_tile_bounds, double simplification, size_t retain, drawvec const &shared_nodes, struct node *shared_nodes_map, size_t nodepos) { int res = 1 << (32 - detail - z); long long area = 1LL << (32 - z); @@ -501,10 +501,35 @@ drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds, } if (prevent[P_SIMPLIFY_SHARED_NODES]) { + // This is kind of weird, because we have two lists of shared nodes to look through: + // * the drawvec, which is nodes that were introduced during clipping to the tile edge, + // and which are in local tile coordinates + // * the shared_nodes_map, which was made globally before tiling began, and which + // is in global quadkey coordinates. + // To look through the latter, we need to offset and encode the coordinates + // of the feature we are simplifying. + auto pt = std::lower_bound(shared_nodes.begin(), shared_nodes.end(), geom[i]); if (pt != shared_nodes.end() && *pt == geom[i]) { geom[i].necessary = true; } + + if (nodepos > 0) { + // offset to global + draw d = geom[i]; + if (z != 0) { + d.x += tx * (1LL << (32 - z)); + d.y += ty * (1LL << (32 - z)); + } + + // to quadkey + struct node n; + n.index = encode_quadkey((unsigned) d.x, (unsigned) d.y); + + if (bsearch(&n, shared_nodes_map, nodepos / sizeof(node), sizeof(node), nodecmp) != NULL) { + geom[i].necessary = true; + } + } } } @@ -661,6 +686,9 @@ drawvec fix_polygon(drawvec &geom) { ring = tmp; } + // Now we are rotating the ring to make the first/last point + // one that would be unlikely to be simplified away. + // calculate centroid // a + 1 < size() because point 0 is duplicated at the end long long xtotal = 0; diff --git a/geometry.hpp b/geometry.hpp index 26f311c33..5da7cdf33 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -49,6 +49,10 @@ struct draw { } } + bool operator>(draw const &s) const { + return s < *this; + } + bool operator==(draw const &s) const { return y == s.y && x == s.x; } @@ -74,7 +78,7 @@ drawvec clip_lines(drawvec &geom, int z, long long buffer); drawvec stairstep(drawvec &geom, int z, int detail); bool point_within_tile(long long x, long long y, int z); int quick_check(long long *bbox, int z, long long buffer); -drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds, double simplification, size_t retain, drawvec const &shared_nodes); +drawvec simplify_lines(drawvec &geom, int z, int tx, int ty, int detail, bool mark_tile_bounds, double simplification, size_t retain, drawvec const &shared_nodes, struct node *shared_nodes_map, size_t nodepos); drawvec reorder_lines(drawvec &geom); drawvec fix_polygon(drawvec &geom); std::vector chop_polygon(std::vector &geoms); diff --git a/main.cpp b/main.cpp index 1ec69d4a8..2c303a99e 100644 --- a/main.cpp +++ b/main.cpp @@ -65,6 +65,7 @@ #include "text.hpp" #include "errors.hpp" #include "read_json.hpp" +#include "sort.hpp" static int low_detail = 12; static int full_detail = -1; @@ -121,7 +122,8 @@ void checkdisk(std::vector *r) { for (size_t i = 0; i < r->size(); i++) { // Pool and tree are used once. // Geometry and index will be duplicated during sorting and tiling. - used += 2 * (*r)[i].geompos + 2 * (*r)[i].indexpos + (*r)[i].poolfile->off + (*r)[i].treefile->off; + used += 2 * (*r)[i].geompos + 2 * (*r)[i].indexpos + (*r)[i].poolfile->off + (*r)[i].treefile->off + + (*r)[i].vertexpos + (*r)[i].nodepos; } static int warned = 0; @@ -1207,6 +1209,34 @@ void choose_first_zoom(long long *file_bbox, long long *file_bbox1, long long *f } } +int vertexcmp(const void *void1, const void *void2) { + vertex *v1 = (vertex *) void1; + vertex *v2 = (vertex *) void2; + + if (v1->mid < v2->mid) { + return -1; + } + if (v1->mid > v2->mid) { + return 1; + } + + if (v1->p1 < v2->p1) { + return -1; + } + if (v1->p1 > v2->p1) { + return 1; + } + + if (v1->p2 < v2->p2) { + return -1; + } + if (v1->p2 > v2->p2) { + return 1; + } + + return 0; +} + std::pair read_input(std::vector &sources, char *fname, int maxzoom, int minzoom, int basezoom, double basezoom_marker_width, sqlite3 *outdb, const char *outdir, std::set *exclude, std::set *include, int exclude_all, json_object *filter, double droprate, int buffer, const char *tmpdir, double gamma, int read_parallel, int forcetable, const char *attribution, bool uses_gamma, long long *file_bbox, long long *file_bbox1, long long *file_bbox2, const char *prefilter, const char *postfilter, const char *description, bool guess_maxzoom, bool guess_cluster_maxzoom, std::map const *attribute_types, const char *pgm, std::map const *attribute_accum, std::map const &attribute_descriptions, std::string const &commandline, int minimum_maxzoom) { int ret = EXIT_SUCCESS; @@ -1219,11 +1249,15 @@ std::pair read_input(std::vector &sources, char *fname, i char treename[strlen(tmpdir) + strlen("/tree.XXXXXXXX") + 1]; char geomname[strlen(tmpdir) + strlen("/geom.XXXXXXXX") + 1]; char indexname[strlen(tmpdir) + strlen("/index.XXXXXXXX") + 1]; + char vertexname[strlen(tmpdir) + strlen("/vertex.XXXXXXXX") + 1]; + char nodename[strlen(tmpdir) + strlen("/node.XXXXXXXX") + 1]; snprintf(poolname, sizeof(poolname), "%s%s", tmpdir, "/pool.XXXXXXXX"); snprintf(treename, sizeof(treename), "%s%s", tmpdir, "/tree.XXXXXXXX"); snprintf(geomname, sizeof(geomname), "%s%s", tmpdir, "/geom.XXXXXXXX"); snprintf(indexname, sizeof(indexname), "%s%s", tmpdir, "/index.XXXXXXXX"); + snprintf(vertexname, sizeof(vertexname), "%s%s", tmpdir, "/vertex.XXXXXXXX"); + snprintf(nodename, sizeof(nodename), "%s%s", tmpdir, "/node.XXXXXXXX"); r->poolfd = mkstemp_cloexec(poolname); if (r->poolfd < 0) { @@ -1245,6 +1279,16 @@ std::pair read_input(std::vector &sources, char *fname, i perror(indexname); exit(EXIT_OPEN); } + r->vertexfd = mkstemp_cloexec(vertexname); + if (r->vertexfd < 0) { + perror(vertexname); + exit(EXIT_OPEN); + } + r->nodefd = mkstemp_cloexec(nodename); + if (r->nodefd < 0) { + perror(nodename); + exit(EXIT_OPEN); + } r->poolfile = memfile_open(r->poolfd); if (r->poolfile == NULL) { @@ -1266,13 +1310,27 @@ std::pair read_input(std::vector &sources, char *fname, i perror(indexname); exit(EXIT_OPEN); } + r->vertexfile = fopen_oflag(vertexname, "w+b", O_RDWR | O_CLOEXEC); + if (r->vertexfile == NULL) { + perror(("open vertexfile " + std::string(vertexname)).c_str()); + exit(EXIT_OPEN); + } + r->nodefile = fopen_oflag(nodename, "w+b", O_RDWR | O_CLOEXEC); + if (r->nodefile == NULL) { + perror(nodename); + exit(EXIT_OPEN); + } r->geompos = 0; r->indexpos = 0; + r->vertexpos = 0; + r->nodepos = 0; unlink(poolname); unlink(treename); unlink(geomname); unlink(indexname); + unlink(vertexname); + unlink(nodename); // To distinguish a null value { @@ -1842,6 +1900,9 @@ std::pair read_input(std::vector &sources, char *fname, i fflush(stderr); } + std::atomic vertexpos(0); + std::atomic nodepos(0); + for (size_t i = 0; i < CPUS; i++) { if (fclose(readers[i].geomfile) != 0) { perror("fclose geom"); @@ -1857,6 +1918,13 @@ std::pair read_input(std::vector &sources, char *fname, i perror("stat geom\n"); exit(EXIT_STAT); } + + vertexpos += readers[i].vertexpos; + nodepos += readers[i].nodepos; + } + + if (!quiet) { + fprintf(stderr, "Merging string pool \r"); } // Create a combined string pool @@ -1885,7 +1953,6 @@ std::pair read_input(std::vector &sources, char *fname, i } unlink(poolname); - std::atomic poolpos(0); for (size_t i = 0; i < CPUS; i++) { @@ -1949,6 +2016,162 @@ std::pair read_input(std::vector &sources, char *fname, i madvise(stringpool, poolpos, MADV_RANDOM); } + if (!quiet) { + fprintf(stderr, "Merging vertices \r"); + } + + // Sort the vertices; + // find nodes where the same central point is part of two different vertices + { + std::string tmpname = std::string(tmpdir) + "/vertex2.XXXXXX"; + int vertexfd = mkstemp((char *) tmpname.c_str()); + if (vertexfd < 0) { + perror(("mkstemp vertexfile " + std::string(tmpname)).c_str()); + exit(EXIT_OPEN); + } + unlink(tmpname.c_str()); + FILE *vertex_out = fdopen(vertexfd, "w+b"); + if (vertex_out == NULL) { + perror(tmpname.c_str()); + exit(EXIT_OPEN); + } + + std::vector vertex_readers; + for (size_t i = 0; i < CPUS; i++) { + vertex_readers.push_back(readers[i].vertexfile); + rewind(readers[i].vertexfile); + } + fqsort(vertex_readers, sizeof(vertex), vertexcmp, vertex_out, memsize / 10); + + for (size_t i = 0; i < CPUS; i++) { + if (fclose(readers[i].vertexfile) != 0) { + perror("fclose vertex"); + exit(EXIT_CLOSE); + } + } + + rewind(vertex_out); + + vertex prev(draw(VT_MOVETO, 0, 0), draw(VT_MOVETO, 0, 0), draw(VT_MOVETO, 0, 0)); + vertex v(draw(VT_MOVETO, 0, 0), draw(VT_MOVETO, 0, 0), draw(VT_MOVETO, 0, 0)); + while (fread((void *) &v, sizeof(vertex), 1, vertex_out)) { + if (v.mid == prev.mid && (v.p1 != prev.p1 || v.p2 != prev.p2)) { + long long x = v.mid.x * (1LL << geometry_scale); + long long y = v.mid.y * (1LL << geometry_scale); + +#if 0 + double lon, lat; + tile2lonlat(x, y, 32, &lon, &lat); + printf("{\"type\":\"Feature\", \"properties\":{}, \"geometry\":{\"type\":\"Point\", \"coordinates\":[%f,%f]}}\n", lon, lat); +#endif + + struct node n; + n.index = encode_quadkey((unsigned) x, (unsigned) y); + + fwrite_check((char *) &n, sizeof(struct node), 1, readers[0].nodefile, &readers[0].nodepos, "vertices"); + } + prev = v; + } + + fclose(vertex_out); + } + + if (!quiet) { + fprintf(stderr, "Merging nodes \r"); + } + + // Sort nodes that can't be simplified away; scan the list to remove duplicates + + FILE *shared_nodes; + node *shared_nodes_map = NULL; // will be null if there are no shared nodes + { + // sort + + std::string tmpname = std::string(tmpdir) + "/node2.XXXXXX"; + int nodefd = mkstemp((char *) tmpname.c_str()); + if (nodefd < 0) { + perror(("mkstemp nodefile " + std::string(tmpname)).c_str()); + exit(EXIT_OPEN); + } + unlink(tmpname.c_str()); + FILE *node_out; + node_out = fdopen(nodefd, "w+b"); + if (node_out == NULL) { + perror(tmpname.c_str()); + exit(EXIT_OPEN); + } + + std::vector node_readers; + for (size_t i = 0; i < CPUS; i++) { + node_readers.push_back(readers[i].nodefile); + rewind(readers[i].nodefile); + } + + fqsort(node_readers, sizeof(node), nodecmp, node_out, memsize / 10); + + for (size_t i = 0; i < CPUS; i++) { + if (fclose(readers[i].nodefile) != 0) { + perror("fclose node"); + exit(EXIT_CLOSE); + } + } + + rewind(node_out); + + // scan + + tmpname = std::string(tmpdir) + "/node3.XXXXXX"; + nodefd = mkstemp((char *) tmpname.c_str()); + if (nodefd < 0) { + perror(("mkstemp nodefile " + std::string(tmpname)).c_str()); + exit(EXIT_OPEN); + } + unlink(tmpname.c_str()); + shared_nodes = fdopen(nodefd, "w+b"); + if (shared_nodes == NULL) { + perror(tmpname.c_str()); + exit(EXIT_OPEN); + } + + // `written` is to see if this node has already been preserved + // and doesn't need to be preserved again + struct node written; + written.index = ULONG_MAX; + + nodepos = 0; + struct node here; + while (fread((void *) &here, sizeof(here), 1, node_out)) { + if (nodecmp((void *) &here, (void *) &written) != 0) { + fwrite_check((void *) &here, sizeof(here), 1, shared_nodes, &nodepos, "shared nodes"); + written = here; + +#if 0 + unsigned wx, wy; + decode_quadkey(here.index, &wx, &wy); + double lon, lat; + tile2lonlat(wx, wy, 32, &lon, &lat); + printf("{\"type\":\"Feature\", \"properties\":{}, \"geometry\":{\"type\":\"Point\", \"coordinates\":[%f,%f]}}\n", lon, lat); +#endif + } + } + + fflush(shared_nodes); + + if (nodepos > 0) { + shared_nodes_map = (node *) mmap(NULL, nodepos, PROT_READ, MAP_PRIVATE, nodefd, 0); + if (shared_nodes_map == (node *) MAP_FAILED) { + perror("mmap nodes"); + exit(EXIT_MEMORY); + } + } + + fclose(node_out); + } + + if (!quiet) { + fprintf(stderr, "Merging index \r"); + } + char indexname[strlen(tmpdir) + strlen("/index.XXXXXXXX") + 1]; snprintf(indexname, sizeof(indexname), "%s%s", tmpdir, "/index.XXXXXXXX"); @@ -2023,7 +2246,9 @@ std::pair read_input(std::vector &sources, char *fname, i long long s = progress_seq; long long geompos_print = geompos; long long poolpos_print = poolpos; - fprintf(stderr, "%lld features, %lld bytes of geometry, %lld bytes of string pool\n", s, geompos_print, poolpos_print); + long long vertexpos_print = vertexpos; + long long nodepos_print = nodepos; + fprintf(stderr, "%lld features, %lld bytes of geometry and attributes, %lld bytes of string pool, %lld bytes of vertices, %lld bytes of nodes\n", s, geompos_print, poolpos_print, vertexpos_print, nodepos_print); } if (indexpos == 0) { @@ -2532,7 +2757,7 @@ std::pair read_input(std::vector &sources, char *fname, i std::atomic midx(0); std::atomic midy(0); std::vector strategies; - int written = traverse_zooms(fd, size, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, tmpdir, gamma, full_detail, low_detail, min_detail, pool_off, initial_x, initial_y, simplification, maxzoom_simplification, layermaps, prefilter, postfilter, attribute_accum, filter, strategies, iz); + int written = traverse_zooms(fd, size, stringpool, &midx, &midy, maxzoom, minzoom, outdb, outdir, buffer, fname, tmpdir, gamma, full_detail, low_detail, min_detail, pool_off, initial_x, initial_y, simplification, maxzoom_simplification, layermaps, prefilter, postfilter, attribute_accum, filter, strategies, iz, shared_nodes_map, nodepos); if (maxzoom != written) { if (written > minzoom) { @@ -2555,6 +2780,8 @@ std::pair read_input(std::vector &sources, char *fname, i perror("close pool"); } + fclose(shared_nodes); + // mbtiles-style bounding box and center double minlat = 0, minlon = 0, maxlat = 0, maxlon = 0, midlat = 0, midlon = 0; diff --git a/man/tippecanoe.1 b/man/tippecanoe.1 index 53a7fb979..a1abaaf2c 100644 --- a/man/tippecanoe.1 +++ b/man/tippecanoe.1 @@ -54,7 +54,7 @@ $ tippecanoe \-o file.mbtiles [options] [file.json file.json.gz file.fgb ...] .RE .PP If no files are specified, it reads GeoJSON from the standard input. -If multiple files are specified, each is placed in its own layer. +If multiple files are specified, each is placed in its own layer \[la]#input-files-and-layer-names\[ra]\&. .PP The GeoJSON features need not be wrapped in a FeatureCollection. You can concatenate multiple GeoJSON features or files together, @@ -629,7 +629,7 @@ the line or polygon within one tile unit of its proper location. You can probabl .IP \(bu 2 \fB\fC\-\-simplification\-at\-maximum\-zoom=\fR\fIscale\fP: Use the specified \fIscale\fP at maxzoom instead of the standard simplification scale (which still applies at lower zooms) .IP \(bu 2 -\fB\fC\-pn\fR or \fB\fC\-\-no\-simplification\-of\-shared\-nodes\fR: Don't simplify away nodes that appear in more than one feature or are used multiple times within the same feature, so that the intersection node will not be lost from intersecting roads. (This will not be effective if you also use \fB\fC\-\-coalesce\fR\&.) For polygons, the vertices where polygon rings meet will be detected, and the shared edges between the polygons will be simplified identically if possible. Use this instead of \fB\fC\-\-detect\-shared\-borders\fR\&. +\fB\fC\-pn\fR or \fB\fC\-\-no\-simplification\-of\-shared\-nodes\fR: Don't simplify away nodes at which LineStrings or Polygon rings converge, diverge, or cross. (This will not be effective if you also use \fB\fC\-\-coalesce\fR\&.) In between intersection nodes, LineString segments or polygon edges will be simplified identically in each feature if possible. Use this instead of \fB\fC\-\-detect\-shared\-borders\fR\&. .IP \(bu 2 \fB\fC\-pt\fR or \fB\fC\-\-no\-tiny\-polygon\-reduction\fR: Don't combine the area of very small polygons into small squares that represent their combined area. .IP \(bu 2 diff --git a/serial.cpp b/serial.cpp index a462c920e..9d6f73709 100644 --- a/serial.cpp +++ b/serial.cpp @@ -393,6 +393,29 @@ static std::string strip_zeroes(std::string s) { return s; } +int nodecmp(const void *void1, const void *void2) { + node *n1 = (node *) void1; + node *n2 = (node *) void2; + + if (n1->index < n2->index) { + return -1; + } else if (n1->index > n2->index) { + return 1; + } + + return 0; +} + +static void add_scaled_node(struct reader *r, serialization_state *sst, draw g) { + long long x = SHIFT_LEFT(g.x); + long long y = SHIFT_LEFT(g.y); + + struct node n; + n.index = encode_quadkey((unsigned) x, (unsigned) y); + + fwrite_check((char *) &n, sizeof(struct node), 1, r->nodefile, &r->nodepos, sst->fname); +} + // called from frontends int serialize_feature(struct serialization_state *sst, serial_feature &sf) { struct reader *r = &(*sst->readers)[sst->segment]; @@ -446,11 +469,12 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) { scaled_geometry = simple_clip_poly(scaled_geometry, SHIFT_RIGHT(c.minx), SHIFT_RIGHT(c.miny), SHIFT_RIGHT(c.maxx), SHIFT_RIGHT(c.maxy), prevent[P_SIMPLIFY_SHARED_NODES]); } else if (sf.t == VT_LINE) { scaled_geometry = clip_lines(scaled_geometry, SHIFT_RIGHT(c.minx), SHIFT_RIGHT(c.miny), SHIFT_RIGHT(c.maxx), SHIFT_RIGHT(c.maxy)); - scaled_geometry = remove_noop(scaled_geometry, sf.t, 0); } else if (sf.t == VT_POINT) { scaled_geometry = clip_point(scaled_geometry, SHIFT_RIGHT(c.minx), SHIFT_RIGHT(c.miny), SHIFT_RIGHT(c.maxx), SHIFT_RIGHT(c.maxy)); } + scaled_geometry = remove_noop(scaled_geometry, sf.t, 0); + sf.bbox[0] = LLONG_MAX; sf.bbox[1] = LLONG_MAX; sf.bbox[2] = LLONG_MIN; @@ -480,6 +504,100 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) { return 1; } + if (prevent[P_SIMPLIFY_SHARED_NODES]) { + scaled_geometry = remove_noop(scaled_geometry, sf.t, 0); + + if (sf.t == VT_POLYGON || sf.t == VT_LINE) { + for (size_t i = 0; i < scaled_geometry.size(); i++) { + if (scaled_geometry[i].op == VT_MOVETO) { + size_t j; + + for (j = i + 1; j < scaled_geometry.size(); j++) { + if (scaled_geometry[j].op != VT_LINETO) { + break; + } + } + + if (sf.t == VT_POLYGON && j - i >= 4) { + for (size_t k = i; k < j - 1; k++) { + // % (j - i - 1) because we don't want the duplicate last point + + struct vertex v( + scaled_geometry[(k - i + 0) % (j - i - 1) + i], + scaled_geometry[(k - i + 1) % (j - i - 1) + i], + scaled_geometry[(k - i + 2) % (j - i - 1) + i]); + + fwrite_check((char *) &v, sizeof(struct vertex), 1, r->vertexfile, &r->vertexpos, sst->fname); + } + } else if (sf.t == VT_LINE && j - i >= 2) { + for (size_t k = i; k + 2 < j; k++) { + struct vertex v( + scaled_geometry[k + 0], + scaled_geometry[k + 1], + scaled_geometry[k + 2]); + + fwrite_check((char *) &v, sizeof(struct vertex), 1, r->vertexfile, &r->vertexpos, sst->fname); + } + } + + // since the starting point is never simplified away, + // don't let it be simplified away in any other polygons either. + // Needs to appear twice here so that the check below will see + // it as appearing in multiple features. + add_scaled_node(r, sst, scaled_geometry[i]); + + if (sf.t == VT_LINE && j - i >= 2) { + // linestrings also need to preserve the last point + + add_scaled_node(r, sst, scaled_geometry[j - 1]); + } else if (sf.t == VT_POLYGON && j - i >= 4) { + // To avoid letting polygons get simplified away to nothing, + // also keep the furthest-away point from the initial point + // (which Douglas-Peucker simplification would keep anyway, + // if its search weren't being split up by polygon side). + + double far = 0; + size_t which = i; + for (size_t k = i + 1; k < j - 1; k++) { + double xd = scaled_geometry[k].x - scaled_geometry[i].x; + double yd = scaled_geometry[k].y - scaled_geometry[i].y; + double d = xd * xd + yd * yd; + if (d > far || + ((d == far) && (scaled_geometry[k] < scaled_geometry[which]))) { + far = d; + which = k; + } + } + + add_scaled_node(r, sst, scaled_geometry[which]); + + // And, likewise, the point most distant from those two points, + // which probably would also be the one that Douglas-Peucker + // would keep next. + + far = 0; + size_t which2 = i; + + for (size_t k = i + 1; k < j - 1; k++) { + double d = distance_from_line(scaled_geometry[k].x, scaled_geometry[k].y, + scaled_geometry[i].x, scaled_geometry[i].y, + scaled_geometry[which].x, scaled_geometry[which].y); + if ((d > far) || + ((d == far) && (scaled_geometry[k] < scaled_geometry[which2]))) { + far = d; + which2 = k; + } + } + + add_scaled_node(r, sst, scaled_geometry[which2]); + } + + i = j - 1; + } + } + } + } + if (!sf.has_id) { if (additional[A_GENERATE_IDS]) { sf.has_id = true; @@ -583,7 +701,9 @@ int serialize_feature(struct serialization_state *sst, serial_feature &sf) { for (size_t i = 0; i < scaled_geometry.size(); i++) { ix += scaled_geometry[i].x + scaled_geometry[i].y; } - ix = ix % scaled_geometry.size(); + if (scaled_geometry.size() != 0) { + ix = ix % scaled_geometry.size(); + } // If off the edge of the plane, mask to bring it back into the addressable area midx = SHIFT_LEFT(scaled_geometry[ix].x) & ((1LL << 32) - 1); diff --git a/serial.hpp b/serial.hpp index bb1879e84..a465bfd67 100644 --- a/serial.hpp +++ b/serial.hpp @@ -78,14 +78,20 @@ struct reader { int treefd = -1; int geomfd = -1; int indexfd = -1; + int vertexfd = -1; + int nodefd = -1; struct memfile *poolfile = NULL; struct memfile *treefile = NULL; FILE *geomfile = NULL; FILE *indexfile = NULL; + FILE *vertexfile = NULL; + FILE *nodefile = NULL; std::atomic geompos; std::atomic indexpos; + std::atomic vertexpos; + std::atomic nodepos; long long file_bbox[4] = {0, 0, 0, 0}; @@ -97,7 +103,7 @@ struct reader { char *geom_map = NULL; reader() - : geompos(0), indexpos(0) { + : geompos(0), indexpos(0), vertexpos(0), nodepos(0) { } reader(reader const &r) { @@ -105,11 +111,15 @@ struct reader { treefd = r.treefd; geomfd = r.geomfd; indexfd = r.indexfd; + vertexfd = r.vertexfd; + nodefd = r.nodefd; poolfile = r.poolfile; treefile = r.treefile; geomfile = r.geomfile; indexfile = r.indexfile; + vertexfile = r.vertexfile; + nodefile = r.nodefile; long long p = r.geompos; geompos = p; @@ -117,6 +127,12 @@ struct reader { p = r.indexpos; indexpos = p; + p = r.vertexpos; + vertexpos = p; + + p = r.nodepos; + nodepos = p; + memcpy(file_bbox, r.file_bbox, sizeof(file_bbox)); geomst = r.geomst; @@ -158,6 +174,52 @@ struct serialization_state { int exclude_all = 0; }; +struct vertex { + // these are scaled geometry, + // but because scaling is disabled if P_SHARED_NODES is set, + // they are effectively also world coordinates + draw p1; + draw mid; + draw p2; + + vertex(draw one, draw joint, draw two) { + if (one < two) { + p1 = one; + p2 = two; + } else { + p1 = two; + p2 = one; + } + + mid = joint; + } + + bool operator<(const vertex &v) const { + if (mid < v.mid) { + return true; + } else if (mid == v.mid) { + if (p1 < v.p1) { + return true; + } else if (p1 == v.p1) { + if (p2 < v.p2) { + return true; + } + } + } + + return false; + } +}; + +struct node { + // this is in quadkey coordinates so that the nodes for each tile + // will be adjacent in memory, reducing potential thrashing during + // the binary search. + unsigned long long index; +}; + +int nodecmp(const void *void1, const void *void2); + int serialize_feature(struct serialization_state *sst, serial_feature &sf); void coerce_value(std::string const &key, int &vt, std::string &val, std::map const *attribute_types); diff --git a/sort.cpp b/sort.cpp new file mode 100644 index 000000000..4edb4959c --- /dev/null +++ b/sort.cpp @@ -0,0 +1,123 @@ +#include +#include +#include +#include +#include + +#define MAX_MEMORY (10 * 1024 * 1024) + +void fqsort(std::vector &inputs, size_t width, int (*cmp)(const void *, const void *), FILE *out, size_t mem) { + std::string pivot; + FILE *fp1, *fp2; + + { + // read some elements into memory to choose a pivot from + // + // this is in its own scope so `buf` can go out of scope + // before trying to do any sub-sorts. + + std::string buf; + + bool read_everything = false; + for (size_t i = 0; i < inputs.size(); i++) { + if (buf.size() > mem) { + break; + } + + while (true) { + std::string element; + element.resize(width); + + size_t n = fread((void *) element.c_str(), width, 1, inputs[i]); + if (n == 0) { + if (i + 1 == inputs.size()) { + read_everything = true; + } + break; + } + + buf.append(element); + + if (buf.size() > mem) { + break; + } + } + } + + qsort((void *) buf.c_str(), buf.size() / width, width, cmp); + + // If that was everything we have to sort, we are done. + + if (read_everything) { + fwrite((void *) buf.c_str(), buf.size() / width, width, out); + return; + } + + // Otherwise, choose a pivot from it, make some temporary files, + // write what we have to those files, and then partition the rest + // of the input into them. + + // This would be unstable if the pivot is one of several elements + // that compare equal. Does it matter? + + size_t pivot_off = width * (buf.size() / width / 2); + pivot = std::string(buf, pivot_off, width); + + std::string t1 = "/tmp/sort1.XXXXXX"; + std::string t2 = "/tmp/sort2.XXXXXX"; + + int fd1 = mkstemp((char *) t1.c_str()); + unlink(t1.c_str()); + int fd2 = mkstemp((char *) t2.c_str()); + unlink(t2.c_str()); + + fp1 = fdopen(fd1, "w+b"); + if (fp1 == NULL) { + perror(t1.c_str()); + exit(EXIT_FAILURE); + } + fp2 = fdopen(fd2, "w+b"); + if (fp2 == NULL) { + perror(t2.c_str()); + exit(EXIT_FAILURE); + } + + fwrite((void *) buf.c_str(), sizeof(char), pivot_off, fp1); + fwrite((void *) ((char *) buf.c_str() + pivot_off), sizeof(char), buf.size() - pivot_off, fp2); + } + + // read the remaining input into the temporary files + + for (size_t i = 0; i < inputs.size(); i++) { + while (true) { + std::string element; + element.resize(width); + + size_t n = fread((void *) element.c_str(), width, 1, inputs[i]); + if (n == 0) { + break; + } + + if (cmp((void *) element.c_str(), (void *) pivot.c_str()) < 0) { + fwrite((void *) element.c_str(), width, 1, fp1); + } else { + fwrite((void *) element.c_str(), width, 1, fp2); + } + } + } + + // Now sort the sub-ranges into the output. + + rewind(fp1); + rewind(fp2); + + std::vector v1; + v1.emplace_back(fp1); + fqsort(v1, width, cmp, out, mem); + fclose(fp1); + + std::vector v2; + v2.emplace_back(fp2); + fqsort(v2, width, cmp, out, mem); + fclose(fp2); +} diff --git a/sort.hpp b/sort.hpp new file mode 100644 index 000000000..cfae2cf9e --- /dev/null +++ b/sort.hpp @@ -0,0 +1,6 @@ +#ifndef SORT_HPP +#define SORT_HPP + +void fqsort(std::vector &inputs, size_t width, int (*cmp)(const void *, const void *), FILE *out, size_t mem); + +#endif diff --git a/tests/ne_110m_admin_0_countries/out/-z1_-yname_--no-simplification-of-shared-nodes.json b/tests/ne_110m_admin_0_countries/out/-z1_-yname_--no-simplification-of-shared-nodes.json index f2e3baf5e..d370bc674 100644 --- a/tests/ne_110m_admin_0_countries/out/-z1_-yname_--no-simplification-of-shared-nodes.json +++ b/tests/ne_110m_admin_0_countries/out/-z1_-yname_--no-simplification-of-shared-nodes.json @@ -367,7 +367,7 @@ , { "type": "Feature", "properties": { "name": "New Zealand" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ 172.792969, -40.513799 ], [ 173.232422, -41.310824 ], [ 173.935547, -40.913513 ], [ 174.287109, -41.376809 ], [ 174.287109, -41.771312 ], [ 173.232422, -42.940339 ], [ 172.705078, -43.389082 ], [ 173.056641, -43.834527 ], [ 172.265625, -43.834527 ], [ 171.474609, -44.213710 ], [ 170.595703, -45.890008 ], [ 169.365234, -46.619261 ], [ 168.398438, -46.619261 ], [ 167.783203, -46.316584 ], [ 166.640625, -46.195042 ], [ 166.552734, -45.828799 ], [ 167.080078, -45.089036 ], [ 168.310547, -44.150681 ], [ 168.925781, -43.961191 ], [ 170.507812, -43.004647 ], [ 171.123047, -42.488302 ], [ 171.562500, -41.771312 ], [ 171.914062, -41.508577 ], [ 172.089844, -40.979898 ], [ 172.792969, -40.513799 ] ] ], [ [ [ -187.031250, -43.197167 ], [ -187.031250, -40.847060 ], [ -186.767578, -41.310824 ], [ -186.064453, -40.913513 ], [ -185.712891, -41.376809 ], [ -185.712891, -41.771312 ], [ -187.031250, -43.197167 ] ] ], [ [ [ 172.968750, -34.452218 ], [ 173.583984, -35.029996 ], [ 174.287109, -35.245619 ], [ 174.638672, -36.173357 ], [ 175.341797, -37.230328 ], [ 175.341797, -36.527295 ], [ 175.781250, -36.809285 ], [ 175.957031, -37.579413 ], [ 176.748047, -37.857507 ], [ 177.451172, -37.926868 ], [ 177.978516, -37.579413 ], [ 178.505859, -37.718590 ], [ 177.978516, -39.164141 ], [ 177.187500, -39.164141 ], [ 176.923828, -39.436193 ], [ 177.011719, -39.909736 ], [ 176.044922, -41.310824 ], [ 175.253906, -41.705729 ], [ 175.078125, -41.442726 ], [ 174.638672, -41.310824 ], [ 175.253906, -40.446947 ], [ 174.902344, -39.909736 ], [ 173.847656, -39.504041 ], [ 173.847656, -39.164141 ], [ 174.550781, -38.822591 ], [ 174.726562, -37.996163 ], [ 174.726562, -37.370157 ], [ 174.287109, -36.738884 ], [ 174.287109, -36.527295 ], [ 173.056641, -35.245619 ], [ 172.617188, -34.524661 ], [ 172.968750, -34.452218 ] ] ], [ [ [ -187.031250, -34.452218 ], [ -186.416016, -35.029996 ], [ -185.712891, -35.245619 ], [ -185.361328, -36.173357 ], [ -184.658203, -37.230328 ], [ -184.658203, -36.527295 ], [ -184.218750, -36.809285 ], [ -184.042969, -37.579413 ], [ -183.251953, -37.857507 ], [ -182.548828, -37.926868 ], [ -182.021484, -37.579413 ], [ -181.494141, -37.718590 ], [ -182.021484, -39.164141 ], [ -182.812500, -39.164141 ], [ -183.076172, -39.436193 ], [ -182.988281, -39.909736 ], [ -183.955078, -41.310824 ], [ -184.746094, -41.705729 ], [ -184.921875, -41.442726 ], [ -185.361328, -41.310824 ], [ -184.746094, -40.446947 ], [ -185.097656, -39.909736 ], [ -186.152344, -39.504041 ], [ -186.152344, -39.164141 ], [ -185.449219, -38.822591 ], [ -185.273438, -37.996163 ], [ -185.273438, -37.370157 ], [ -185.712891, -36.738884 ], [ -185.712891, -36.527295 ], [ -187.031250, -35.101934 ], [ -187.031250, -34.452218 ] ] ], [ [ [ -187.031250, -43.197167 ], [ -186.943359, -43.834527 ], [ -187.031250, -43.834527 ], [ -187.031250, -43.197167 ] ] ] ] } } , -{ "type": "Feature", "properties": { "name": "Antarctica" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -46.669922, -77.823323 ], [ -45.175781, -78.043795 ], [ -43.945312, -78.473002 ], [ -43.505859, -79.088462 ], [ -43.330078, -80.027655 ], [ -44.912109, -80.342262 ], [ -46.494141, -80.589727 ], [ -48.427734, -80.830907 ], [ -50.449219, -81.024916 ], [ -52.822266, -80.969904 ], [ -54.140625, -80.632740 ], [ -53.964844, -80.223588 ], [ -51.855469, -79.951265 ], [ -50.976562, -79.608215 ], [ -49.921875, -78.819036 ], [ -48.691406, -78.043795 ], [ -48.164062, -78.043795 ], [ -46.669922, -77.823323 ] ] ], [ [ [ -60.644531, -79.624056 ], [ -59.589844, -80.042864 ], [ -60.117188, -80.997452 ], [ -62.226562, -80.858875 ], [ -64.511719, -80.928426 ], [ -65.742188, -80.589727 ], [ -66.269531, -80.253391 ], [ -64.072266, -80.297927 ], [ -61.875000, -80.386396 ], [ -60.644531, -79.624056 ] ] ], [ [ [ -57.832031, -63.273182 ], [ -57.216797, -63.509375 ], [ -57.568359, -63.860036 ], [ -58.623047, -64.168107 ], [ -59.062500, -64.358931 ], [ -59.765625, -64.206377 ], [ -60.644531, -64.320872 ], [ -62.050781, -64.811557 ], [ -62.490234, -65.109148 ], [ -62.666016, -65.476508 ], [ -62.578125, -65.874725 ], [ -62.138672, -66.196009 ], [ -62.841797, -66.407955 ], [ -63.720703, -66.513260 ], [ -65.478516, -67.575717 ], [ -65.654297, -67.941650 ], [ -65.302734, -68.366801 ], [ -64.775391, -68.688521 ], [ -63.984375, -68.911005 ], [ -63.193359, -69.224997 ], [ -62.753906, -69.626510 ], [ -62.314453, -70.377854 ], [ -61.787109, -70.728979 ], [ -61.523438, -71.102543 ], [ -61.347656, -72.019729 ], [ -60.732422, -73.175897 ], [ -60.820312, -73.701948 ], [ -61.347656, -74.116047 ], [ -61.962891, -74.449358 ], [ -63.281250, -74.566736 ], [ -64.335938, -75.253057 ], [ -65.830078, -75.628632 ], [ -67.236328, -75.802118 ], [ -69.785156, -76.226907 ], [ -70.576172, -76.639226 ], [ -72.246094, -76.679785 ], [ -74.003906, -76.639226 ], [ -75.585938, -76.720223 ], [ -77.255859, -76.720223 ], [ -76.904297, -77.098423 ], [ -75.410156, -77.273855 ], [ -74.267578, -77.561042 ], [ -73.652344, -77.915669 ], [ -74.794922, -78.224513 ], [ -76.464844, -78.116408 ], [ -77.958984, -78.384855 ], [ -78.046875, -79.187834 ], [ -76.816406, -79.512662 ], [ -76.640625, -79.889737 ], [ -75.322266, -80.253391 ], [ -73.212891, -80.415707 ], [ -71.455078, -80.689789 ], [ -70.048828, -80.997452 ], [ -68.203125, -81.321593 ], [ -65.742188, -81.479293 ], [ -63.281250, -81.748454 ], [ -59.677734, -82.379147 ], [ -58.710938, -82.842440 ], [ -58.183594, -83.215693 ], [ -57.041016, -82.864308 ], [ -53.613281, -82.261699 ], [ -51.503906, -82.009169 ], [ -49.746094, -81.723188 ], [ -47.285156, -81.710526 ], [ -44.824219, -81.848756 ], [ -42.802734, -82.082145 ], [ -42.187500, -81.646927 ], [ -40.781250, -81.361287 ], [ -38.232422, -81.334844 ], [ -34.365234, -80.900669 ], [ -30.058594, -80.589727 ], [ -28.564453, -80.342262 ], [ -29.267578, -79.981891 ], [ -29.707031, -79.639874 ], [ -29.707031, -79.253586 ], [ -31.640625, -79.302640 ], [ -33.662109, -79.448477 ], [ -35.683594, -79.448477 ], [ -35.947266, -79.088462 ], [ -35.771484, -78.331648 ], [ -35.332031, -78.116408 ], [ -32.255859, -77.655346 ], [ -29.794922, -77.059116 ], [ -28.916016, -76.679785 ], [ -25.488281, -76.289542 ], [ -23.906250, -76.247817 ], [ -22.500000, -76.100796 ], [ -20.039062, -75.672197 ], [ -17.490234, -75.118222 ], [ -15.732422, -74.496413 ], [ -15.380859, -74.116047 ], [ -16.435547, -73.873717 ], [ -16.083984, -73.453473 ], [ -15.468750, -73.150440 ], [ -13.271484, -72.711903 ], [ -12.304688, -72.395706 ], [ -11.513672, -72.019729 ], [ -10.986328, -71.552741 ], [ -10.283203, -71.272595 ], [ -9.140625, -71.328950 ], [ -8.613281, -71.663663 ], [ -7.382812, -71.691293 ], [ -7.382812, -71.328950 ], [ -6.855469, -70.931004 ], [ -5.800781, -71.016960 ], [ -5.537109, -71.413177 ], [ -4.306641, -71.469124 ], [ -1.757812, -71.159391 ], [ -0.703125, -71.216075 ], [ -0.263672, -71.635993 ], [ 0.878906, -71.300793 ], [ 1.845703, -71.130988 ], [ 4.130859, -70.844673 ], [ 5.185547, -70.612614 ], [ 6.240234, -70.466207 ], [ 7.119141, -70.259452 ], [ 7.734375, -69.900118 ], [ 8.525391, -70.140364 ], [ 9.492188, -70.020587 ], [ 10.810547, -70.844673 ], [ 11.953125, -70.641769 ], [ 12.392578, -70.259452 ], [ 13.447266, -69.960439 ], [ 14.765625, -70.020587 ], [ 15.117188, -70.407348 ], [ 15.908203, -70.020587 ], [ 17.050781, -69.900118 ], [ 19.248047, -69.900118 ], [ 21.445312, -70.080562 ], [ 21.884766, -70.407348 ], [ 22.587891, -70.699951 ], [ 23.642578, -70.524897 ], [ 27.070312, -70.466207 ], [ 29.179688, -70.199994 ], [ 30.058594, -69.930300 ], [ 30.937500, -69.748551 ], [ 31.992188, -69.657086 ], [ 32.783203, -69.380313 ], [ 33.310547, -68.847665 ], [ 33.837891, -68.496040 ], [ 34.892578, -68.656555 ], [ 35.332031, -69.005675 ], [ 36.123047, -69.256149 ], [ 37.177734, -69.162558 ], [ 37.880859, -69.534518 ], [ 38.671875, -69.778952 ], [ 39.638672, -69.534518 ], [ 39.990234, -69.099940 ], [ 40.957031, -68.942607 ], [ 41.923828, -68.592487 ], [ 44.121094, -68.269387 ], [ 46.494141, -67.609221 ], [ 47.460938, -67.709445 ], [ 48.955078, -67.101656 ], [ 49.921875, -67.101656 ], [ 50.712891, -66.861082 ], [ 50.976562, -66.513260 ], [ 52.646484, -66.053716 ], [ 54.492188, -65.802776 ], [ 56.337891, -65.982270 ], [ 57.128906, -66.231457 ], [ 57.216797, -66.687784 ], [ 58.710938, -67.272043 ], [ 59.941406, -67.407487 ], [ 61.435547, -67.941650 ], [ 62.402344, -68.007571 ], [ 63.193359, -67.809245 ], [ 64.072266, -67.407487 ], [ 64.951172, -67.609221 ], [ 66.884766, -67.842416 ], [ 67.851562, -67.941650 ], [ 68.906250, -67.941650 ], [ 69.697266, -68.974164 ], [ 69.697266, -69.224997 ], [ 69.521484, -69.687618 ], [ 68.554688, -69.930300 ], [ 67.851562, -70.318738 ], [ 67.939453, -70.699951 ], [ 69.082031, -70.670881 ], [ 68.906250, -71.074056 ], [ 67.939453, -71.856229 ], [ 68.730469, -72.154890 ], [ 69.873047, -72.262310 ], [ 71.015625, -72.100944 ], [ 71.894531, -71.328950 ], [ 73.125000, -70.728979 ], [ 73.828125, -69.869892 ], [ 74.531250, -69.778952 ], [ 75.585938, -69.748551 ], [ 77.607422, -69.472969 ], [ 78.398438, -68.688521 ], [ 79.101562, -68.334376 ], [ 80.947266, -67.875541 ], [ 81.474609, -67.542167 ], [ 82.089844, -67.373698 ], [ 82.792969, -67.204032 ], [ 83.759766, -67.305976 ], [ 85.693359, -67.101656 ], [ 86.748047, -67.135829 ], [ 87.451172, -66.861082 ], [ 87.978516, -66.196009 ], [ 88.857422, -66.964476 ], [ 89.648438, -67.135829 ], [ 90.615234, -67.238062 ], [ 91.582031, -67.101656 ], [ 93.515625, -67.204032 ], [ 94.218750, -67.101656 ], [ 95.009766, -67.169955 ], [ 95.800781, -67.373698 ], [ 96.679688, -67.238062 ], [ 97.734375, -67.238062 ], [ 98.701172, -67.101656 ], [ 99.755859, -67.238062 ], [ 102.832031, -65.549367 ], [ 103.447266, -65.694476 ], [ 104.238281, -65.982270 ], [ 106.171875, -66.930060 ], [ 108.105469, -66.964476 ], [ 110.214844, -66.687784 ], [ 111.708984, -66.124962 ], [ 112.851562, -66.089364 ], [ 113.642578, -65.874725 ], [ 114.345703, -66.089364 ], [ 115.576172, -66.687784 ], [ 116.718750, -66.652977 ], [ 117.421875, -66.930060 ], [ 118.564453, -67.169955 ], [ 119.794922, -67.272043 ], [ 120.849609, -67.204032 ], [ 122.343750, -66.548263 ], [ 123.222656, -66.478208 ], [ 125.156250, -66.722541 ], [ 126.123047, -66.548263 ], [ 127.001953, -66.548263 ], [ 128.759766, -66.757250 ], [ 130.781250, -66.407955 ], [ 132.978516, -66.372755 ], [ 134.736328, -66.196009 ], [ 135.000000, -65.730626 ], [ 135.087891, -65.293468 ], [ 135.703125, -65.585720 ], [ 135.878906, -66.018018 ], [ 136.582031, -66.791909 ], [ 137.460938, -66.964476 ], [ 140.800781, -66.826520 ], [ 143.085938, -66.791909 ], [ 145.458984, -66.930060 ], [ 146.162109, -67.238062 ], [ 145.986328, -67.609221 ], [ 146.689453, -67.908619 ], [ 148.798828, -68.399180 ], [ 152.490234, -68.879358 ], [ 153.632812, -68.879358 ], [ 154.248047, -68.560384 ], [ 155.917969, -69.162558 ], [ 156.796875, -69.380313 ], [ 159.169922, -69.595890 ], [ 159.697266, -69.990535 ], [ 160.839844, -70.229744 ], [ 161.542969, -70.583418 ], [ 162.685547, -70.728979 ], [ 166.113281, -70.757966 ], [ 167.343750, -70.844673 ], [ 168.398438, -70.959697 ], [ 170.507812, -71.413177 ], [ 171.210938, -71.691293 ], [ 171.123047, -72.100944 ], [ 170.595703, -72.448792 ], [ 169.277344, -73.652545 ], [ 167.958984, -73.824820 ], [ 167.343750, -74.164085 ], [ 166.113281, -74.378513 ], [ 165.673828, -74.775843 ], [ 164.267578, -75.453071 ], [ 163.564453, -76.247817 ], [ 163.476562, -76.700019 ], [ 163.476562, -77.059116 ], [ 164.091797, -77.466028 ], [ 164.267578, -77.823323 ], [ 164.707031, -78.188586 ], [ 166.640625, -78.313860 ], [ 166.992188, -78.750659 ], [ 165.234375, -78.903929 ], [ 163.652344, -79.121686 ], [ 161.806641, -79.154810 ], [ 160.927734, -79.734281 ], [ 160.751953, -80.193694 ], [ 159.785156, -80.942273 ], [ 161.103516, -81.281717 ], [ 161.630859, -81.685144 ], [ 162.509766, -82.057893 ], [ 163.740234, -82.390794 ], [ 166.640625, -83.026219 ], [ 168.925781, -83.339153 ], [ 169.365234, -83.829945 ], [ 172.265625, -84.043447 ], [ 173.232422, -84.414502 ], [ 175.957031, -84.160849 ], [ 180.000000, -84.714152 ], [ 180.087891, -84.722243 ], [ 180.966797, -84.142939 ], [ 182.724609, -84.457112 ], [ 183.955078, -84.097922 ], [ 184.130859, -84.115970 ], [ 185.625000, -84.532994 ], [ 187.031250, -84.079819 ], [ 187.031250, -85.622069 ], [ 180.000000, -85.622069 ], [ -180.000000, -85.622069 ], [ -187.031250, -85.622069 ], [ -187.031250, -84.310902 ], [ -186.767578, -84.414502 ], [ -184.042969, -84.160849 ], [ -180.000000, -84.714152 ], [ -179.912109, -84.722243 ], [ -179.033203, -84.142939 ], [ -177.275391, -84.457112 ], [ -176.044922, -84.097922 ], [ -175.869141, -84.115970 ], [ -174.375000, -84.532994 ], [ -172.880859, -84.061661 ], [ -169.980469, -83.886366 ], [ -166.992188, -84.566386 ], [ -164.179688, -84.826305 ], [ -162.597656, -85.051129 ], [ -161.894531, -85.141284 ], [ -158.115234, -85.373767 ], [ -155.214844, -85.096413 ], [ -150.908203, -85.295131 ], [ -148.535156, -85.608630 ], [ -145.898438, -85.316708 ], [ -143.173828, -85.051129 ], [ -143.085938, -85.043541 ], [ -142.910156, -84.566386 ], [ -146.865234, -84.532994 ], [ -150.029297, -84.293450 ], [ -150.908203, -83.905058 ], [ -153.544922, -83.686615 ], [ -153.369141, -83.236426 ], [ -152.666016, -82.448764 ], [ -152.841797, -82.045740 ], [ -154.511719, -81.773644 ], [ -155.302734, -81.413933 ], [ -156.796875, -81.106811 ], [ -154.423828, -81.160996 ], [ -152.138672, -80.997452 ], [ -150.644531, -81.334844 ], [ -148.886719, -81.038617 ], [ -147.216797, -80.675559 ], [ -146.425781, -80.342262 ], [ -146.777344, -79.920548 ], [ -149.501953, -79.351472 ], [ -151.611328, -79.302640 ], [ -153.369141, -79.154810 ], [ -155.302734, -79.071812 ], [ -156.005859, -78.699106 ], [ -157.236328, -78.384855 ], [ -158.027344, -78.025574 ], [ -158.378906, -76.880775 ], [ -157.851562, -76.980149 ], [ -156.972656, -77.293202 ], [ -155.302734, -77.196176 ], [ -153.720703, -77.059116 ], [ -152.929688, -77.504119 ], [ -151.347656, -77.389504 ], [ -150.029297, -77.176684 ], [ -148.710938, -76.900709 ], [ -147.656250, -76.578159 ], [ -146.074219, -76.475773 ], [ -146.162109, -76.100796 ], [ -146.513672, -75.737303 ], [ -146.162109, -75.386696 ], [ -144.931641, -75.208245 ], [ -144.316406, -75.541113 ], [ -142.822266, -75.342282 ], [ -141.679688, -75.095633 ], [ -140.185547, -75.073010 ], [ -138.867188, -74.959392 ], [ -135.175781, -74.307353 ], [ -133.769531, -74.449358 ], [ -132.275391, -74.307353 ], [ -130.957031, -74.472903 ], [ -129.550781, -74.449358 ], [ -128.232422, -74.331108 ], [ -125.419922, -74.519889 ], [ -124.013672, -74.472903 ], [ -121.113281, -74.519889 ], [ -119.707031, -74.472903 ], [ -118.652344, -74.188052 ], [ -117.509766, -74.019543 ], [ -116.191406, -74.235878 ], [ -115.048828, -74.067866 ], [ -113.906250, -73.726595 ], [ -113.291016, -74.019543 ], [ -112.939453, -74.378513 ], [ -112.324219, -74.706450 ], [ -111.269531, -74.425777 ], [ -110.039062, -74.798906 ], [ -108.720703, -74.913708 ], [ -107.578125, -75.185789 ], [ -106.171875, -75.118222 ], [ -104.853516, -74.959392 ], [ -103.359375, -74.982183 ], [ -100.634766, -75.297735 ], [ -100.107422, -74.867889 ], [ -101.250000, -74.188052 ], [ -102.568359, -74.116047 ], [ -103.095703, -73.726595 ], [ -103.710938, -72.607120 ], [ -102.919922, -72.764065 ], [ -101.601562, -72.816074 ], [ -100.283203, -72.764065 ], [ -99.140625, -72.919635 ], [ -98.085938, -73.201317 ], [ -97.646484, -73.553302 ], [ -96.328125, -73.627789 ], [ -92.460938, -73.175897 ], [ -91.406250, -73.403338 ], [ -90.087891, -73.327858 ], [ -89.208984, -72.554498 ], [ -88.417969, -72.996909 ], [ -87.275391, -73.175897 ], [ -86.044922, -73.099413 ], [ -85.166016, -73.478485 ], [ -83.847656, -73.528399 ], [ -82.705078, -73.627789 ], [ -81.474609, -73.849286 ], [ -80.683594, -73.478485 ], [ -80.332031, -73.124945 ], [ -79.277344, -73.528399 ], [ -77.958984, -73.428424 ], [ -76.904297, -73.627789 ], [ -76.201172, -73.971078 ], [ -74.882812, -73.873717 ], [ -72.861328, -73.403338 ], [ -68.906250, -72.996909 ], [ -67.939453, -72.790088 ], [ -67.412109, -72.475276 ], [ -67.148438, -72.046840 ], [ -67.236328, -71.635993 ], [ -68.466797, -70.110485 ], [ -68.554688, -69.718107 ], [ -68.466797, -69.318320 ], [ -67.587891, -68.528235 ], [ -67.412109, -68.138852 ], [ -67.763672, -67.339861 ], [ -67.236328, -66.861082 ], [ -66.093750, -66.196009 ], [ -64.599609, -65.585720 ], [ -64.160156, -65.183030 ], [ -63.632812, -64.886265 ], [ -63.017578, -64.623877 ], [ -62.050781, -64.586185 ], [ -61.435547, -64.282760 ], [ -60.732422, -64.091408 ], [ -59.853516, -63.937372 ], [ -59.150391, -63.704722 ], [ -58.623047, -63.391522 ], [ -57.832031, -63.273182 ] ] ], [ [ [ -163.125000, -78.224513 ], [ -161.279297, -78.384855 ], [ -160.224609, -78.699106 ], [ -159.521484, -79.038437 ], [ -159.169922, -79.496652 ], [ -161.103516, -79.639874 ], [ -162.421875, -79.286313 ], [ -163.037109, -78.870048 ], [ -163.740234, -78.595299 ], [ -163.125000, -78.224513 ] ] ], [ [ [ -122.431641, -73.327858 ], [ -119.882812, -73.652545 ], [ -118.740234, -73.478485 ], [ -119.267578, -73.824820 ], [ -120.234375, -74.091974 ], [ -121.640625, -74.019543 ], [ -122.607422, -73.652545 ], [ -122.431641, -73.327858 ] ] ], [ [ [ -126.562500, -73.252045 ], [ -124.013672, -73.873717 ], [ -125.947266, -73.726595 ], [ -127.265625, -73.453473 ], [ -126.562500, -73.252045 ] ] ], [ [ [ -101.689453, -71.718882 ], [ -100.458984, -71.856229 ], [ -98.964844, -71.938158 ], [ -97.910156, -72.073911 ], [ -96.767578, -71.965388 ], [ -96.240234, -72.528130 ], [ -96.943359, -72.448792 ], [ -98.173828, -72.475276 ], [ -99.404297, -72.448792 ], [ -100.810547, -72.501722 ], [ -101.777344, -72.315785 ], [ -102.304688, -71.883578 ], [ -101.689453, -71.718882 ] ] ], [ [ [ -70.224609, -68.879358 ], [ -69.697266, -69.256149 ], [ -68.466797, -70.959697 ], [ -68.291016, -71.413177 ], [ -68.818359, -72.181804 ], [ -69.960938, -72.315785 ], [ -71.103516, -72.501722 ], [ -72.421875, -72.475276 ], [ -71.894531, -72.100944 ], [ -74.179688, -72.369105 ], [ -74.970703, -72.073911 ], [ -74.970703, -71.663663 ], [ -73.916016, -71.272595 ], [ -73.212891, -71.159391 ], [ -72.070312, -71.187754 ], [ -71.806641, -70.670881 ], [ -71.718750, -69.503765 ], [ -71.191406, -69.037142 ], [ -70.224609, -68.879358 ] ] ] ] } } +{ "type": "Feature", "properties": { "name": "Antarctica" }, "geometry": { "type": "MultiPolygon", "coordinates": [ [ [ [ -46.669922, -77.823323 ], [ -45.175781, -78.043795 ], [ -43.945312, -78.473002 ], [ -43.505859, -79.088462 ], [ -43.330078, -80.027655 ], [ -44.912109, -80.342262 ], [ -46.494141, -80.589727 ], [ -48.427734, -80.830907 ], [ -50.449219, -81.024916 ], [ -52.822266, -80.969904 ], [ -54.140625, -80.632740 ], [ -53.964844, -80.223588 ], [ -51.855469, -79.951265 ], [ -50.976562, -79.608215 ], [ -49.921875, -78.819036 ], [ -48.691406, -78.043795 ], [ -48.164062, -78.043795 ], [ -46.669922, -77.823323 ] ] ], [ [ [ -60.644531, -79.624056 ], [ -59.589844, -80.042864 ], [ -60.117188, -80.997452 ], [ -62.226562, -80.858875 ], [ -64.511719, -80.928426 ], [ -65.742188, -80.589727 ], [ -66.269531, -80.253391 ], [ -64.072266, -80.297927 ], [ -61.875000, -80.386396 ], [ -60.644531, -79.624056 ] ] ], [ [ [ -57.832031, -63.273182 ], [ -57.216797, -63.509375 ], [ -57.568359, -63.860036 ], [ -58.623047, -64.168107 ], [ -59.062500, -64.358931 ], [ -59.765625, -64.206377 ], [ -60.644531, -64.320872 ], [ -62.050781, -64.811557 ], [ -62.490234, -65.109148 ], [ -62.666016, -65.476508 ], [ -62.578125, -65.874725 ], [ -62.138672, -66.196009 ], [ -62.841797, -66.407955 ], [ -63.720703, -66.513260 ], [ -65.478516, -67.575717 ], [ -65.654297, -67.941650 ], [ -65.302734, -68.366801 ], [ -64.775391, -68.688521 ], [ -63.984375, -68.911005 ], [ -63.193359, -69.224997 ], [ -62.753906, -69.626510 ], [ -62.314453, -70.377854 ], [ -61.787109, -70.728979 ], [ -61.523438, -71.102543 ], [ -61.347656, -72.019729 ], [ -60.732422, -73.175897 ], [ -60.820312, -73.701948 ], [ -61.347656, -74.116047 ], [ -61.962891, -74.449358 ], [ -63.281250, -74.566736 ], [ -64.335938, -75.253057 ], [ -65.830078, -75.628632 ], [ -67.236328, -75.802118 ], [ -69.785156, -76.226907 ], [ -70.576172, -76.639226 ], [ -72.246094, -76.679785 ], [ -74.003906, -76.639226 ], [ -75.585938, -76.720223 ], [ -77.255859, -76.720223 ], [ -76.904297, -77.098423 ], [ -75.410156, -77.273855 ], [ -74.267578, -77.561042 ], [ -73.652344, -77.915669 ], [ -74.794922, -78.224513 ], [ -76.464844, -78.116408 ], [ -77.958984, -78.384855 ], [ -78.046875, -79.187834 ], [ -76.816406, -79.512662 ], [ -76.640625, -79.889737 ], [ -75.322266, -80.253391 ], [ -73.212891, -80.415707 ], [ -71.455078, -80.689789 ], [ -70.048828, -80.997452 ], [ -68.203125, -81.321593 ], [ -65.742188, -81.479293 ], [ -63.281250, -81.748454 ], [ -59.677734, -82.379147 ], [ -58.710938, -82.842440 ], [ -58.183594, -83.215693 ], [ -57.041016, -82.864308 ], [ -53.613281, -82.261699 ], [ -51.503906, -82.009169 ], [ -49.746094, -81.723188 ], [ -47.285156, -81.710526 ], [ -44.824219, -81.848756 ], [ -42.802734, -82.082145 ], [ -42.187500, -81.646927 ], [ -40.781250, -81.361287 ], [ -38.232422, -81.334844 ], [ -34.365234, -80.900669 ], [ -30.058594, -80.589727 ], [ -28.564453, -80.342262 ], [ -29.267578, -79.981891 ], [ -29.707031, -79.639874 ], [ -29.707031, -79.253586 ], [ -31.640625, -79.302640 ], [ -33.662109, -79.448477 ], [ -35.683594, -79.448477 ], [ -35.947266, -79.088462 ], [ -35.771484, -78.331648 ], [ -35.332031, -78.116408 ], [ -32.255859, -77.655346 ], [ -29.794922, -77.059116 ], [ -28.916016, -76.679785 ], [ -25.488281, -76.289542 ], [ -23.906250, -76.247817 ], [ -22.500000, -76.100796 ], [ -20.039062, -75.672197 ], [ -17.490234, -75.118222 ], [ -15.732422, -74.496413 ], [ -15.380859, -74.116047 ], [ -16.435547, -73.873717 ], [ -16.083984, -73.453473 ], [ -15.468750, -73.150440 ], [ -13.271484, -72.711903 ], [ -12.304688, -72.395706 ], [ -11.513672, -72.019729 ], [ -10.986328, -71.552741 ], [ -10.283203, -71.272595 ], [ -9.140625, -71.328950 ], [ -8.613281, -71.663663 ], [ -7.382812, -71.691293 ], [ -7.382812, -71.328950 ], [ -6.855469, -70.931004 ], [ -5.800781, -71.016960 ], [ -5.537109, -71.413177 ], [ -4.306641, -71.469124 ], [ -1.757812, -71.159391 ], [ -0.703125, -71.216075 ], [ -0.263672, -71.635993 ], [ 0.878906, -71.300793 ], [ 1.845703, -71.130988 ], [ 4.130859, -70.844673 ], [ 5.185547, -70.612614 ], [ 6.240234, -70.466207 ], [ 7.119141, -70.259452 ], [ 7.734375, -69.900118 ], [ 8.525391, -70.140364 ], [ 9.492188, -70.020587 ], [ 10.810547, -70.844673 ], [ 11.953125, -70.641769 ], [ 12.392578, -70.259452 ], [ 13.447266, -69.960439 ], [ 14.765625, -70.020587 ], [ 15.117188, -70.407348 ], [ 15.908203, -70.020587 ], [ 17.050781, -69.900118 ], [ 18.193359, -69.869892 ], [ 19.248047, -69.900118 ], [ 21.445312, -70.080562 ], [ 21.884766, -70.407348 ], [ 22.587891, -70.699951 ], [ 23.642578, -70.524897 ], [ 27.070312, -70.466207 ], [ 29.179688, -70.199994 ], [ 30.058594, -69.930300 ], [ 30.937500, -69.748551 ], [ 31.992188, -69.657086 ], [ 32.783203, -69.380313 ], [ 33.310547, -68.847665 ], [ 33.837891, -68.496040 ], [ 34.892578, -68.656555 ], [ 35.332031, -69.005675 ], [ 36.123047, -69.256149 ], [ 37.177734, -69.162558 ], [ 37.880859, -69.534518 ], [ 38.671875, -69.778952 ], [ 39.638672, -69.534518 ], [ 39.990234, -69.099940 ], [ 40.957031, -68.942607 ], [ 41.923828, -68.592487 ], [ 44.121094, -68.269387 ], [ 46.494141, -67.609221 ], [ 47.460938, -67.709445 ], [ 48.955078, -67.101656 ], [ 49.921875, -67.101656 ], [ 50.712891, -66.861082 ], [ 50.976562, -66.513260 ], [ 52.646484, -66.053716 ], [ 54.492188, -65.802776 ], [ 56.337891, -65.982270 ], [ 57.128906, -66.231457 ], [ 57.216797, -66.687784 ], [ 58.710938, -67.272043 ], [ 59.941406, -67.407487 ], [ 61.435547, -67.941650 ], [ 62.402344, -68.007571 ], [ 63.193359, -67.809245 ], [ 64.072266, -67.407487 ], [ 64.951172, -67.609221 ], [ 66.884766, -67.842416 ], [ 67.851562, -67.941650 ], [ 68.906250, -67.941650 ], [ 69.697266, -68.974164 ], [ 69.697266, -69.224997 ], [ 69.521484, -69.687618 ], [ 68.554688, -69.930300 ], [ 67.851562, -70.318738 ], [ 67.939453, -70.699951 ], [ 69.082031, -70.670881 ], [ 68.906250, -71.074056 ], [ 67.939453, -71.856229 ], [ 68.730469, -72.154890 ], [ 69.873047, -72.262310 ], [ 71.015625, -72.100944 ], [ 71.894531, -71.328950 ], [ 73.125000, -70.728979 ], [ 73.828125, -69.869892 ], [ 74.531250, -69.778952 ], [ 75.585938, -69.748551 ], [ 77.607422, -69.472969 ], [ 78.398438, -68.688521 ], [ 79.101562, -68.334376 ], [ 80.947266, -67.875541 ], [ 81.474609, -67.542167 ], [ 82.089844, -67.373698 ], [ 82.792969, -67.204032 ], [ 83.759766, -67.305976 ], [ 85.693359, -67.101656 ], [ 86.748047, -67.135829 ], [ 87.451172, -66.861082 ], [ 87.978516, -66.196009 ], [ 88.857422, -66.964476 ], [ 89.648438, -67.135829 ], [ 90.615234, -67.238062 ], [ 91.582031, -67.101656 ], [ 93.515625, -67.204032 ], [ 94.218750, -67.101656 ], [ 95.009766, -67.169955 ], [ 95.800781, -67.373698 ], [ 96.679688, -67.238062 ], [ 97.734375, -67.238062 ], [ 98.701172, -67.101656 ], [ 99.755859, -67.238062 ], [ 102.832031, -65.549367 ], [ 103.447266, -65.694476 ], [ 104.238281, -65.982270 ], [ 106.171875, -66.930060 ], [ 108.105469, -66.964476 ], [ 110.214844, -66.687784 ], [ 111.708984, -66.124962 ], [ 112.851562, -66.089364 ], [ 113.642578, -65.874725 ], [ 114.345703, -66.089364 ], [ 115.576172, -66.687784 ], [ 116.718750, -66.652977 ], [ 117.421875, -66.930060 ], [ 118.564453, -67.169955 ], [ 119.794922, -67.272043 ], [ 120.849609, -67.204032 ], [ 122.343750, -66.548263 ], [ 123.222656, -66.478208 ], [ 125.156250, -66.722541 ], [ 126.123047, -66.548263 ], [ 127.001953, -66.548263 ], [ 128.759766, -66.757250 ], [ 130.781250, -66.407955 ], [ 132.978516, -66.372755 ], [ 134.736328, -66.196009 ], [ 135.000000, -65.730626 ], [ 135.087891, -65.293468 ], [ 135.703125, -65.585720 ], [ 135.878906, -66.018018 ], [ 136.582031, -66.791909 ], [ 137.460938, -66.964476 ], [ 140.800781, -66.826520 ], [ 143.085938, -66.791909 ], [ 145.458984, -66.930060 ], [ 146.162109, -67.238062 ], [ 145.986328, -67.609221 ], [ 146.689453, -67.908619 ], [ 148.798828, -68.399180 ], [ 152.490234, -68.879358 ], [ 153.632812, -68.879358 ], [ 154.248047, -68.560384 ], [ 155.917969, -69.162558 ], [ 156.796875, -69.380313 ], [ 159.169922, -69.595890 ], [ 159.697266, -69.990535 ], [ 160.839844, -70.229744 ], [ 161.542969, -70.583418 ], [ 162.685547, -70.728979 ], [ 166.113281, -70.757966 ], [ 167.343750, -70.844673 ], [ 168.398438, -70.959697 ], [ 170.507812, -71.413177 ], [ 171.210938, -71.691293 ], [ 171.123047, -72.100944 ], [ 170.595703, -72.448792 ], [ 169.277344, -73.652545 ], [ 167.958984, -73.824820 ], [ 167.343750, -74.164085 ], [ 166.113281, -74.378513 ], [ 165.673828, -74.775843 ], [ 164.267578, -75.453071 ], [ 163.564453, -76.247817 ], [ 163.476562, -76.700019 ], [ 163.476562, -77.059116 ], [ 164.091797, -77.466028 ], [ 164.267578, -77.823323 ], [ 164.707031, -78.188586 ], [ 166.640625, -78.313860 ], [ 166.992188, -78.750659 ], [ 165.234375, -78.903929 ], [ 163.652344, -79.121686 ], [ 161.806641, -79.154810 ], [ 160.927734, -79.734281 ], [ 160.751953, -80.193694 ], [ 159.785156, -80.942273 ], [ 161.103516, -81.281717 ], [ 161.630859, -81.685144 ], [ 162.509766, -82.057893 ], [ 163.740234, -82.390794 ], [ 166.640625, -83.026219 ], [ 168.925781, -83.339153 ], [ 169.365234, -83.829945 ], [ 172.265625, -84.043447 ], [ 173.232422, -84.414502 ], [ 175.957031, -84.160849 ], [ 180.000000, -84.714152 ], [ 180.087891, -84.722243 ], [ 180.966797, -84.142939 ], [ 182.724609, -84.457112 ], [ 183.955078, -84.097922 ], [ 184.130859, -84.115970 ], [ 185.625000, -84.532994 ], [ 187.031250, -84.079819 ], [ 187.031250, -85.622069 ], [ 180.000000, -85.622069 ], [ -180.000000, -85.622069 ], [ -187.031250, -85.622069 ], [ -187.031250, -84.310902 ], [ -186.767578, -84.414502 ], [ -184.042969, -84.160849 ], [ -180.000000, -84.714152 ], [ -179.912109, -84.722243 ], [ -179.033203, -84.142939 ], [ -177.275391, -84.457112 ], [ -176.044922, -84.097922 ], [ -175.869141, -84.115970 ], [ -174.375000, -84.532994 ], [ -172.880859, -84.061661 ], [ -169.980469, -83.886366 ], [ -166.992188, -84.566386 ], [ -164.179688, -84.826305 ], [ -162.597656, -85.051129 ], [ -161.894531, -85.141284 ], [ -158.115234, -85.373767 ], [ -155.214844, -85.096413 ], [ -150.908203, -85.295131 ], [ -148.535156, -85.608630 ], [ -145.898438, -85.316708 ], [ -143.173828, -85.051129 ], [ -143.085938, -85.043541 ], [ -142.910156, -84.566386 ], [ -146.865234, -84.532994 ], [ -150.029297, -84.293450 ], [ -150.908203, -83.905058 ], [ -153.544922, -83.686615 ], [ -153.369141, -83.236426 ], [ -152.666016, -82.448764 ], [ -152.841797, -82.045740 ], [ -154.511719, -81.773644 ], [ -155.302734, -81.413933 ], [ -156.796875, -81.106811 ], [ -154.423828, -81.160996 ], [ -152.138672, -80.997452 ], [ -150.644531, -81.334844 ], [ -148.886719, -81.038617 ], [ -147.216797, -80.675559 ], [ -146.425781, -80.342262 ], [ -146.777344, -79.920548 ], [ -149.501953, -79.351472 ], [ -151.611328, -79.302640 ], [ -153.369141, -79.154810 ], [ -155.302734, -79.071812 ], [ -156.005859, -78.699106 ], [ -157.236328, -78.384855 ], [ -158.027344, -78.025574 ], [ -158.378906, -76.880775 ], [ -157.851562, -76.980149 ], [ -156.972656, -77.293202 ], [ -155.302734, -77.196176 ], [ -153.720703, -77.059116 ], [ -152.929688, -77.504119 ], [ -151.347656, -77.389504 ], [ -150.029297, -77.176684 ], [ -148.710938, -76.900709 ], [ -147.656250, -76.578159 ], [ -146.074219, -76.475773 ], [ -146.162109, -76.100796 ], [ -146.513672, -75.737303 ], [ -146.162109, -75.386696 ], [ -144.931641, -75.208245 ], [ -144.316406, -75.541113 ], [ -142.822266, -75.342282 ], [ -141.679688, -75.095633 ], [ -140.185547, -75.073010 ], [ -138.867188, -74.959392 ], [ -135.175781, -74.307353 ], [ -133.769531, -74.449358 ], [ -132.275391, -74.307353 ], [ -130.957031, -74.472903 ], [ -129.550781, -74.449358 ], [ -128.232422, -74.331108 ], [ -125.419922, -74.519889 ], [ -124.013672, -74.472903 ], [ -121.113281, -74.519889 ], [ -119.707031, -74.472903 ], [ -118.652344, -74.188052 ], [ -117.509766, -74.019543 ], [ -116.191406, -74.235878 ], [ -115.048828, -74.067866 ], [ -113.906250, -73.726595 ], [ -113.291016, -74.019543 ], [ -112.939453, -74.378513 ], [ -112.324219, -74.706450 ], [ -111.269531, -74.425777 ], [ -110.039062, -74.798906 ], [ -108.720703, -74.913708 ], [ -107.578125, -75.185789 ], [ -106.171875, -75.118222 ], [ -104.853516, -74.959392 ], [ -103.359375, -74.982183 ], [ -100.634766, -75.297735 ], [ -100.107422, -74.867889 ], [ -101.250000, -74.188052 ], [ -102.568359, -74.116047 ], [ -103.095703, -73.726595 ], [ -103.710938, -72.607120 ], [ -102.919922, -72.764065 ], [ -101.601562, -72.816074 ], [ -100.283203, -72.764065 ], [ -99.140625, -72.919635 ], [ -98.085938, -73.201317 ], [ -97.646484, -73.553302 ], [ -96.328125, -73.627789 ], [ -92.460938, -73.175897 ], [ -91.406250, -73.403338 ], [ -90.087891, -73.327858 ], [ -89.208984, -72.554498 ], [ -88.417969, -72.996909 ], [ -87.275391, -73.175897 ], [ -86.044922, -73.099413 ], [ -85.166016, -73.478485 ], [ -83.847656, -73.528399 ], [ -82.705078, -73.627789 ], [ -81.474609, -73.849286 ], [ -80.683594, -73.478485 ], [ -80.332031, -73.124945 ], [ -79.277344, -73.528399 ], [ -77.958984, -73.428424 ], [ -76.904297, -73.627789 ], [ -76.201172, -73.971078 ], [ -74.882812, -73.873717 ], [ -72.861328, -73.403338 ], [ -68.906250, -72.996909 ], [ -67.939453, -72.790088 ], [ -67.412109, -72.475276 ], [ -67.148438, -72.046840 ], [ -67.236328, -71.635993 ], [ -68.466797, -70.110485 ], [ -68.554688, -69.718107 ], [ -68.466797, -69.318320 ], [ -67.587891, -68.528235 ], [ -67.412109, -68.138852 ], [ -67.763672, -67.339861 ], [ -67.236328, -66.861082 ], [ -66.093750, -66.196009 ], [ -64.599609, -65.585720 ], [ -64.160156, -65.183030 ], [ -63.632812, -64.886265 ], [ -63.017578, -64.623877 ], [ -62.050781, -64.586185 ], [ -61.435547, -64.282760 ], [ -60.732422, -64.091408 ], [ -59.853516, -63.937372 ], [ -59.150391, -63.704722 ], [ -58.623047, -63.391522 ], [ -57.832031, -63.273182 ] ] ], [ [ [ -163.125000, -78.224513 ], [ -161.279297, -78.384855 ], [ -160.224609, -78.699106 ], [ -159.521484, -79.038437 ], [ -159.169922, -79.496652 ], [ -161.103516, -79.639874 ], [ -162.421875, -79.286313 ], [ -163.037109, -78.870048 ], [ -163.740234, -78.595299 ], [ -163.125000, -78.224513 ] ] ], [ [ [ -122.431641, -73.327858 ], [ -119.882812, -73.652545 ], [ -118.740234, -73.478485 ], [ -119.267578, -73.824820 ], [ -120.234375, -74.091974 ], [ -121.640625, -74.019543 ], [ -122.607422, -73.652545 ], [ -122.431641, -73.327858 ] ] ], [ [ [ -126.562500, -73.252045 ], [ -124.013672, -73.873717 ], [ -125.947266, -73.726595 ], [ -127.265625, -73.453473 ], [ -126.562500, -73.252045 ] ] ], [ [ [ -101.689453, -71.718882 ], [ -100.458984, -71.856229 ], [ -98.964844, -71.938158 ], [ -97.910156, -72.073911 ], [ -96.767578, -71.965388 ], [ -96.240234, -72.528130 ], [ -96.943359, -72.448792 ], [ -98.173828, -72.475276 ], [ -99.404297, -72.448792 ], [ -100.810547, -72.501722 ], [ -101.777344, -72.315785 ], [ -102.304688, -71.883578 ], [ -101.689453, -71.718882 ] ] ], [ [ [ -70.224609, -68.879358 ], [ -69.697266, -69.256149 ], [ -68.466797, -70.959697 ], [ -68.291016, -71.413177 ], [ -68.818359, -72.181804 ], [ -69.960938, -72.315785 ], [ -71.103516, -72.501722 ], [ -72.421875, -72.475276 ], [ -71.894531, -72.100944 ], [ -74.179688, -72.369105 ], [ -74.970703, -72.073911 ], [ -74.970703, -71.663663 ], [ -73.916016, -71.272595 ], [ -73.212891, -71.159391 ], [ -72.070312, -71.187754 ], [ -71.806641, -70.670881 ], [ -71.718750, -69.503765 ], [ -71.191406, -69.037142 ], [ -70.224609, -68.879358 ] ] ] ] } } ] } ] } , diff --git a/tests/tl_2018_51685_roads/out/-Z11_-z11_--no-simplification-of-shared-nodes.json b/tests/tl_2018_51685_roads/out/-Z11_-z11_--no-simplification-of-shared-nodes.json index c3dd7fcb2..628565928 100644 --- a/tests/tl_2018_51685_roads/out/-Z11_-z11_--no-simplification-of-shared-nodes.json +++ b/tests/tl_2018_51685_roads/out/-Z11_-z11_--no-simplification-of-shared-nodes.json @@ -130,21 +130,21 @@ , { "type": "Feature", "properties": { "LINEARID": "1103679538189", "FULLNAME": "Old Centreville Rd", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.458506, 38.787409 ], [ -77.458720, 38.785602 ], [ -77.458935, 38.783762 ], [ -77.459021, 38.783227 ], [ -77.459064, 38.782290 ], [ -77.459106, 38.781856 ], [ -77.459493, 38.778410 ], [ -77.459450, 38.778209 ], [ -77.454772, 38.775532 ], [ -77.453613, 38.774897 ], [ -77.452970, 38.774462 ], [ -77.452669, 38.774094 ], [ -77.452497, 38.773525 ], [ -77.452283, 38.773458 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "110410477217", "FULLNAME": "State Rte 28", "RTTYP": "S", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452111, 38.773425 ], [ -77.452283, 38.772622 ], [ -77.452326, 38.772387 ], [ -77.452455, 38.771886 ], [ -77.452497, 38.771685 ], [ -77.452583, 38.771317 ], [ -77.452712, 38.770681 ], [ -77.452798, 38.770514 ], [ -77.452884, 38.770246 ], [ -77.452970, 38.770012 ], [ -77.453098, 38.769811 ], [ -77.453270, 38.769510 ], [ -77.453356, 38.769343 ], [ -77.453570, 38.769042 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "110410477217", "FULLNAME": "State Rte 28", "RTTYP": "S", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452111, 38.773425 ], [ -77.452497, 38.771685 ], [ -77.452884, 38.770246 ], [ -77.453270, 38.769510 ], [ -77.453570, 38.769042 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "110410477454", "FULLNAME": "Centreville Rd", "RTTYP": "M", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452111, 38.773425 ], [ -77.452283, 38.772622 ], [ -77.452326, 38.772387 ], [ -77.452455, 38.771886 ], [ -77.452497, 38.771685 ], [ -77.452583, 38.771317 ], [ -77.452712, 38.770681 ], [ -77.452798, 38.770514 ], [ -77.452884, 38.770246 ], [ -77.452970, 38.770012 ], [ -77.453098, 38.769811 ], [ -77.453270, 38.769510 ], [ -77.453356, 38.769343 ], [ -77.453570, 38.769042 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "110410477454", "FULLNAME": "Centreville Rd", "RTTYP": "M", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452111, 38.773425 ], [ -77.452497, 38.771685 ], [ -77.452884, 38.770246 ], [ -77.453270, 38.769510 ], [ -77.453570, 38.769042 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1106073064085", "FULLNAME": "Blooms Quarry Ln", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452111, 38.773425 ], [ -77.451854, 38.773391 ], [ -77.451768, 38.773391 ], [ -77.451725, 38.773391 ], [ -77.451639, 38.773391 ], [ -77.451339, 38.773458 ], [ -77.451210, 38.773492 ], [ -77.450867, 38.773625 ], [ -77.450824, 38.773659 ], [ -77.450695, 38.773659 ], [ -77.450566, 38.773692 ], [ -77.450266, 38.773692 ], [ -77.449965, 38.773659 ], [ -77.449880, 38.773625 ], [ -77.449322, 38.773358 ], [ -77.448163, 38.772856 ], [ -77.447605, 38.772588 ], [ -77.447305, 38.772387 ], [ -77.447047, 38.772187 ], [ -77.446790, 38.771986 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1106073064085", "FULLNAME": "Blooms Quarry Ln", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452111, 38.773425 ], [ -77.451339, 38.773458 ], [ -77.450824, 38.773659 ], [ -77.450266, 38.773692 ], [ -77.449965, 38.773659 ], [ -77.449322, 38.773358 ], [ -77.447605, 38.772588 ], [ -77.446790, 38.771986 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1102214409896", "FULLNAME": "Centreville Rd", "RTTYP": "M", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452283, 38.773458 ], [ -77.452326, 38.773224 ], [ -77.452497, 38.772421 ], [ -77.452669, 38.771718 ], [ -77.452841, 38.770882 ], [ -77.452927, 38.770581 ], [ -77.453012, 38.770380 ], [ -77.453098, 38.770146 ], [ -77.453227, 38.769845 ], [ -77.453399, 38.769543 ], [ -77.453485, 38.769376 ], [ -77.453699, 38.769075 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1102214409896", "FULLNAME": "Centreville Rd", "RTTYP": "M", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452283, 38.773458 ], [ -77.452669, 38.771718 ], [ -77.452927, 38.770581 ], [ -77.453227, 38.769845 ], [ -77.453699, 38.769075 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1102214411210", "FULLNAME": "State Rte 28", "RTTYP": "S", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452283, 38.773458 ], [ -77.452326, 38.773224 ], [ -77.452497, 38.772421 ], [ -77.452669, 38.771718 ], [ -77.452841, 38.770882 ], [ -77.452927, 38.770581 ], [ -77.453012, 38.770380 ], [ -77.453098, 38.770146 ], [ -77.453227, 38.769845 ], [ -77.453399, 38.769543 ], [ -77.453485, 38.769376 ], [ -77.453699, 38.769075 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1102214411210", "FULLNAME": "State Rte 28", "RTTYP": "S", "MTFCC": "S1200" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452283, 38.773458 ], [ -77.452669, 38.771718 ], [ -77.452927, 38.770581 ], [ -77.453227, 38.769845 ], [ -77.453699, 38.769075 ] ] } } , { "type": "Feature", "properties": { "LINEARID": "110410477418", "FULLNAME": "Manassas Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.443743, 38.768640 ], [ -77.443914, 38.768841 ], [ -77.445374, 38.769711 ], [ -77.445545, 38.769778 ], [ -77.446747, 38.770581 ], [ -77.447648, 38.770982 ], [ -77.448807, 38.771250 ], [ -77.449880, 38.771417 ], [ -77.452283, 38.771752 ], [ -77.452497, 38.771685 ] ] } } , { "type": "Feature", "properties": { "LINEARID": "110410477419", "FULLNAME": "Manassas Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452497, 38.771685 ], [ -77.452326, 38.771584 ], [ -77.452025, 38.771518 ], [ -77.449880, 38.771250 ], [ -77.448764, 38.771083 ], [ -77.447777, 38.770815 ], [ -77.446790, 38.770380 ], [ -77.445674, 38.769644 ], [ -77.445545, 38.769543 ], [ -77.444601, 38.769042 ], [ -77.443743, 38.768640 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1106092774118", "FULLNAME": "Manassas Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452283, 38.773458 ], [ -77.452111, 38.773425 ], [ -77.451854, 38.773391 ], [ -77.451768, 38.773391 ], [ -77.451725, 38.773391 ], [ -77.451639, 38.773391 ], [ -77.451339, 38.773458 ], [ -77.451210, 38.773492 ], [ -77.450867, 38.773625 ], [ -77.450824, 38.773659 ], [ -77.450695, 38.773659 ], [ -77.450566, 38.773692 ], [ -77.450266, 38.773692 ], [ -77.449965, 38.773659 ], [ -77.449880, 38.773625 ], [ -77.449322, 38.773358 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1106092774118", "FULLNAME": "Manassas Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.452283, 38.773458 ], [ -77.452111, 38.773425 ], [ -77.451339, 38.773458 ], [ -77.450824, 38.773659 ], [ -77.450266, 38.773692 ], [ -77.449965, 38.773659 ], [ -77.449322, 38.773358 ] ] } } , { "type": "Feature", "properties": { "LINEARID": "110410477551", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.450566, 38.772287 ], [ -77.450824, 38.772387 ], [ -77.450910, 38.772254 ], [ -77.450738, 38.772153 ], [ -77.450566, 38.772287 ] ] } } , @@ -154,7 +154,7 @@ , { "type": "Feature", "properties": { "LINEARID": "110410477440", "FULLNAME": "Sunnyside Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.449665, 38.772053 ], [ -77.449493, 38.771952 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1106087402596", "FULLNAME": "Blooms Quarry Rd", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.449322, 38.773358 ], [ -77.448163, 38.772856 ], [ -77.447605, 38.772588 ], [ -77.447305, 38.772387 ], [ -77.447047, 38.772187 ], [ -77.446790, 38.771986 ], [ -77.445030, 38.770614 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1106087402596", "FULLNAME": "Blooms Quarry Rd", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.449322, 38.773358 ], [ -77.447605, 38.772588 ], [ -77.446790, 38.771986 ], [ -77.445030, 38.770614 ] ] } } , { "type": "Feature", "properties": { "LINEARID": "110410477452", "FULLNAME": "White Pine Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.447348, 38.771417 ], [ -77.446961, 38.771250 ], [ -77.446876, 38.771350 ], [ -77.447176, 38.771584 ], [ -77.448249, 38.772321 ], [ -77.449064, 38.772689 ], [ -77.450180, 38.773157 ] ] } } , @@ -336,13 +336,13 @@ , { "type": "Feature", "properties": { "LINEARID": "110410477422", "FULLNAME": "Primrose Ln", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.434087, 38.763989 ], [ -77.433829, 38.764190 ], [ -77.433658, 38.764457 ], [ -77.433443, 38.764491 ], [ -77.433186, 38.764625 ], [ -77.433100, 38.764758 ], [ -77.433143, 38.765026 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1103678606081", "FULLNAME": "Silver Meteor Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.432799, 38.762650 ], [ -77.432885, 38.762851 ], [ -77.432971, 38.763119 ], [ -77.432971, 38.763186 ], [ -77.432971, 38.763253 ], [ -77.432842, 38.763420 ], [ -77.432628, 38.763822 ], [ -77.432585, 38.763855 ], [ -77.432499, 38.763888 ], [ -77.432284, 38.763955 ], [ -77.432241, 38.763955 ], [ -77.432156, 38.763955 ], [ -77.431855, 38.763822 ], [ -77.431812, 38.763822 ], [ -77.431812, 38.763788 ], [ -77.431812, 38.763721 ], [ -77.431941, 38.763353 ], [ -77.432241, 38.762952 ], [ -77.432284, 38.762918 ], [ -77.432456, 38.762784 ], [ -77.432542, 38.762717 ], [ -77.432628, 38.762684 ], [ -77.432714, 38.762684 ], [ -77.432799, 38.762650 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1103678606081", "FULLNAME": "Silver Meteor Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.432799, 38.762650 ], [ -77.432971, 38.763253 ], [ -77.432585, 38.763855 ], [ -77.432156, 38.763955 ], [ -77.431812, 38.763788 ], [ -77.431941, 38.763353 ], [ -77.432284, 38.762918 ], [ -77.432542, 38.762717 ], [ -77.432799, 38.762650 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1103679538423", "FULLNAME": "Black Hawk Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.432799, 38.762650 ], [ -77.432885, 38.762851 ], [ -77.432971, 38.763119 ], [ -77.432971, 38.763186 ], [ -77.432971, 38.763253 ], [ -77.432842, 38.763420 ], [ -77.432628, 38.763822 ], [ -77.432585, 38.763855 ], [ -77.432499, 38.763888 ], [ -77.432284, 38.763955 ], [ -77.432241, 38.763955 ], [ -77.432156, 38.763955 ], [ -77.431855, 38.763822 ], [ -77.431812, 38.763822 ], [ -77.431812, 38.763788 ], [ -77.431812, 38.763721 ], [ -77.431941, 38.763353 ], [ -77.432241, 38.762952 ], [ -77.432284, 38.762918 ], [ -77.432456, 38.762784 ], [ -77.432542, 38.762717 ], [ -77.432628, 38.762684 ], [ -77.432714, 38.762684 ], [ -77.432799, 38.762650 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1103679538423", "FULLNAME": "Black Hawk Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.432799, 38.762650 ], [ -77.432971, 38.763253 ], [ -77.432585, 38.763855 ], [ -77.432156, 38.763955 ], [ -77.431812, 38.763788 ], [ -77.431941, 38.763353 ], [ -77.432284, 38.762918 ], [ -77.432542, 38.762717 ], [ -77.432799, 38.762650 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1103678606039", "FULLNAME": "Silver Meteor Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.433400, 38.762583 ], [ -77.433100, 38.762650 ], [ -77.432928, 38.762684 ], [ -77.432799, 38.762650 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1103678606039", "FULLNAME": "Silver Meteor Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.433400, 38.762583 ], [ -77.432799, 38.762650 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1103679538422", "FULLNAME": "Black Hawk Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.433400, 38.762583 ], [ -77.433100, 38.762650 ], [ -77.432928, 38.762684 ], [ -77.432799, 38.762650 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1103679538422", "FULLNAME": "Black Hawk Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.433400, 38.762583 ], [ -77.432799, 38.762650 ] ] } } , { "type": "Feature", "properties": { "LINEARID": "110410477423", "FULLNAME": "Railroad Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.442241, 38.762617 ], [ -77.441897, 38.762650 ], [ -77.441554, 38.762851 ], [ -77.440696, 38.763688 ], [ -77.440567, 38.763989 ], [ -77.440567, 38.764089 ] ] } } , @@ -434,9 +434,9 @@ , { "type": "Feature", "properties": { "LINEARID": "110410477227", "FULLNAME": "Gary Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.429924, 38.767636 ], [ -77.430696, 38.767101 ], [ -77.431211, 38.766733 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "1106087387886", "FULLNAME": "Manassas Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.427735, 38.765361 ], [ -77.427692, 38.765796 ], [ -77.427649, 38.767302 ], [ -77.427478, 38.768673 ], [ -77.427564, 38.769108 ], [ -77.427649, 38.769242 ], [ -77.427735, 38.769376 ], [ -77.427821, 38.769476 ], [ -77.427950, 38.769577 ], [ -77.428079, 38.769677 ], [ -77.428207, 38.769778 ], [ -77.428293, 38.769811 ], [ -77.428422, 38.769878 ], [ -77.428551, 38.769911 ], [ -77.428722, 38.769945 ], [ -77.428808, 38.769978 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "1106087387886", "FULLNAME": "Manassas Dr", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.427735, 38.765361 ], [ -77.427692, 38.765796 ], [ -77.427649, 38.767302 ], [ -77.427478, 38.768673 ], [ -77.427564, 38.769108 ], [ -77.427735, 38.769376 ], [ -77.428079, 38.769677 ], [ -77.428422, 38.769878 ], [ -77.428808, 38.769978 ] ] } } , -{ "type": "Feature", "properties": { "LINEARID": "110410477374", "FULLNAME": "Fairway Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.427564, 38.769108 ], [ -77.427649, 38.769242 ], [ -77.427735, 38.769376 ], [ -77.427821, 38.769476 ], [ -77.427950, 38.769577 ], [ -77.428079, 38.769677 ], [ -77.428207, 38.769778 ], [ -77.428293, 38.769811 ], [ -77.428422, 38.769878 ], [ -77.428551, 38.769911 ], [ -77.428722, 38.769945 ], [ -77.428808, 38.769978 ], [ -77.430611, 38.769911 ], [ -77.431340, 38.769711 ] ] } } +{ "type": "Feature", "properties": { "LINEARID": "110410477374", "FULLNAME": "Fairway Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.427564, 38.769108 ], [ -77.427735, 38.769376 ], [ -77.428079, 38.769677 ], [ -77.428422, 38.769878 ], [ -77.428808, 38.769978 ], [ -77.430611, 38.769911 ], [ -77.431340, 38.769711 ] ] } } , { "type": "Feature", "properties": { "LINEARID": "110410477279", "FULLNAME": "Payne Ct", "RTTYP": "M", "MTFCC": "S1400" }, "geometry": { "type": "LineString", "coordinates": [ [ -77.428508, 38.767302 ], [ -77.428594, 38.766565 ], [ -77.428551, 38.766197 ] ] } } , diff --git a/tile-join.cpp b/tile-join.cpp index a5116f019..6f30bb46c 100644 --- a/tile-join.cpp +++ b/tile-join.cpp @@ -378,7 +378,7 @@ double max(double a, double b) { struct tilecmp { bool operator()(std::pair const &a, std::pair const &b) { - // must match behavior of reader::operator<() + // must match behavior of tileset_reader::operator<() if (a.first < b.first) { return true; @@ -394,13 +394,13 @@ struct tilecmp { } } tilecmp; -// The `reader` is an iterator through the tiles of a tileset, +// The `tileset_reader` is an iterator through the tiles of a tileset, // in z/x/tms_y order. // // The basic idea is that it is used like this: // // void blah(const char *fname) { -// reader r(fname); +// tileset_reader r(fname); // // for (; !r.all_done(); r.advance()) { // std::pair tile = r.current(); @@ -411,13 +411,13 @@ struct tilecmp { // } // // The complication is that you can actually keep calling current() -// and advance() after the reader claims to be done, in which case +// and advance() after the tileset_reader claims to be done, in which case // it will produce overzoomed tiles generated from the tiles in // the maxzoom tileset. The parent tiles for those overzoomed tiles // are retrieved internally using get_tile() rather than through the // main iteration query. -struct reader { +struct tileset_reader { // z/x/y and data of the current tile long long zoom = 0; long long x = 0; @@ -442,7 +442,7 @@ struct reader { // for iterating mbtiles sqlite3 *db = NULL; sqlite3_stmt *stmt = NULL; - struct reader *next = NULL; + struct tileset_reader *next = NULL; // for iterating dirtiles std::vector dirtiles; @@ -453,7 +453,7 @@ struct reader { char *pmtiles_map = NULL; std::vector pmtiles_entries; - reader(const char *fname) { + tileset_reader(const char *fname) { name = fname; struct stat st; if (stat(fname, &st) == 0 && (st.st_mode & S_IFDIR) != 0) { @@ -503,7 +503,7 @@ struct reader { } } - // Checks the done status not only of this reader but also + // Checks the done status not only of this tileset_reader but also // the others chained to it in the queue. // // Also claims not to be done if at least one overzoomed tile @@ -517,7 +517,7 @@ struct reader { return false; } - for (struct reader *r = next; r != NULL; r = r->next) { + for (struct tileset_reader *r = next; r != NULL; r = r->next) { if (!r->done) { return false; } @@ -708,7 +708,7 @@ struct reader { // Sort in z/x/tms_y order, because that is the order of the // straightforward query of the mbtiles tiles table. - bool operator<(const struct reader &r) const { + bool operator<(const struct tileset_reader &r) const { // must match behavior of tilecmp if (zoom < r.zoom) { @@ -792,10 +792,10 @@ struct reader { } }; -struct reader *begin_reading(char *fname) { - struct reader *r = new reader(fname); +struct tileset_reader *begin_reading(char *fname) { + struct tileset_reader *r = new tileset_reader(fname); - // The reason this prefetches is so the reader queue can be + // The reason this prefetches is so the tileset_reader queue can be // priority-ordered, so the one with the next relevant tile // is first in line. r->advance(); @@ -816,7 +816,7 @@ struct arg { std::set *remove_layers = NULL; int ifmatched = 0; json_object *filter = NULL; - struct reader *readers = NULL; + struct tileset_reader *readers = NULL; }; void *join_worker(void *v) { @@ -861,7 +861,7 @@ void *join_worker(void *v) { return NULL; } -void dispatch_tasks(std::map> &tasks, std::vector> &layermaps, sqlite3 *outdb, const char *outdir, std::vector &header, std::map> &mapping, std::set &exclude, std::set &include, int ifmatched, std::set &keep_layers, std::set &remove_layers, json_object *filter, struct reader *readers) { +void dispatch_tasks(std::map> &tasks, std::vector> &layermaps, sqlite3 *outdb, const char *outdir, std::vector &header, std::map> &mapping, std::set &exclude, std::set &include, int ifmatched, std::set &keep_layers, std::set &remove_layers, json_object *filter, struct tileset_reader *readers) { pthread_t pthreads[CPUS]; std::vector args; @@ -1009,7 +1009,7 @@ void handle_vector_layers(json_object *vector_layers, std::map &layermap, sqlite3 *outdb, const char *outdir, struct stats *st, std::vector &header, std::map> &mapping, std::set &exclude, std::set &include, int ifmatched, std::string &attribution, std::string &description, std::set &keep_layers, std::set &remove_layers, std::string &name, json_object *filter, std::map &attribute_descriptions, std::string &generator_options, std::vector *strategies) { +void decode(struct tileset_reader *readers, std::map &layermap, sqlite3 *outdb, const char *outdir, struct stats *st, std::vector &header, std::map> &mapping, std::set &exclude, std::set &include, int ifmatched, std::string &attribution, std::string &description, std::set &keep_layers, std::set &remove_layers, std::string &name, json_object *filter, std::map &attribute_descriptions, std::string &generator_options, std::vector *strategies) { std::vector> layermaps; for (size_t i = 0; i < CPUS; i++) { layermaps.push_back(std::map()); @@ -1061,19 +1061,19 @@ void decode(struct reader *readers, std::map &layer f->second.push_back(current.second); } - // Advance the reader that we just added as a task. - // The reason this prefetches is so the reader queue can be + // Advance the tileset_reader that we just added as a task. + // The reason this prefetches is so the tileset_reader queue can be // priority-ordered, so the one with the next relevant tile // is first in line. readers->advance(); - // pull the reader off the front of the queue for reordering + // pull the tileset_reader off the front of the queue for reordering - reader *r = readers; + tileset_reader *r = readers; readers = readers->next; r->next = NULL; - // Is the next reader on the reader queue looking at a different tile? + // Is the next tileset_reader on the tileset_reader queue looking at a different tile? // Then this tile is done and we can safely run the output queue. if (readers == NULL || readers->zoom != current.first.z || readers->x != current.first.x || readers->y != current.first.y) { @@ -1083,10 +1083,10 @@ void decode(struct reader *readers, std::map &layer } } - // put the reader back onto the queue, + // put the tileset_reader back onto the queue, // in whatever sequence its next tile calls for - struct reader **rr; + struct tileset_reader **rr; for (rr = &readers; *rr != NULL; rr = &((*rr)->next)) { if (*r < **rr) { break; @@ -1110,8 +1110,8 @@ void decode(struct reader *readers, std::map &layer dispatch_tasks(tasks, layermaps, outdb, outdir, header, mapping, exclude, include, ifmatched, keep_layers, remove_layers, filter, readers); layermap = merge_layermaps(layermaps); - struct reader *next; - for (struct reader *r = readers; r != NULL; r = next) { + struct tileset_reader *next; + for (struct tileset_reader *r = readers; r != NULL; r = next) { next = r->next; r->close(); @@ -1262,7 +1262,7 @@ int main(int argc, char **argv) { int filearg = 0; json_object *filter = NULL; - struct reader *readers = NULL; + struct tileset_reader *readers = NULL; CPUS = sysconf(_SC_NPROCESSORS_ONLN); @@ -1461,10 +1461,10 @@ int main(int argc, char **argv) { filearg = 1; while (getline(read_file, sa)) { char *c = const_cast(sa.c_str()); - reader *r = begin_reading(c); + tileset_reader *r = begin_reading(c); - // put the new reader in priority order - struct reader **rr; + // put the new tileset_reader in priority order + struct tileset_reader **rr; for (rr = &readers; *rr != NULL; rr = &((*rr)->next)) { if (*r < **rr) { break; @@ -1553,10 +1553,10 @@ int main(int argc, char **argv) { if (filearg == 0) { for (i = optind; i < argc; i++) { - reader *r = begin_reading(argv[i]); + tileset_reader *r = begin_reading(argv[i]); - // put the new reader in priority order - struct reader **rr; + // put the new tileset_reader in priority order + struct tileset_reader **rr; for (rr = &readers; *rr != NULL; rr = &((*rr)->next)) { if (*r < **rr) { break; diff --git a/tile.cpp b/tile.cpp index 4584c316a..d94b1493a 100644 --- a/tile.cpp +++ b/tile.cpp @@ -480,7 +480,10 @@ struct partial_arg { std::vector *partials = NULL; int task = 0; int tasks = 0; + drawvec *shared_nodes; + node *shared_nodes_map; + size_t nodepos; }; drawvec revive_polygon(drawvec &geom, double area, int z, int detail) { @@ -524,7 +527,7 @@ drawvec revive_polygon(drawvec &geom, double area, int z, int detail) { } } -double simplify_partial(partial *p, drawvec &shared_nodes) { +double simplify_partial(partial *p, drawvec const &shared_nodes, node *shared_nodes_map, size_t nodepos) { drawvec geom; for (size_t j = 0; j < p->geoms.size(); j++) { @@ -579,7 +582,7 @@ double simplify_partial(partial *p, drawvec &shared_nodes) { } // continues to simplify to line_detail even if we have extra detail - drawvec ngeom = simplify_lines(geom, z, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), p->simplification, t == VT_POLYGON ? 4 : 0, shared_nodes); + drawvec ngeom = simplify_lines(geom, z, p->tx, p->ty, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), p->simplification, t == VT_POLYGON ? 4 : 0, shared_nodes, shared_nodes_map, nodepos); if (t != VT_POLYGON || ngeom.size() >= 3) { geom = ngeom; @@ -602,7 +605,7 @@ void *partial_feature_worker(void *v) { std::vector *partials = a->partials; for (size_t i = a->task; i < (*partials).size(); i += a->tasks) { - double area = simplify_partial(&((*partials)[i]), *(a->shared_nodes)); + double area = simplify_partial(&((*partials)[i]), *(a->shared_nodes), a->shared_nodes_map, a->nodepos); signed char t = (*partials)[i].t; int z = (*partials)[i].z; @@ -1031,7 +1034,8 @@ bool find_common_edges(std::vector &partials, int z, int line_detail, d } } if (!(prevent[P_SIMPLIFY] || (z == maxzoom && prevent[P_SIMPLIFY_LOW]) || (z < maxzoom && additional[A_GRID_LOW_ZOOMS]))) { - simplified_arcs[ai->second] = simplify_lines(dv, z, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), simplification, 4, drawvec()); + // tx and ty are 0 here because we aren't trying to do anything with the shared_nodes_map + simplified_arcs[ai->second] = simplify_lines(dv, z, 0, 0, line_detail, !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), simplification, 4, drawvec(), NULL, 0); } else { simplified_arcs[ai->second] = dv; } @@ -1294,48 +1298,6 @@ long long choose_minextent(std::vector &extents, double f) { return extents[(extents.size() - 1) * (1 - f)]; } -struct joint { - long long x : 34; // enough to wrap around the world either way - unsigned long long p1 : 64 - 34; - - long long y : 33; // enough to touch the top and bottom of the world - unsigned long long p2 : 64 - 34; - - joint(draw one, draw hinge, draw two) { - if (one < two) { - std::swap(one, two); - } - - long long coord1[2] = {one.x, one.y}; - long long coord2[2] = {two.x, two.y}; - p1 = fnv1a(std::string((const char *) &coord1, sizeof(coord1))); - p2 = fnv1a(std::string((const char *) &coord2, sizeof(coord2))); - - x = hinge.x; - y = hinge.y; - } - - bool operator<(const joint &o) const { - if (y < o.y) { - return true; - } else if (y == o.y) { - if (x < o.x) { - return true; - } else if (x == o.x) { - if (p1 < o.p1) { - return true; - } else if (p1 == o.p1) { - if (p2 < o.p2) { - return true; - } - } - } - } - - return false; - } -}; - struct write_tile_args { struct task *tasks = NULL; char *stringpool = NULL; @@ -1387,6 +1349,8 @@ struct write_tile_args { atomic_strategy *strategy = NULL; int zoom = -1; bool compressed; + struct node *shared_nodes_map; + size_t nodepos; }; bool clip_to_tile(serial_feature &sf, int z, long long buffer) { @@ -1934,7 +1898,7 @@ void add_sample_to(std::vector &vals, T val, size_t &increment, size_t seq) { } } -long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, double fraction, const char *prefilter, const char *postfilter, struct json_object *filter, write_tile_args *arg, atomic_strategy *strategy, bool compressed_input) { +long long write_tile(decompressor *geoms, std::atomic *geompos_in, char *stringpool, int z, const unsigned tx, const unsigned ty, const int detail, int min_detail, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, compressor **geomfile, int minzoom, int maxzoom, double todo, std::atomic *along, long long alongminus, double gamma, int child_shards, long long *pool_off, unsigned *initial_x, unsigned *initial_y, std::atomic *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t tiling_seg, size_t pass, unsigned long long mingap, long long minextent, double fraction, const char *prefilter, const char *postfilter, struct json_object *filter, write_tile_args *arg, atomic_strategy *strategy, bool compressed_input, struct node *shared_nodes_map, size_t nodepos) { double merge_fraction = 1; double mingap_fraction = 1; double minextent_fraction = 1; @@ -1990,7 +1954,6 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch double coalesced_area = 0; drawvec shared_nodes; - std::vector shared_joints; int tile_detail = line_detail; size_t skipped = 0; @@ -2272,89 +2235,8 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch } else { kept++; - if (prevent[P_SIMPLIFY_SHARED_NODES]) { - if (sf.t == VT_LINE) { - for (auto &g : sf.geometry) { - shared_nodes.push_back(g); - } - } else if (sf.t == VT_POLYGON) { - sf.geometry = remove_noop(sf.geometry, sf.t, 0); - - for (size_t i = 0; i < sf.geometry.size(); i++) { - if (sf.geometry[i].op == VT_MOVETO) { - size_t j; - for (j = i + 1; j < sf.geometry.size(); j++) { - if (sf.geometry[j].op != VT_LINETO) { - break; - } - } - - // j - 1 because we don't want the duplicate last point - for (size_t k = i; k < j - 1; k++) { - shared_joints.emplace_back( - sf.geometry[k], - sf.geometry[(k + 1 - i) % (j - 1 - i) + i], - sf.geometry[(k + 2 - i) % (j - 1 - i) + i]); - } - - // since the starting point is never simplified away, - // don't let it be simplified away in any other polygons either. - // Needs to appear twice here so that the check below will see - // it as appearing in multiple features. - shared_nodes.push_back(sf.geometry[i]); - shared_nodes.push_back(sf.geometry[i]); - - // To avoid letting polygons get simplified away to nothing, - // also keep the furthest-away point from the initial point - // (which Douglas-Peucker simplification would keep anyway, - // if its search weren't being split up by polygon side). - - double far = 0; - size_t which = i; - for (size_t k = i + 1; k < j - 1; k++) { - double xd = sf.geometry[k].x - sf.geometry[i].x; - double yd = sf.geometry[k].y - sf.geometry[i].y; - double d = xd * xd + yd * yd; - if (d > far || - ((d == far) && (sf.geometry[k] < sf.geometry[which]))) { - far = d; - which = k; - } - } - - shared_nodes.push_back(sf.geometry[which]); - shared_nodes.push_back(sf.geometry[which]); - - // And, likewise, the point most distant from those two points, - // which probably would also be the one that Douglas-Peucker - // would keep next. - - far = 0; - size_t which2 = i; - - for (size_t k = i + 1; k < j - 1; k++) { - double d = distance_from_line(sf.geometry[k].x, sf.geometry[k].y, - sf.geometry[i].x, sf.geometry[i].y, - sf.geometry[which].x, sf.geometry[which].y); - if ((d > far) || - ((d == far) && (sf.geometry[k] < sf.geometry[which2]))) { - far = d; - which2 = k; - } - } - - shared_nodes.push_back(sf.geometry[which2]); - shared_nodes.push_back(sf.geometry[which2]); - - i = j - 1; - } - } - } - - for (auto &p : sf.edge_nodes) { - shared_nodes.push_back(p); - shared_nodes.push_back(p); - } + for (auto &p : sf.edge_nodes) { + shared_nodes.push_back(p); } partial p; @@ -2398,11 +2280,15 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch partials.push_back(p); unsimplified_geometry_size += sf.geometry.size() * sizeof(draw); - if (unsimplified_geometry_size > 10 * 1024 * 1024 && !additional[A_DETECT_SHARED_BORDERS] && !prevent[P_SIMPLIFY_SHARED_NODES]) { + if (unsimplified_geometry_size > 10 * 1024 * 1024 && !additional[A_DETECT_SHARED_BORDERS]) { + // we should be safe to simplify here with P_SIMPLIFY_SHARED_NODES, since they will + // have been assembled globally, although that also means that simplification + // may not be very effective for reducing memory usage. + drawvec dv; for (; simplified_geometry_through < partials.size(); simplified_geometry_through++) { - simplify_partial(&partials[simplified_geometry_through], dv); + simplify_partial(&partials[simplified_geometry_through], dv, shared_nodes_map, nodepos); for (auto &g : partials[simplified_geometry_through].geoms) { if (partials[simplified_geometry_through].t == VT_POLYGON) { @@ -2421,38 +2307,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch coalesced_area = 0; } - { - drawvec just_shared_nodes; - std::sort(shared_nodes.begin(), shared_nodes.end()); - - for (size_t i = 0; i + 1 < shared_nodes.size(); i++) { - if (shared_nodes[i] == shared_nodes[i + 1]) { - just_shared_nodes.push_back(shared_nodes[i]); - - draw d = shared_nodes[i]; - i++; // consume the first, point at the duplicate - while (i + 1 < shared_nodes.size() && shared_nodes[i + 1] == d) { - i++; - } - } - } - - shared_nodes = just_shared_nodes; - - std::sort(shared_joints.begin(), shared_joints.end()); - for (size_t i = 0; i + 1 < shared_joints.size(); i++) { - if (shared_joints[i].x == shared_joints[i + 1].x && - shared_joints[i].y == shared_joints[i + 1].y) { - if (shared_joints[i].p1 != shared_joints[i + 1].p1 || - shared_joints[i].p2 != shared_joints[i + 1].p2) { - shared_nodes.push_back(draw(VT_MOVETO, shared_joints[i].x, shared_joints[i].y)); - } - } - } - - std::sort(shared_nodes.begin(), shared_nodes.end()); - shared_joints.clear(); - } + std::sort(shared_nodes.begin(), shared_nodes.end()); for (size_t i = 0; i < partials.size(); i++) { partial &p = partials[i]; @@ -2552,6 +2407,8 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch args[i].tasks = tasks; args[i].partials = &partials; args[i].shared_nodes = &shared_nodes; + args[i].shared_nodes_map = shared_nodes_map; + args[i].nodepos = nodepos; if (tasks > 1) { if (pthread_create(&pthreads[i], NULL, partial_feature_worker, &args[i]) != 0) { @@ -2667,8 +2524,9 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch if (layer_features[x].coalesced && layer_features[x].type == VT_LINE) { layer_features[x].geom = remove_noop(layer_features[x].geom, layer_features[x].type, 0); if (!(prevent[P_SIMPLIFY] || (z == maxzoom && prevent[P_SIMPLIFY_LOW]))) { - layer_features[x].geom = simplify_lines(layer_features[x].geom, 32, 0, - !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), simplification, layer_features[x].type == VT_POLYGON ? 4 : 0, shared_nodes); + // XXX revisit: why does this not take zoom into account? + layer_features[x].geom = simplify_lines(layer_features[x].geom, 32, 0, 0, 0, + !(prevent[P_CLIPPING] || prevent[P_DUPLICATION]), simplification, layer_features[x].type == VT_POLYGON ? 4 : 0, shared_nodes, NULL, 0); } } @@ -3082,7 +2940,7 @@ void *run_thread(void *vargs) { // fprintf(stderr, "%d/%u/%u\n", z, x, y); - long long len = write_tile(&dc, &geompos, arg->stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->fraction, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed); + long long len = write_tile(&dc, &geompos, arg->stringpool, z, x, y, z == arg->maxzoom ? arg->full_detail : arg->low_detail, arg->min_detail, arg->outdb, arg->outdir, arg->buffer, arg->fname, arg->geomfile, arg->minzoom, arg->maxzoom, arg->todo, arg->along, geompos, arg->gamma, arg->child_shards, arg->pool_off, arg->initial_x, arg->initial_y, arg->running, arg->simplification, arg->layermaps, arg->layer_unmaps, arg->tiling_seg, arg->pass, arg->mingap, arg->minextent, arg->fraction, arg->prefilter, arg->postfilter, arg->filter, arg, arg->strategy, arg->compressed, arg->shared_nodes_map, arg->nodepos); if (len < 0) { int *err = &arg->err; @@ -3147,7 +3005,7 @@ void *run_thread(void *vargs) { return NULL; } -int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz) { +int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector> &layermaps, const char *prefilter, const char *postfilter, std::map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos) { last_progress = 0; // The existing layermaps are one table per input thread. @@ -3352,6 +3210,8 @@ int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic< args[thread].strategy = &strategy; args[thread].zoom = z; args[thread].compressed = (z != iz); + args[thread].shared_nodes_map = shared_nodes_map; + args[thread].nodepos = nodepos; if (pthread_create(&pthreads[thread], NULL, run_thread, &args[thread]) != 0) { perror("pthread_create"); diff --git a/tile.hpp b/tile.hpp index 1159f3761..cecff0066 100644 --- a/tile.hpp +++ b/tile.hpp @@ -7,6 +7,7 @@ #include #include #include "mbtiles.hpp" +#include "serial.hpp" #include "jsonpull/jsonpull.h" enum attribute_op { @@ -61,9 +62,9 @@ struct strategy { strategy() = default; }; -long long write_tile(char **geom, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, sqlite3 *outdb, const char *outdir, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, std::atomic *strategy); +// long long write_tile(char **geom, char *stringpool, unsigned *file_bbox, int z, unsigned x, unsigned y, int detail, int min_detail, int basezoom, sqlite3 *outdb, const char *outdir, double droprate, int buffer, const char *fname, FILE **geomfile, int file_minzoom, int file_maxzoom, double todo, char *geomstart, long long along, double gamma, int nlayers, std::atomic *strategy); -int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz); +int traverse_zooms(int *geomfd, off_t *geom_size, char *stringpool, std::atomic *midx, std::atomic *midy, int &maxzoom, int minzoom, sqlite3 *outdb, const char *outdir, int buffer, const char *fname, const char *tmpdir, double gamma, int full_detail, int low_detail, int min_detail, long long *pool_off, unsigned *initial_x, unsigned *initial_y, double simplification, double maxzoom_simplification, std::vector > &layermap, const char *prefilter, const char *postfilter, std::map const *attribute_accum, struct json_object *filter, std::vector &strategies, int iz, struct node *shared_nodes_map, size_t nodepos); int manage_gap(unsigned long long index, unsigned long long *previndex, double scale, double gamma, double *gap); diff --git a/unit.cpp b/unit.cpp index 24c3fb100..006dab326 100644 --- a/unit.cpp +++ b/unit.cpp @@ -1,6 +1,9 @@ #define CATCH_CONFIG_MAIN #include "catch/catch.hpp" #include "text.hpp" +#include "sort.hpp" +#include +#include TEST_CASE("UTF-8 enforcement", "[utf8]") { REQUIRE(check_utf8("") == std::string("")); @@ -18,3 +21,47 @@ TEST_CASE("UTF-8 truncation", "[trunc]") { REQUIRE(truncate16("0123456789😀😬😁😂😃😄😅😆", 17) == std::string("0123456789😀😬😁")); REQUIRE(truncate16("0123456789あいうえおかきくけこさ", 16) == std::string("0123456789あいうえおか")); } + +int intcmp(const void *v1, const void *v2) { + return *((int *) v1) - *((int *) v2); +} + +TEST_CASE("External quicksort", "fqsort") { + std::vector inputs; + + size_t written = 0; + for (size_t i = 0; i < 5; i++) { + std::string tmpname = "/tmp/in.XXXXXXX"; + int fd = mkstemp((char *) tmpname.c_str()); + unlink(tmpname.c_str()); + FILE *f = fdopen(fd, "w+b"); + inputs.emplace_back(f); + size_t iterations = 2000 + rand() % 200; + for (size_t j = 0; j < iterations; j++) { + int n = rand(); + fwrite((void *) &n, sizeof(int), 1, f); + written++; + } + rewind(f); + } + + std::string tmpname = "/tmp/out.XXXXXX"; + int fd = mkstemp((char *) tmpname.c_str()); + unlink(tmpname.c_str()); + FILE *f = fdopen(fd, "w+b"); + + fqsort(inputs, sizeof(int), intcmp, f, 256); + rewind(f); + + int prev = INT_MIN; + int here; + size_t nread = 0; + while (fread((void *) &here, sizeof(int), 1, f)) { + REQUIRE(here >= prev); + prev = here; + nread++; + } + + fclose(f); + REQUIRE(nread == written); +} diff --git a/version.hpp b/version.hpp index 9d8e4b143..a88ebb886 100644 --- a/version.hpp +++ b/version.hpp @@ -1,6 +1,6 @@ #ifndef VERSION_HPP #define VERSION_HPP -#define VERSION "v2.32.1" +#define VERSION "v2.33.0" #endif