From 5194a39c16d8e35f6d935ea3eb6ae853c9f4d002 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Fri, 9 Dec 2016 10:47:03 -0800 Subject: [PATCH] Factor out clipping to tile boundaries; test random attributes & layers --- Makefile | 13 ++--- geometry.cpp | 10 ++-- geometry.hpp | 10 ++-- tile.cpp | 146 +++++++++++++++++++++++++++------------------------ 4 files changed, 94 insertions(+), 85 deletions(-) diff --git a/Makefile b/Makefile index db9f04a..aae15cf 100644 --- a/Makefile +++ b/Makefile @@ -87,16 +87,17 @@ test: tippecanoe tippecanoe-decode $(addsuffix .check,$(TESTS)) parallel-test pb parallel-test: mkdir -p tests/parallel - perl -e 'for ($$i = 0; $$i < 20; $$i++) { $$lon = rand(360) - 180; $$lat = rand(180) - 90; print "{ \"type\": \"Feature\", \"properties\": { \"yes\": \"no\", \"who\": 1 }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ $$lon, $$lat ] } }\n"; }' > tests/parallel/in1.json + perl -e 'for ($$i = 0; $$i < 20; $$i++) { $$lon = rand(360) - 180; $$lat = rand(180) - 90; $$k = rand(1); $$v = rand(1); print "{ \"type\": \"Feature\", \"properties\": { \"yes\": \"no\", \"who\": 1, \"$$k\": \"$$v\" }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ $$lon, $$lat ] } }\n"; }' > tests/parallel/in1.json perl -e 'for ($$i = 0; $$i < 300000; $$i++) { $$lon = rand(360) - 180; $$lat = rand(180) - 90; print "{ \"type\": \"Feature\", \"properties\": { }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ $$lon, $$lat ] } }\n"; }' > tests/parallel/in2.json perl -e 'for ($$i = 0; $$i < 20; $$i++) { $$lon = rand(360) - 180; $$lat = rand(180) - 90; print "{ \"type\": \"Feature\", \"properties\": { }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ $$lon, $$lat ] } }\n"; }' > tests/parallel/in3.json + perl -e 'for ($$i = 0; $$i < 20; $$i++) { $$lon = rand(360) - 180; $$lat = rand(180) - 90; $$v = rand(1); print "{ \"type\": \"Feature\", \"properties\": { }, \"tippecanoe\": { \"layer\": \"$$v\" }, \"geometry\": { \"type\": \"Point\", \"coordinates\": [ $$lon, $$lat ] } }\n"; }' > tests/parallel/in4.json echo -n "" > tests/parallel/empty1.json echo "" > tests/parallel/empty2.json - ./tippecanoe -z5 -f -pi -l test -n test -o tests/parallel/linear-file.mbtiles tests/parallel/in[123].json tests/parallel/empty[12].json - ./tippecanoe -z5 -f -pi -l test -n test -P -o tests/parallel/parallel-file.mbtiles tests/parallel/in[123].json tests/parallel/empty[12].json - cat tests/parallel/in[123].json | ./tippecanoe -z5 -f -pi -l test -n test -o tests/parallel/linear-pipe.mbtiles - cat tests/parallel/in[123].json | ./tippecanoe -z5 -f -pi -l test -n test -P -o tests/parallel/parallel-pipe.mbtiles - ./tippecanoe -z5 -f -pi -l test -n test -P -o tests/parallel/parallel-pipes.mbtiles <(cat tests/parallel/in1.json) <(cat tests/parallel/empty1.json) <(cat tests/parallel/empty2.json) <(cat tests/parallel/in2.json) /dev/null <(cat tests/parallel/in3.json) + ./tippecanoe -z5 -f -pi -l test -n test -o tests/parallel/linear-file.mbtiles tests/parallel/in[1234].json tests/parallel/empty[12].json + ./tippecanoe -z5 -f -pi -l test -n test -P -o tests/parallel/parallel-file.mbtiles tests/parallel/in[1234].json tests/parallel/empty[12].json + cat tests/parallel/in[1234].json | ./tippecanoe -z5 -f -pi -l test -n test -o tests/parallel/linear-pipe.mbtiles + cat tests/parallel/in[1234].json | ./tippecanoe -z5 -f -pi -l test -n test -P -o tests/parallel/parallel-pipe.mbtiles + ./tippecanoe -z5 -f -pi -l test -n test -P -o tests/parallel/parallel-pipes.mbtiles <(cat tests/parallel/in1.json) <(cat tests/parallel/empty1.json) <(cat tests/parallel/empty2.json) <(cat tests/parallel/in2.json) /dev/null <(cat tests/parallel/in3.json) <(cat tests/parallel/in4.json) ./tippecanoe-decode tests/parallel/linear-file.mbtiles > tests/parallel/linear-file.json ./tippecanoe-decode tests/parallel/parallel-file.mbtiles > tests/parallel/parallel-file.json ./tippecanoe-decode tests/parallel/linear-pipe.mbtiles > tests/parallel/linear-pipe.json diff --git a/geometry.cpp b/geometry.cpp index 2746d39..c004f80 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -699,7 +699,7 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon return out; } -drawvec simple_clip_poly(drawvec &geom, int z, int detail, int buffer) { +drawvec simple_clip_poly(drawvec &geom, int z, int buffer) { long long area = 1LL << (32 - z); long long clip_buffer = buffer * area / 256; @@ -785,7 +785,7 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double return out; } -drawvec clip_point(drawvec &geom, int z, int detail, long long buffer) { +drawvec clip_point(drawvec &geom, int z, long long buffer) { drawvec out; long long min = 0; @@ -803,7 +803,7 @@ drawvec clip_point(drawvec &geom, int z, int detail, long long buffer) { return out; } -int quick_check(long long *bbox, int z, int detail, long long buffer) { +int quick_check(long long *bbox, int z, long long buffer) { long long min = 0; long long area = 1LL << (32 - z); @@ -827,7 +827,7 @@ int quick_check(long long *bbox, int z, int detail, long long buffer) { return 2; } -bool point_within_tile(long long x, long long y, int z, int detail, long long buffer) { +bool point_within_tile(long long x, long long y, int z, long long buffer) { // No adjustment for buffer, because the point must be // strictly within the tile to appear exactly once @@ -836,7 +836,7 @@ bool point_within_tile(long long x, long long y, int z, int detail, long long bu return x >= 0 && y >= 0 && x < area && y < area; } -drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer) { +drawvec clip_lines(drawvec &geom, int z, long long buffer) { drawvec out; long long min = 0; diff --git a/geometry.hpp b/geometry.hpp index 8f522d9..b8f0bf0 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -56,15 +56,15 @@ typedef std::vector drawvec; drawvec decode_geometry(FILE *meta, long long *geompos, int z, unsigned tx, unsigned ty, long long *bbox, unsigned initial_x, unsigned initial_y); void to_tile_scale(drawvec &geom, int z, int detail); drawvec remove_noop(drawvec geom, int type, int shift); -drawvec clip_point(drawvec &geom, int z, int detail, long long buffer); +drawvec clip_point(drawvec &geom, int z, long long buffer); drawvec clean_or_clip_poly(drawvec &geom, int z, int detail, int buffer, bool clip); -drawvec simple_clip_poly(drawvec &geom, int z, int detail, int buffer); +drawvec simple_clip_poly(drawvec &geom, int z, int buffer); drawvec close_poly(drawvec &geom); drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area); -drawvec clip_lines(drawvec &geom, int z, int detail, long long buffer); +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 detail, long long buffer); -int quick_check(long long *bbox, int z, int detail, long long buffer); +bool point_within_tile(long long x, long long y, int z, long long buffer); +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 reorder_lines(drawvec &geom); drawvec fix_polygon(drawvec &geom); diff --git a/tile.cpp b/tile.cpp index 584e8cc..f0ebc69 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1171,6 +1171,82 @@ struct write_tile_args { const char *postfilter; }; +bool clip_to_tile(serial_feature &sf, int z, long long buffer) { + int quick = quick_check(sf.bbox, z, buffer); + if (quick == 0) { + return true; + } + + if (z == 0) { + if (sf.bbox[0] < 0 || sf.bbox[2] > 1LL << 32) { + // If the geometry extends off the edge of the world, concatenate on another copy + // shifted by 360 degrees, and then make sure both copies get clipped down to size. + + size_t n = sf.geometry.size(); + + if (sf.bbox[0] < 0) { + for (size_t i = 0; i < n; i++) { + sf.geometry.push_back(draw(sf.geometry[i].op, sf.geometry[i].x + (1LL << 32), sf.geometry[i].y)); + } + } + + if (sf.bbox[2] > 1LL << 32) { + for (size_t i = 0; i < n; i++) { + sf.geometry.push_back(draw(sf.geometry[i].op, sf.geometry[i].x - (1LL << 32), sf.geometry[i].y)); + } + } + + sf.bbox[0] = 0; + sf.bbox[2] = 1LL << 32; + + quick = -1; + } + } + + // Can't accept the quick check if guaranteeing no duplication, since the + // overlap might have been in the buffer. + if (quick != 1 || prevent[P_DUPLICATION]) { + drawvec clipped; + + // Do the clipping, even if we are going to include the whole feature, + // so that we can know whether the feature itself, or only the feature's + // bounding box, touches the tile. + + if (sf.t == VT_LINE) { + clipped = clip_lines(sf.geometry, z, buffer); + } + if (sf.t == VT_POLYGON) { + clipped = simple_clip_poly(sf.geometry, z, buffer); + } + if (sf.t == VT_POINT) { + clipped = clip_point(sf.geometry, z, buffer); + } + + clipped = remove_noop(clipped, sf.t, 0); + + // Must clip at z0 even if we don't want clipping, to handle features + // that are duplicated across the date line + + if (prevent[P_DUPLICATION] && z != 0) { + if (point_within_tile((sf.bbox[0] + sf.bbox[2]) / 2, (sf.bbox[1] + sf.bbox[3]) / 2, z, buffer)) { + // sf.geometry is unchanged + } else { + sf.geometry.clear(); + } + } else if (prevent[P_CLIPPING] && z != 0) { + if (clipped.size() == 0) { + sf.geometry.clear(); + } else { + // sf.geometry is unchanged + } + } else { + sf.geometry = clipped; + } + } + + return false; +} + long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *stringpool, int z, unsigned tx, unsigned ty, int detail, int min_detail, int basezoom, sqlite3 *outdb, double droprate, int buffer, const char *fname, FILE **geomfile, int minzoom, int maxzoom, double todo, volatile long long *along, long long alongminus, double gamma, int child_shards, long long *meta_off, long long *pool_off, unsigned *initial_x, unsigned *initial_y, volatile int *running, double simplification, std::vector> *layermaps, std::vector> *layer_unmaps, size_t pass, size_t passes, unsigned long long mingap, long long minextent, double fraction, const char *prefilter, const char *postfilter, write_tile_args *arg) { int line_detail; double merge_fraction = 1; @@ -1270,78 +1346,10 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s original_features++; - int quick = quick_check(sf.bbox, z, line_detail, buffer); - if (quick == 0) { + if (clip_to_tile(sf, z, buffer)) { continue; } - if (z == 0) { - if (sf.bbox[0] < 0 || sf.bbox[2] > 1LL << 32) { - // If the geometry extends off the edge of the world, concatenate on another copy - // shifted by 360 degrees, and then make sure both copies get clipped down to size. - - size_t n = sf.geometry.size(); - - if (sf.bbox[0] < 0) { - for (size_t i = 0; i < n; i++) { - sf.geometry.push_back(draw(sf.geometry[i].op, sf.geometry[i].x + (1LL << 32), sf.geometry[i].y)); - } - } - - if (sf.bbox[2] > 1LL << 32) { - for (size_t i = 0; i < n; i++) { - sf.geometry.push_back(draw(sf.geometry[i].op, sf.geometry[i].x - (1LL << 32), sf.geometry[i].y)); - } - } - - sf.bbox[0] = 0; - sf.bbox[2] = 1LL << 32; - - quick = -1; - } - } - - // Can't accept the quick check if guaranteeing no duplication, since the - // overlap might have been in the buffer. - if (quick != 1 || prevent[P_DUPLICATION]) { - drawvec clipped; - - // Do the clipping, even if we are going to include the whole feature, - // so that we can know whether the feature itself, or only the feature's - // bounding box, touches the tile. - - if (sf.t == VT_LINE) { - clipped = clip_lines(sf.geometry, z, line_detail, buffer); - } - if (sf.t == VT_POLYGON) { - clipped = simple_clip_poly(sf.geometry, z, line_detail, buffer); - } - if (sf.t == VT_POINT) { - clipped = clip_point(sf.geometry, z, line_detail, buffer); - } - - clipped = remove_noop(clipped, sf.t, 0); - - // Must clip at z0 even if we don't want clipping, to handle features - // that are duplicated across the date line - - if (prevent[P_DUPLICATION] && z != 0) { - if (point_within_tile((sf.bbox[0] + sf.bbox[2]) / 2, (sf.bbox[1] + sf.bbox[3]) / 2, z, line_detail, buffer)) { - // sf.geometry is unchanged - } else { - sf.geometry.clear(); - } - } else if (prevent[P_CLIPPING] && z != 0) { - if (clipped.size() == 0) { - sf.geometry.clear(); - } else { - // sf.geometry is unchanged - } - } else { - sf.geometry = clipped; - } - } - if (sf.geometry.size() > 0) { unclipped_features++; }