From 67cb8c566f2a00b099d10864cf4734103b379e89 Mon Sep 17 00:00:00 2001 From: Erica Fischer Date: Fri, 20 Oct 2023 08:40:59 -0700 Subject: [PATCH] Don't try to revive continous polygons if they collapse away --- geometry.cpp | 60 +++++++++++++++++++++++++++++++++++++--------------- geometry.hpp | 3 ++- tile.cpp | 12 ++++++++--- 3 files changed, 54 insertions(+), 21 deletions(-) diff --git a/geometry.cpp b/geometry.cpp index d212113ef..b4e004b7d 100644 --- a/geometry.cpp +++ b/geometry.cpp @@ -21,6 +21,8 @@ #include "errors.hpp" #include "projection.hpp" +bool is_shared_node(draw d, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos); + drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, long long *bbox, unsigned initial_x, unsigned initial_y) { drawvec out; @@ -176,7 +178,28 @@ void check_polygon(drawvec &geom) { } } -drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *still_needs_simplification, bool *reduced_away, double *accum_area, serial_feature *this_feature, serial_feature *tiny_feature) { +bool can_be_dust(drawvec const &geom, size_t i, size_t j, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos) { + if (!prevent[P_SIMPLIFY_SHARED_NODES]) { + return true; + } + + size_t count = 0; + for (; i < j; i++) { + if (is_shared_node(geom[i], z, tx, ty, shared_nodes_map, nodepos)) { + count++; + + if (count >= 4) { + // the three fixed nodes of this feature, + // plus some intersection with some other feature + return false; + } + } + } + + return true; +} + +drawvec reduce_tiny_poly(drawvec &geom, int z, int tx, int ty, int detail, bool *still_needs_simplification, bool *reduced_away, double *accum_area, serial_feature *this_feature, serial_feature *tiny_feature, struct node *shared_nodes_map, size_t nodepos) { drawvec out; const double pixel = (1LL << (32 - detail - z)) * (double) tiny_polygon_size; bool includes_real = false; @@ -213,7 +236,8 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *still_needs_sim // 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 (can_be_dust(geom, i, j, z, tx, ty, shared_nodes_map, nodepos) && + ((area > 0 && area <= pixel * pixel) || (area < 0 && !included_last_outer))) { *accum_area += area; *reduced_away = true; @@ -485,20 +509,7 @@ drawvec impose_tile_boundaries(drawvec &geom, long long extent) { return out; } -bool is_shared_node(draw d, int z, int tx, int ty, drawvec const &shared_nodes, struct node *shared_nodes_map, size_t nodepos) { - // 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(), d); - if (pt != shared_nodes.end() && *pt == d) { - return true; - } - +bool is_shared_node(draw d, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos) { if (nodepos > 0) { // offset to global if (z != 0) { @@ -534,7 +545,22 @@ drawvec simplify_lines(drawvec &geom, int z, int tx, int ty, int detail, bool ma } if (prevent[P_SIMPLIFY_SHARED_NODES]) { - if (is_shared_node(geom[i], z, tx, ty, shared_nodes, shared_nodes_map, nodepos)) { + // 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. + + // nodes introduced at the tile edge + 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; + } + + // global shared nodes + if (is_shared_node(geom[i], z, tx, ty, shared_nodes_map, nodepos)) { geom[i].necessary = true; } } diff --git a/geometry.hpp b/geometry.hpp index 5da7cdf33..777cb0555 100644 --- a/geometry.hpp +++ b/geometry.hpp @@ -72,7 +72,7 @@ drawvec remove_noop(drawvec geom, int type, int shift); drawvec clip_point(drawvec &geom, int z, long long buffer); drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip, bool try_scaling); drawvec close_poly(drawvec &geom); -drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *still_needs_simplification, bool *reduced_away, double *accum_area, serial_feature *this_feature, serial_feature *tiny_feature); +drawvec reduce_tiny_poly(drawvec &geom, int z, int tx, int ty, int detail, bool *still_needs_simplification, bool *reduced_away, double *accum_area, serial_feature *this_feature, serial_feature *tiny_feature, struct node *shared_nodes_map, size_t nodepos); int clip(long long *x0, long long *y0, long long *x1, long long *y1, long long xmin, long long ymin, long long xmax, long long ymax); drawvec clip_lines(drawvec &geom, int z, long long buffer); drawvec stairstep(drawvec &geom, int z, int detail); @@ -97,6 +97,7 @@ drawvec clip_point(drawvec &geom, long long x1, long long y1, long long x2, long void visvalingam(drawvec &ls, size_t start, size_t end, double threshold, size_t retain); int pnpoly(const drawvec &vert, size_t start, size_t nvert, long long testx, long long testy); double distance_from_line(long long point_x, long long point_y, long long segA_x, long long segA_y, long long segB_x, long long segB_y); +bool can_be_dust(drawvec const &geom, size_t i, size_t j, int z, int tx, int ty, struct node *shared_nodes_map, size_t nodepos); std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int ny, int detail, int buffer, std::set const &keep, bool do_compress); diff --git a/tile.cpp b/tile.cpp index d94b1493a..92099a2c6 100644 --- a/tile.cpp +++ b/tile.cpp @@ -609,9 +609,12 @@ void *partial_feature_worker(void *v) { signed char t = (*partials)[i].t; int z = (*partials)[i].z; + int tx = (*partials)[i].tx; + int ty = (*partials)[i].ty; int out_detail = (*partials)[i].extra_detail; drawvec geom = (*partials)[i].geoms[0]; + drawvec before_scaling = (*partials)[i].geoms[0]; to_tile_scale(geom, z, out_detail); if (t == VT_POLYGON) { @@ -625,8 +628,11 @@ void *partial_feature_worker(void *v) { check_polygon(geom); } - if (geom.size() < 3) { - if (area > 0) { + if (geom.size() < 4) { + // Don't try to revive a continuous polygon, because we should be + // able to rely on its three fixed points to keep it alive if it + // actually has any area at this zoom level. + if (area > 0 && can_be_dust(before_scaling, 0, before_scaling.size(), z, tx, ty, a->shared_nodes_map, a->nodepos)) { // area is in world coordinates, calculated before scaling down geom = revive_polygon(before, area, z, out_detail); } else { @@ -2207,7 +2213,7 @@ long long write_tile(decompressor *geoms, std::atomic *geompos_in, ch bool prevent_tiny = prevent[P_TINY_POLYGON_REDUCTION] || (prevent[P_TINY_POLYGON_REDUCTION_AT_MAXZOOM] && z == maxzoom); if (!prevent_tiny && !additional[A_GRID_LOW_ZOOMS]) { - sf.geometry = reduce_tiny_poly(sf.geometry, z, line_detail, &still_need_simplification_after_reduction, &simplified_away_by_reduction, &accum_area, &sf, &tiny_feature); + sf.geometry = reduce_tiny_poly(sf.geometry, z, tx, ty, line_detail, &still_need_simplification_after_reduction, &simplified_away_by_reduction, &accum_area, &sf, &tiny_feature, shared_nodes_map, nodepos); if (simplified_away_by_reduction) { strategy->tiny_polygons++; }