From f294e2af8a52e6bd6f83528a2f575065ab80d5c9 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Fri, 25 Aug 2023 12:22:55 -0700 Subject: [PATCH] Treat tiny polygon holes as dust again, but accumulated separately now --- geometry.cpp | 30 +++++++++++++++++++++++++----- geometry.hpp | 2 +- overzoom.cpp | 2 +- tile.cpp | 4 ++-- 4 files changed, 29 insertions(+), 9 deletions(-) diff --git a/geometry.cpp b/geometry.cpp index a080012a..fb326455 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -186,7 +186,7 @@ void check_polygon(drawvec &geom) { } } -drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area, serial_feature *this_feature, serial_feature *tiny_feature) { +drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area, double *accum_hole, serial_feature *this_feature, serial_feature *tiny_feature) { drawvec out; const double pixel = (1LL << (32 - detail - z)) * (double) tiny_polygon_size; bool includes_real = false; @@ -222,7 +222,7 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double // OR it is an inner ring and we haven't output an outer ring for it to be // cut out of, so we are just subtracting its area from the tiny polygon // rather than trying to deal with it geometrically - if ((area > 0 && area <= pixel * pixel) || (area < 0 && !included_last_outer)) { + if ((area > 0 && area <= pixel * pixel) || (area < 0 && -area <= pixel * pixel && !included_last_outer)) { // printf("area is only %f vs %lld so using square\n", area, pixel * pixel); *accum_area += area; @@ -243,10 +243,30 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double included_last_outer = false; } } + + // this is a tiny hole, and we have a real outer ring that we can cut it out of, + // so accumulate some hole, and if we have enough to be worth cutting, cut it out. + // + // these hole areas are accumulated separately rather than just decrementing + // the accumulated area, because otherwise it is possible to accumulate enough + // negative area to lose entire outer rings in the process of getting back up to 0. + else if (area < 0 && -area <= pixel * pixel && included_last_outer) { + *accum_hole -= area; + if (*accum_hole > pixel * pixel) { + // XXX use centroid; + + out.push_back(draw(VT_MOVETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2)); + out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2 + pixel, geom[i].y - pixel / 2)); + out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2 + pixel, geom[i].y - pixel / 2 + pixel)); + out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2 + pixel)); + out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2)); + includes_dust = true; + + *accum_hole -= pixel * pixel; + } + } + // i.e., this ring is large enough that it gets to represent itself - // or it is a tiny hole out of a real polygon, which we are still treating - // as a real geometry because otherwise we can accumulate enough tiny holes - // that we will drop the next several outer rings getting back up to 0. else { // printf("area is %f so keeping instead of %lld\n", area, pixel * pixel); diff --git a/geometry.hpp b/geometry.hpp index 50a86be1..637e4bb6 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -66,7 +66,7 @@ drawvec clip_point(drawvec &geom, int z, long long buffer); drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip); 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, serial_feature *this_feature, serial_feature *tiny_feature); +drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double *accum_area, double *accum_hole, serial_feature *this_feature, serial_feature *tiny_feature); int clip(double *x0, double *y0, double *x1, double *y1, double xmin, double ymin, double xmax, double ymax); drawvec clip_lines(drawvec &geom, int z, long long buffer); drawvec stairstep(drawvec &geom, int z, int detail); diff --git a/overzoom.cpp b/overzoom.cpp index b22399ce..a4b18b83 100644 --- a/overzoom.cpp +++ b/overzoom.cpp @@ -11,7 +11,7 @@ extern char *optarg; extern int optind; int detail = 12; // tippecanoe-style: mvt extent == 1 << detail -int buffer = 5; // tippecanoe-style: mvt buffer == extent * buffer / 256; +int buffer = 5; // tippecanoe-style: mvt buffer == extent * buffer / 256; std::set keep; diff --git a/tile.cpp b/tile.cpp index 950fb2b7..666e7f95 100644 --- a/tile.cpp +++ b/tile.cpp @@ -1914,7 +1914,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch int line_detail; for (line_detail = detail; line_detail >= min_detail || line_detail == detail; line_detail--, oprogress = 0) { long long count = 0; - double accum_area = 0; + double accum_area = 0, accum_hole = 0; double fraction_accum = 0; @@ -2185,7 +2185,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch bool reduced = false; if (sf.t == VT_POLYGON) { if (!prevent[P_TINY_POLYGON_REDUCTION] && !additional[A_GRID_LOW_ZOOMS]) { - sf.geometry = reduce_tiny_poly(sf.geometry, z, line_detail, &reduced, &accum_area, &sf, &tiny_feature); + sf.geometry = reduce_tiny_poly(sf.geometry, z, line_detail, &reduced, &accum_area, &accum_hole, &sf, &tiny_feature); if (reduced) { strategy->tiny_polygons++; }