From 0db8d9ed8bdb61d11021ed78428caec3a24b0fe4 Mon Sep 17 00:00:00 2001 From: Eric Fischer Date: Mon, 28 Nov 2016 14:55:22 -0800 Subject: [PATCH] Option to snap low zooms to a stairstep grid --- README.md | 1 + geometry.cpp | 82 ++++++++++++++++++++++++++++++++++++++++++++++++ geometry.hpp | 1 + main.cpp | 1 + man/tippecanoe.1 | 2 ++ options.hpp | 1 + tile.cpp | 10 ++++-- 7 files changed, 95 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 1d23682..2144636 100644 --- a/README.md +++ b/README.md @@ -131,6 +131,7 @@ resolution is obtained than by using a smaller _maxzoom_ or _detail_. * -as or --drop-densest-as-needed: If a tile is too large, try to reduce it to under 500K by increasing the minimum spacing between features. The discovered spacing applies to the entire zoom level. * -ad or --drop-fraction-as-needed: Dynamically drop some fraction of features from each zoom level to keep large tiles under the 500K size limit. (This is like `-pd` but applies to the entire zoom level, not to each tile.) * -an or --drop-smallest-as-needed: Dynamically drop the smallest features (physically smallest: the shortest lines or the smallest polygons) from each zoom level to keep large tiles under the 500K size limit. This option will not work for point features. + * -aL or --grid-low-zooms: At all zoom levels below _maxzoom_, snap all lines and polygons to a stairstep grid instead of allowing diagonals. You will also want to specify a tile resolution, probably `-D8`. This option provides a way to display continuous parcel, gridded, or binned data at low zooms without overwhelming the tiles with tiny polygons, since features will either get stretched out to the grid unit or lost entirely, depending on how they happened to be aligned in the original data. ### Doing less diff --git a/geometry.cpp b/geometry.cpp index b6e0265..a7f3ccc 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -1299,3 +1299,85 @@ static int clip(double *x0, double *y0, double *x1, double *y1, double xmin, dou return changed + 1; } } + +drawvec stairstep(drawvec &geom, int z, int detail) { + drawvec out; + + for (size_t i = 0; i < geom.size(); i++) { + geom[i].x >>= (32 - detail - z); + geom[i].y >>= (32 - detail - z); + } + + for (size_t i = 0; i < geom.size(); i++) { + if (geom[i].op == VT_MOVETO) { + out.push_back(geom[i]); + } else { + long long x0 = out[out.size() - 1].x; + long long y0 = out[out.size() - 1].y; + long long x1 = geom[i].x; + long long y1 = geom[i].y; + bool swap = false; + + if (y0 < y1) { + swap = true; + std::swap(x0, x1); + std::swap(y0, y1); + } + + long long xx = x0, yy = y0; + long long dx = std::abs(x1 - x0); + long long sx = (x0 < x1) ? 1 : -1; + long long dy = std::abs(y1 - y0); + long long sy = (y0 < y1) ? 1 : -1; + long long err = ((dx > dy) ? dx : -dy) / 2; + int last = -1; + + drawvec tmp; + tmp.push_back(draw(VT_LINETO, xx, yy)); + + while (xx != x1 || yy != y1) { + long long e2 = err; + + if (e2 > -dx) { + err -= dy; + xx += sx; + if (last == 1) { + tmp[tmp.size() - 1] = draw(VT_LINETO, xx, yy); + } else { + tmp.push_back(draw(VT_LINETO, xx, yy)); + } + last = 1; + } + if (e2 < dy) { + err += dx; + yy += sy; + if (last == 2) { + tmp[tmp.size() - 1] = draw(VT_LINETO, xx, yy); + } else { + tmp.push_back(draw(VT_LINETO, xx, yy)); + } + last = 2; + } + } + + if (swap) { + for (size_t j = tmp.size(); j > 0; j--) { + out.push_back(tmp[j - 1]); + } + } else { + for (size_t j = 0; j < tmp.size(); j++) { + out.push_back(tmp[j]); + } + } + + // out.push_back(draw(VT_LINETO, xx, yy)); + } + } + + for (size_t i = 0; i < out.size(); i++) { + out[i].x <<= (32 - detail - z); + out[i].y <<= (32 - detail - z); + } + + return out; +} diff --git a/geometry.hpp b/geometry.hpp index de5683d..ce4517c 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -62,6 +62,7 @@ drawvec simple_clip_poly(drawvec &geom, int z, int detail, 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 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); drawvec simplify_lines(drawvec &geom, int z, int detail, bool mark_tile_bounds, double simplification, size_t retain); diff --git a/main.cpp b/main.cpp index 7e38bd6..ca4bd86 100644 --- a/main.cpp +++ b/main.cpp @@ -1920,6 +1920,7 @@ int main(int argc, char **argv) { {"drop-densest-as-needed", no_argument, &additional[A_DROP_DENSEST_AS_NEEDED], 1}, {"drop-fraction-as-needed", no_argument, &additional[A_DROP_FRACTION_AS_NEEDED], 1}, {"drop-smallest-as-needed", no_argument, &additional[A_DROP_SMALLEST_AS_NEEDED], 1}, + {"grid-low-zooms", no_argument, &additional[A_GRID_LOW_ZOOMS], 1}, {"no-line-simplification", no_argument, &prevent[P_SIMPLIFY], 1}, {"simplify-only-low-zooms", no_argument, &prevent[P_SIMPLIFY_LOW], 1}, diff --git a/man/tippecanoe.1 b/man/tippecanoe.1 index 14f73ce..c3ad2ed 100644 --- a/man/tippecanoe.1 +++ b/man/tippecanoe.1 @@ -160,6 +160,8 @@ which may not be what you want. \-ad or \-\-drop\-fraction\-as\-needed: Dynamically drop some fraction of features from each zoom level to keep large tiles under the 500K size limit. (This is like \fB\fC\-pd\fR but applies to the entire zoom level, not to each tile.) .IP \(bu 2 \-an or \-\-drop\-smallest\-as\-needed: Dynamically drop the smallest features (physically smallest: the shortest lines or the smallest polygons) from each zoom level to keep large tiles under the 500K size limit. This option will not work for point features. +.IP \(bu 2 +\-aL or \-\-grid\-low\-zooms: At all zoom levels below \fImaxzoom\fP, snap all lines and polygons to a stairstep grid instead of allowing diagonals. You will also want to specify a tile resolution, probably \fB\fC\-D8\fR\&. This option provides a way to display continuous parcel, gridded, or binned data at low zooms without overwhelming the tiles with tiny polygons, since features will either get stretched out to the grid unit or lost entirely, depending on how they happened to be aligned in the original data. .RE .SS Doing less .RS diff --git a/options.hpp b/options.hpp index 9ce98fb..8b5f66b 100644 --- a/options.hpp +++ b/options.hpp @@ -12,6 +12,7 @@ #define A_DROP_DENSEST_AS_NEEDED ((int) 's') #define A_DROP_FRACTION_AS_NEEDED ((int) 'd') #define A_DROP_SMALLEST_AS_NEEDED ((int) 'n') +#define A_GRID_LOW_ZOOMS ((int) 'L') #define P_SIMPLIFY ((int) 's') #define P_SIMPLIFY_LOW ((int) 'S') diff --git a/tile.cpp b/tile.cpp index 82f8625..a494330 100644 --- a/tile.cpp +++ b/tile.cpp @@ -443,12 +443,16 @@ void *partial_feature_worker(void *v) { int line_detail = (*partials)[i].line_detail; int maxzoom = (*partials)[i].maxzoom; + if (additional[A_GRID_LOW_ZOOMS] && z < maxzoom) { + geom = stairstep(geom, z, line_detail); + } + double area = 0; if (t == VT_POLYGON) { area = get_area(geom, 0, geom.size()); } - if ((t == VT_LINE || t == VT_POLYGON) && !(prevent[P_SIMPLIFY] || (z == maxzoom && prevent[P_SIMPLIFY_LOW]))) { + if ((t == VT_LINE || t == VT_POLYGON) && !(prevent[P_SIMPLIFY] || (z == maxzoom && prevent[P_SIMPLIFY_LOW]) || (z < maxzoom && additional[A_GRID_LOW_ZOOMS]))) { if (1 /* !reduced */) { // XXX why did this not simplify if reduced? if (t == VT_LINE) { geom = remove_noop(geom, t, 32 - z - line_detail); @@ -912,7 +916,7 @@ bool find_common_edges(std::vector &partials, int z, int line_detail, d dv[i].op = VT_LINETO; } } - if (!(prevent[P_SIMPLIFY] || (z == maxzoom && prevent[P_SIMPLIFY_LOW]))) { + 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, 3); } else { simplified_arcs[ai->second] = dv; @@ -1513,7 +1517,7 @@ long long write_tile(FILE *geoms, long long *geompos_in, char *metabase, char *s bool reduced = false; if (t == VT_POLYGON) { - if (!prevent[P_TINY_POLYGON_REDUCTION]) { + if (!prevent[P_TINY_POLYGON_REDUCTION] && !additional[A_GRID_LOW_ZOOMS]) { geom = reduce_tiny_poly(geom, z, line_detail, &reduced, &accum_area); } has_polygons = true;