Add point retention multiplier (#179)

* Add an option to retain N times as many points as usual at each zoom

* Tests for point multipler with specified and guessed maxzooms

* Work in progress on inverse spatial ordering

* Fix inverse spatial feature order

* --reorder was depending on a feature index that wasn't being preserved

* Separate ordering by feature_minzoom from ordering inverse-spatially

* Add a test for the inverse spatial ordering

* Store the basezoom/droprate/multiplier decisions in tileset metadata

* Progress on adding filters to tippecanoe-overzoom

* Type promotion for comparison

* Look up the attribute value for ordering

* Add test of thinning and ordering features

* Plumb tippecanoe_decisions metadata through pmtiles

* Be careful not to put infinities in JSON

* Fix accidental dropping in what is meant to preserve sparse points

* Start distinguishing true, false, and null in expressions

* Most of the type conversions

* Add boolean conversions

* Literals and conjunctions

* Add filtering to tippecanoe-overzoom

* Add a test of filtering in overzoom

* Fix boolean conjunctions

* Handle the combination of cluster size and filtering

* Rework dot dropping to reconcile density threshold and multiplier

* Revert "Rework dot dropping to reconcile density threshold and multiplier"

This reverts commit f253a66382.

* Retain points by multiplier within each tile, not in global probability

* Test that intends to verify that the multiplier is reversible

* Get the test to detect the discrepancy

* Mark the start of multiplier clusters with a magic attribute

* Add string-contains

* Add in and ni operators

* Revert "Look up the attribute value for ordering"

This reverts commit 56bc73e49a.

* Revert "Type promotion for comparison"

This reverts commit 6f3256f5af.

* Make number formatting in tippecanoe_decisions consistent

* Revert "Add a test for the inverse spatial ordering"

This reverts commit c8047de9ab.

* Revert "Separate ordering by feature_minzoom from ordering inverse-spatially"

This reverts commit 35b19a223c.

* Revert "Fix inverse spatial feature order"

This reverts commit 5978ecdb44.

* Revert "Work in progress on inverse spatial ordering"

This reverts commit fdf230f632.

* Somehow missed the tests associated with that last revert

* Round-robin assign attributes to partials from across the multiplier

* Count the multiplier separately in each layer

* Fix distribution of accumulated attribute across multiplier features

* Update changelog, version, and docs

* Add "is null" and "isnt null" expressions

* Update interpretation of FSL expressions to pass the tests

* Test to assert that polygons are unaffected by the multiplier

* Clean up and comment

* Remove accidental unused case
This commit is contained in:
Erica Fischer
2024-01-18 15:56:09 -08:00
committed by GitHub
parent e8ca6c6de3
commit 5d92a17193
56 changed files with 83462 additions and 113 deletions
+87 -27
View File
@@ -7,6 +7,7 @@
#include "errors.hpp"
#include "compression.hpp"
#include "mvt.hpp"
#include "evaluator.hpp"
static std::vector<std::pair<double, double>> clip_poly1(std::vector<std::pair<double, double>> &geom,
long long minx, long long miny, long long maxx, long long maxy,
@@ -754,7 +755,8 @@ static std::vector<std::pair<double, double>> clip_poly1(std::vector<std::pair<d
std::string overzoom(std::string s, int oz, int ox, int oy, int nz, int nx, int ny,
int detail, int buffer, std::set<std::string> const &keep, bool do_compress,
std::vector<std::pair<unsigned, unsigned>> *next_overzoomed_tiles) {
std::vector<std::pair<unsigned, unsigned>> *next_overzoomed_tiles,
bool demultiply, json_object *filter) {
mvt_tile tile;
try {
@@ -768,12 +770,49 @@ std::string overzoom(std::string s, int oz, int ox, int oy, int nz, int nx, int
exit(EXIT_PROTOBUF);
}
return overzoom(tile, oz, ox, oy, nz, nx, ny, detail, buffer, keep, do_compress, next_overzoomed_tiles);
return overzoom(tile, oz, ox, oy, nz, nx, ny, detail, buffer, keep, do_compress, next_overzoomed_tiles, demultiply, filter);
}
struct tile_feature {
drawvec geom;
int t;
bool has_id;
unsigned long long id;
std::vector<unsigned> tags;
mvt_layer const *layer;
};
void feature_out(tile_feature const &feature, mvt_layer &outlayer, std::set<std::string> const &keep) {
// Add geometry to output feature
mvt_feature outfeature;
outfeature.type = feature.t;
for (auto const &g : feature.geom) {
outfeature.geometry.emplace_back(g.op, g.x, g.y);
}
// ID and attributes, if it didn't get clipped away
if (outfeature.geometry.size() > 0) {
if (feature.has_id) {
outfeature.has_id = true;
outfeature.id = feature.id;
}
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
if (keep.size() == 0 || keep.find(feature.layer->keys[feature.tags[i]]) != keep.end()) {
outlayer.tag(outfeature, feature.layer->keys[feature.tags[i]], feature.layer->values[feature.tags[i + 1]]);
}
}
outlayer.features.push_back(outfeature);
}
}
std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int ny,
int detail, int buffer, std::set<std::string> const &keep, bool do_compress,
std::vector<std::pair<unsigned, unsigned>> *next_overzoomed_tiles) {
std::vector<std::pair<unsigned, unsigned>> *next_overzoomed_tiles,
bool demultiply, json_object *filter) {
mvt_tile outtile;
for (auto const &layer : tile.layers) {
@@ -788,8 +827,37 @@ std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int
outlayer.version = layer.version;
outlayer.extent = 1LL << det;
for (auto const &feature : layer.features) {
mvt_feature outfeature;
std::vector<tile_feature> pending_tile_features;
for (auto feature : layer.features) {
bool flush_multiplier_cluster = false;
if (demultiply) {
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
if (layer.keys[feature.tags[i]] == "tippecanoe:retain_points_multiplier_first") {
mvt_value v = layer.values[feature.tags[i + 1]];
if (v.type == mvt_bool && v.numeric_value.bool_value) {
flush_multiplier_cluster = true;
feature.tags.erase(feature.tags.begin() + i, feature.tags.begin() + i + 2);
break;
}
}
}
} else {
flush_multiplier_cluster = true;
}
if (flush_multiplier_cluster) {
if (pending_tile_features.size() > 0) {
feature_out(pending_tile_features[0], outlayer, keep);
pending_tile_features.clear();
}
}
std::set<std::string> exclude_attributes;
if (!evaluate(feature, layer, filter, exclude_attributes, nz)) {
continue;
}
drawvec geom;
int t = feature.type;
@@ -862,29 +930,20 @@ std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int
geom = close_poly(geom);
}
// Add geometry to output feature
tile_feature tf;
tf.geom = geom;
tf.t = t;
tf.has_id = feature.has_id;
tf.id = feature.id;
tf.tags = feature.tags;
tf.layer = &layer;
outfeature.type = t;
for (auto const &g : geom) {
outfeature.geometry.emplace_back(g.op, g.x, g.y);
}
pending_tile_features.push_back(tf);
}
// ID and attributes, if it didn't get clipped away
if (outfeature.geometry.size() > 0) {
if (feature.has_id) {
outfeature.has_id = true;
outfeature.id = feature.id;
}
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
if (keep.size() == 0 || keep.find(layer.keys[feature.tags[i]]) != keep.end()) {
outlayer.tag(outfeature, layer.keys[feature.tags[i]], layer.values[feature.tags[i + 1]]);
}
}
outlayer.features.push_back(outfeature);
}
if (pending_tile_features.size() > 0) {
feature_out(pending_tile_features[0], outlayer, keep);
pending_tile_features.clear();
}
if (outlayer.features.size() > 0) {
@@ -905,7 +964,8 @@ std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int
for (size_t y = 0; y < 2; y++) {
std::string child = overzoom(outtile, nz, nx, ny,
nz + 1, nx * 2 + x, ny * 2 + y,
detail, buffer, keep, false, NULL);
detail, buffer, keep, false, NULL,
demultiply, filter);
if (child.size() > 0) {
next_overzoomed_tiles->emplace_back(nx * 2 + x, ny * 2 + y);
}