More work on plumbing attribute accumulation through (#263)

* Plumb bounding boxes through potential intersections

* Quick bbox reject for bins that can't possibly intersect

* Inching toward attribute accumulation in megatile handling

* Some sort of test for how all these things interact with each other.

Automatic numeric attribute accumulation does *not* apply to attributes
that have an explicit attribute accumulator set, because the order of
operations is too messy and weird

* More sketching

* More sketching

* Actually do some accumulation

* Put all that behind an --accumulate-numeric flag

* Use the same attribute accumulation logic in binning as in megatiles

* Fix backwards conditional

* Add means, but somehow I have some counts of 0

* Handle aggregated attributes with no base attribute in the feature

* Checkpoint before I break everything

* Found a flaw, now to debug

* Fix a typo that broke accumulation

* Add binning tests

* Make sure IDs make it through on the bins

* Fix count/mean accumulation

* Make the numeric accumulation prefix configurable

* Make sure the accumulate test still works with a different prefix

* Forgot to update this test

* More testing to make sure cluster sizes make it all the way through

* Fix neglected --accumulate-attribute when binning

* Mark unexercised attribute accumulation cases as "can't happen"

* Factor out numeric preservation

* Attrs with the accumulation prefix are just preserved, not accumulated

* Test behavior of prefixed attributes

* Plumbing for exclude and exclude-prefix

* Implement and test attribute prefix stripping in overzoom

* Update version and changelog

* For debugging, make an attribute list of source feature IDs

* Revert "For debugging, make an attribute list of source feature IDs"

This reverts commit 65fc99c9d1.
This commit is contained in:
Erica Fischer
2024-09-20 15:30:29 -07:00
committed by GitHub
parent 534242887d
commit c5f2f0da34
23 changed files with 2386 additions and 3919 deletions
+271 -136
View File
@@ -1076,12 +1076,17 @@ bool pnpoly_mp(std::vector<mvt_geometry> const &geom, long long x, long long y)
}
std::string overzoom(std::vector<input_tile> const &tiles, int nz, int nx, int ny,
int detail, int buffer, std::set<std::string> const &keep, bool do_compress,
int detail, int buffer,
std::set<std::string> const &keep,
std::set<std::string> const &exclude,
std::vector<std::string> const &exclude_prefix,
bool do_compress,
std::vector<std::pair<unsigned, unsigned>> *next_overzoomed_tiles,
bool demultiply, json_object *filter, bool preserve_input_order,
std::unordered_map<std::string, attribute_op> const &attribute_accum,
std::vector<std::string> const &unidecode_data, double simplification,
double tiny_polygon_size, std::vector<mvt_layer> const &bins) {
double tiny_polygon_size, std::vector<mvt_layer> const &bins,
std::string const &accumulate_numeric) {
std::vector<source_tile> decoded;
for (auto const &t : tiles) {
@@ -1107,9 +1112,10 @@ std::string overzoom(std::vector<input_tile> const &tiles, int nz, int nx, int n
decoded.push_back(out);
}
return overzoom(decoded, nz, nx, ny, detail, buffer, keep, do_compress, next_overzoomed_tiles, demultiply, filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, bins);
return overzoom(decoded, nz, nx, ny, detail, buffer, keep, exclude, exclude_prefix, do_compress, next_overzoomed_tiles, demultiply, filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, bins, accumulate_numeric);
}
// like a minimal serial_feature, but with mvt_feature-style attributes
struct tile_feature {
drawvec geom;
int t;
@@ -1120,7 +1126,147 @@ struct tile_feature {
size_t seq = 0;
};
static void feature_out(std::vector<tile_feature> const &features, mvt_layer &outlayer, std::set<std::string> const &keep, std::unordered_map<std::string, attribute_op> const &attribute_accum, std::shared_ptr<std::string> const &tile_stringpool) {
static void add_mean(mvt_feature &feature, mvt_layer &layer, std::string const &accumulate_numeric) {
std::string accumulate_numeric_colon = accumulate_numeric + ":";
std::unordered_map<std::string, size_t> attributes;
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
std::string const &key = layer.keys[feature.tags[i]];
if (starts_with(key, accumulate_numeric_colon)) {
attributes.emplace(key, i);
}
}
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
std::string accumulate_numeric_sum_colon = accumulate_numeric + ":sum:";
std::string const &key = layer.keys[feature.tags[i]];
if (starts_with(key, accumulate_numeric_sum_colon)) {
std::string trunc = key.substr(accumulate_numeric_sum_colon.size());
auto const f = attributes.find(accumulate_numeric + ":count:" + trunc);
if (f != attributes.end()) {
mvt_value const &sum = layer.values[feature.tags[i + 1]];
mvt_value const &count = layer.values[feature.tags[f->second + 1]];
double count_val = mvt_value_to_double(count);
if (count_val <= 0) {
fprintf(stderr, "can't happen: count is %s (type %d)\n", count.toString().c_str(), count.type);
exit(EXIT_IMPOSSIBLE);
}
mvt_value mean;
mean.type = mvt_double;
mean.numeric_value.double_value = mvt_value_to_double(sum) / count_val;
layer.tag(feature, accumulate_numeric + ":mean:" + trunc, mean);
}
}
}
};
// accumulate :sum:, :min:, :max:, and :count: versions of the specified attribute
static void preserve_numeric(const std::string &key, const mvt_value &val, // numeric attribute being accumulated
std::vector<std::string> &full_keys, // keys of feature being accumulated onto
std::vector<serial_val> &full_values, // values of features being accumulated onto
const std::string &accumulate_numeric, // prefix of accumulations
std::set<std::string> &keys, // key presence in the source feature
std::map<std::string, size_t> &numeric_out_field, // key index in the output feature
std::unordered_map<std::string, accum_state> &attribute_accum_state // accumulation state for preserve_attribute()
) {
// If this is a numeric attribute, but there is also a prefix:sum (etc.) for the
// same attribute, we want to use that one instead of this one.
for (auto const &op : numeric_operations) {
std::string compound_key = accumulate_numeric + ":" + op.first + ":" + key;
auto compound_found = keys.find(compound_key);
if (compound_found != keys.end()) {
// found, so skip this one
} else {
// not found, so accumulate this one
// if this is already prefixed, strip off the prefix
// if it is the right one, and skip the attribute if
// it is the wrong one.
std::string outkey = key;
if (starts_with(outkey, accumulate_numeric + ":")) {
std::string prefix = accumulate_numeric + ":" + op.first + ":";
if (starts_with(outkey, prefix)) {
outkey = outkey.substr(prefix.size());
} else {
continue; // to next operation
}
}
// and then put it back on for the output field
std::string prefixed = accumulate_numeric + ":" + op.first + ":" + outkey;
// Does it exist in the output feature already?
auto prefixed_attr = numeric_out_field.find(prefixed);
if (prefixed_attr == numeric_out_field.end()) {
// No? Does it exist unprefixed in the output feature already?
auto out_attr = numeric_out_field.find(outkey);
if (out_attr == numeric_out_field.end()) {
// not present at all, so copy our value to the prefixed output
numeric_out_field.emplace(prefixed, full_keys.size());
full_keys.push_back(prefixed);
if (op.second == op_count) {
serial_val sv;
sv.type = mvt_double;
sv.s = "1";
full_values.push_back(sv);
} else {
full_values.push_back(mvt_value_to_serial_val(val));
}
} else {
// exists unprefixed, so copy it, and then accumulate on our value
numeric_out_field.emplace(prefixed, full_keys.size());
full_keys.push_back(prefixed);
if (op.second == op_count) {
serial_val sv;
sv.type = mvt_double;
sv.s = "1";
full_values.push_back(sv);
} else {
full_values.push_back(full_values[out_attr->second]);
}
preserve_attribute(op.second, prefixed, mvt_value_to_serial_val(val), full_keys, full_values, attribute_accum_state);
}
} else {
// exists, so accumulate on our value
preserve_attribute(op.second, prefixed, mvt_value_to_serial_val(val), full_keys, full_values, attribute_accum_state);
}
}
}
}
static bool should_keep(std::string const &key,
std::set<std::string> const &keep,
std::set<std::string> const &exclude,
std::vector<std::string> const &exclude_prefix) {
if (keep.size() == 0 || keep.find(key) != keep.end()) {
if (exclude.find(key) != exclude.end()) {
return false;
}
for (auto const &prefix : exclude_prefix) {
if (starts_with(key, prefix)) {
return false;
}
}
return true;
}
return false;
}
static void feature_out(std::vector<tile_feature> const &features, mvt_layer &outlayer,
std::set<std::string> const &keep,
std::set<std::string> const &exclude,
std::vector<std::string> const &exclude_prefix,
std::unordered_map<std::string, attribute_op> const &attribute_accum,
std::shared_ptr<std::string> const &tile_stringpool,
std::string const &accumulate_numeric) {
// Add geometry to output feature
mvt_feature outfeature;
@@ -1139,7 +1285,7 @@ static void feature_out(std::vector<tile_feature> const &features, mvt_layer &ou
outfeature.seq = features[0].seq;
if (attribute_accum.size() > 0) {
if (attribute_accum.size() > 0 || accumulate_numeric.size() > 0) {
// convert the attributes of the output feature
// from mvt_value to serial_val so they can have
// attributes from the other features of the
@@ -1148,16 +1294,23 @@ static void feature_out(std::vector<tile_feature> const &features, mvt_layer &ou
std::unordered_map<std::string, accum_state> attribute_accum_state;
std::vector<std::string> full_keys;
std::vector<serial_val> full_values;
std::map<std::string, size_t> numeric_out_field;
for (size_t i = 0; i + 1 < features[0].tags.size(); i += 2) {
auto f = attribute_accum.find(features[0].layer->keys[features[0].tags[i]]);
const std::string &key = features[0].layer->keys[features[0].tags[i]];
auto f = attribute_accum.find(key);
if (f != attribute_accum.end()) {
// this attribute has an accumulator, so convert it
full_keys.push_back(features[0].layer->keys[features[0].tags[i]]);
full_values.push_back(mvt_value_to_serial_val(features[0].layer->values[features[0].tags[i + 1]]));
} else if (accumulate_numeric.size() > 0 && features[0].layer->values[features[0].tags[i + 1]].is_numeric()) {
// convert numeric for accumulation
numeric_out_field.emplace(key, full_keys.size());
full_keys.push_back(key);
full_values.push_back(mvt_value_to_serial_val(features[0].layer->values[features[0].tags[i + 1]]));
} else {
// otherwise just tag it directly onto the output feature
if (keep.size() == 0 || keep.find(features[0].layer->keys[features[0].tags[i]]) != keep.end()) {
if (should_keep(features[0].layer->keys[features[0].tags[i]], keep, exclude, exclude_prefix)) {
outlayer.tag(outfeature, features[0].layer->keys[features[0].tags[i]], features[0].layer->values[features[0].tags[i + 1]]);
}
}
@@ -1168,13 +1321,27 @@ static void feature_out(std::vector<tile_feature> const &features, mvt_layer &ou
// features that will not
for (size_t i = 1; i < features.size(); i++) {
std::set<std::string> keys;
for (size_t j = 0; j + 1 < features[i].tags.size(); j += 2) {
std::string key = features[i].layer->keys[features[i].tags[j]];
const std::string &key = features[i].layer->keys[features[i].tags[j]];
keys.insert(key);
}
for (size_t j = 0; j + 1 < features[i].tags.size(); j += 2) {
const std::string &key = features[i].layer->keys[features[i].tags[j]];
auto f = attribute_accum.find(key);
if (f != attribute_accum.end()) {
serial_val val = mvt_value_to_serial_val(features[i].layer->values[features[i].tags[j + 1]]);
preserve_attribute(f->second, key, val, full_keys, full_values, attribute_accum_state);
} else if (accumulate_numeric.size() > 0) {
const mvt_value &val = features[i].layer->values[features[i].tags[j + 1]];
if (val.is_numeric()) {
preserve_numeric(key, val, full_keys, full_values,
accumulate_numeric,
keys, numeric_out_field, attribute_accum_state);
}
}
}
}
@@ -1183,13 +1350,17 @@ static void feature_out(std::vector<tile_feature> const &features, mvt_layer &ou
// and tag them onto the output feature
for (size_t i = 0; i < full_keys.size(); i++) {
if (keep.size() == 0 || keep.find(full_keys[i]) != keep.end()) {
if (should_keep(full_keys[i], keep, exclude, exclude_prefix)) {
outlayer.tag(outfeature, full_keys[i], stringified_to_mvt_value(full_values[i].type, full_values[i].s.c_str(), tile_stringpool));
}
}
if (accumulate_numeric.size() > 0) {
add_mean(outfeature, outlayer, accumulate_numeric);
}
} else {
for (size_t i = 0; i + 1 < features[0].tags.size(); i += 2) {
if (keep.size() == 0 || keep.find(features[0].layer->keys[features[0].tags[i]]) != keep.end()) {
if (should_keep(features[0].layer->keys[features[0].tags[i]], keep, exclude, exclude_prefix)) {
outlayer.tag(outfeature, features[0].layer->keys[features[0].tags[i]], features[0].layer->values[features[0].tags[i + 1]]);
}
}
@@ -1214,9 +1385,11 @@ struct index_event {
} kind;
size_t layer;
size_t feature;
long long xmin, ymin, xmax, ymax;
index_event(unsigned long long where_, index_event_kind kind_, size_t layer_, size_t feature_)
: where(where_), kind(kind_), layer(layer_), feature(feature_) {
index_event(unsigned long long where_, index_event_kind kind_, size_t layer_, size_t feature_,
long long xmin_, long long ymin_, long long xmax_, long long ymax_)
: where(where_), kind(kind_), layer(layer_), feature(feature_), xmin(xmin_), ymin(ymin_), xmax(xmax_), ymax(ymax_) {
}
bool operator<(const index_event &ie) const {
@@ -1287,7 +1460,29 @@ void get_quadkey_bounds(long long xmin, long long ymin, long long xmax, long lon
}
}
mvt_tile assign_to_bins(mvt_tile const &features, std::vector<mvt_layer> const &bins, int z, int x, int y, int detail) {
static bool bbox_intersects(long long x1min, long long y1min, long long x1max, long long y1max,
long long x2min, long long y2min, long long x2max, long long y2max) {
if (x1max < x2min) {
return false;
}
if (x2max < x1min) {
return false;
}
if (y1max < y2min) {
return false;
}
if (y2max < y1min) {
return false;
}
return true;
}
mvt_tile assign_to_bins(mvt_tile const &features, std::vector<mvt_layer> const &bins, int z, int x, int y, int detail,
std::unordered_map<std::string, attribute_op> const &attribute_accum,
std::string const &accumulate_numeric,
std::set<std::string> keep,
std::set<std::string> exclude,
std::vector<std::string> exclude_prefix) {
std::vector<index_event> events;
// Index bins
@@ -1298,8 +1493,8 @@ mvt_tile assign_to_bins(mvt_tile const &features, std::vector<mvt_layer> const &
get_bbox(bins[i].features[j].geometry, &xmin, &ymin, &xmax, &ymax, z, x, y, detail);
get_quadkey_bounds(xmin, ymin, xmax, ymax, &start, &end);
events.emplace_back(start, index_event::ENTER, i, j);
events.emplace_back(end, index_event::EXIT, i, j);
events.emplace_back(start, index_event::ENTER, i, j, xmin, ymin, xmax, ymax);
events.emplace_back(end, index_event::EXIT, i, j, xmin, ymin, xmax, ymax);
}
}
@@ -1312,146 +1507,91 @@ mvt_tile assign_to_bins(mvt_tile const &features, std::vector<mvt_layer> const &
if (features.layers[i].features[j].geometry.size() > 0) {
get_bbox(features.layers[i].features[j].geometry, &xmin, &ymin, &xmax, &ymax, z, x, y, detail);
get_quadkey_bounds(xmin, ymin, xmax, ymax, &start, &end);
events.emplace_back(start, index_event::CHECK, i, j);
events.emplace_back(start, index_event::CHECK, i, j, xmin, ymin, xmax, ymax);
}
}
}
std::sort(events.begin(), events.end());
std::set<active_bin> active;
std::vector<size_t> counters; // separate because set items can't be mutated from an iterator
std::vector<std::map<std::string, double>> sums; // separate because set items can't be mutated from an iterator
std::vector<std::map<std::string, double>> maxes; // separate because set items can't be mutated from an iterator
std::vector<std::map<std::string, double>> mins; // separate because set items can't be mutated from an iterator
std::vector<std::map<std::string, double>> counts; // separate because set items can't be mutated from an iterator
mvt_layer outlayer;
outlayer.extent = 1 << detail;
outlayer.version = 2;
outlayer.name = features.layers[0].name;
std::vector<std::vector<tile_feature>> outfeatures;
std::shared_ptr<std::string> tile_stringpool = std::make_shared<std::string>();
for (auto &e : events) {
if (e.kind == index_event::ENTER) {
active_bin a(e.layer, e.feature);
a.xmin = e.xmin;
a.ymin = e.ymin;
a.xmax = e.xmax;
a.ymax = e.ymax;
const mvt_feature &bin = bins[e.layer].features[e.feature];
mvt_feature outfeature;
outfeature.geometry = bin.geometry;
outfeature.type = bin.type;
tile_feature outfeature;
for (auto const &g : bin.geometry) {
outfeature.geom.emplace_back(g.op, g.x, g.y);
}
outfeature.t = bin.type;
outfeature.has_id = bin.has_id;
outfeature.id = bin.id;
a.outfeature = outlayer.features.size();
outfeature.tags = bin.tags;
outfeature.layer = &bins[e.layer];
outfeature.seq = e.feature;
a.counter = counters.size();
counters.push_back(0);
sums.emplace_back();
maxes.emplace_back();
mins.emplace_back();
counts.emplace_back();
a.outfeature = outfeatures.size();
outfeatures.push_back({std::move(outfeature)});
outlayer.features.push_back(std::move(outfeature));
active.insert(std::move(a));
} else if (e.kind == index_event::CHECK) {
auto const &feature = features.layers[e.layer].features[e.feature];
#if 0
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
printf("%s ", features.layers[e.layer].values[feature.tags[i + 1]].toString().c_str());
}
printf(": ");
#endif
for (auto const &a : active) {
auto const &bin = bins[a.layer].features[a.feature];
// XXX do bounding box check first
if (pnpoly_mp(bin.geometry, feature.geometry[0].x, feature.geometry[0].y)) {
#if 0
printf("found: ");
for (size_t i = 0; i + 1 < bin.tags.size(); i += 2) {
printf("%s ", bins[a.first].values[bin.tags[i + 1]].toString().c_str());
}
printf("\n");
#endif
counters[a.counter]++;
for (size_t i = 0; i + 1 < feature.tags.size(); i += 2) {
const mvt_value &val = features.layers[e.layer].values[feature.tags[i + 1]];
const std::string &key = features.layers[e.layer].keys[feature.tags[i]];
if (val.is_numeric()) {
auto sum_attr = sums[a.counter].find(key);
if (sum_attr == sums[a.counter].end()) {
sums[a.counter].emplace(key, 0);
mins[a.counter].emplace(key, std::numeric_limits<double>::infinity());
maxes[a.counter].emplace(key, -std::numeric_limits<double>::infinity());
counts[a.counter].emplace(key, 0);
sum_attr = sums[a.counter].find(key);
}
auto min_attr = mins[a.counter].find(key);
auto max_attr = maxes[a.counter].find(key);
auto count_attr = counts[a.counter].find(key);
double v = mvt_value_to_double(val);
sum_attr->second += v;
count_attr->second += 1;
min_attr->second = std::min(min_attr->second, v);
max_attr->second = std::max(max_attr->second, v);
if (bbox_intersects(e.xmin, e.ymin, e.xmax, e.ymax,
a.xmin, a.ymin, a.xmax, a.ymax)) {
if (pnpoly_mp(bin.geometry, feature.geometry[0].x, feature.geometry[0].y)) {
tile_feature outfeature;
for (auto const &g : feature.geometry) {
outfeature.geom.emplace_back(g.op, g.x, g.y);
}
}
outfeature.t = feature.type;
outfeature.has_id = feature.has_id;
outfeature.id = feature.id;
outfeature.tags = feature.tags;
outfeature.layer = &features.layers[e.layer];
outfeature.seq = e.feature;
outfeatures[a.outfeature].push_back(std::move(outfeature));
break;
break;
}
}
}
} else /* EXIT */ {
auto const &found = active.find({e.layer, e.feature});
if (found != active.end()) {
mvt_feature &outfeature = outlayer.features[found->outfeature];
if (outfeatures[found->outfeature].size() > 1) {
feature_out(outfeatures[found->outfeature], outlayer,
keep, exclude, exclude_prefix, attribute_accum,
tile_stringpool, accumulate_numeric);
mvt_feature &nfeature = outlayer.features.back();
mvt_value val;
val.type = mvt_uint;
val.numeric_value.uint_value = outfeatures[found->outfeature].size() - 1;
if (counters[found->counter] >= 0) {
const mvt_feature &bin = bins[e.layer].features[e.feature];
// copy attributes from the original bin feature
for (size_t i = 0; i + 1 < bin.tags.size(); i += 2) {
outlayer.tag(outfeature, bins[e.layer].keys[bin.tags[i]], bins[e.layer].values[bin.tags[i + 1]]);
std::string attrname;
if (accumulate_numeric.size() == 0) {
attrname = "tippecanoe:count";
} else {
attrname = accumulate_numeric + ":count";
}
// new attribute for number of features assigned to the bin
mvt_value v;
v.type = mvt_uint;
v.numeric_value.uint_value = counters[found->counter];
outlayer.tag(outfeature, "tippecanoe:count", v);
for (auto const &kv : sums[found->counter]) {
mvt_value v_sum;
v_sum.type = mvt_double;
v_sum.numeric_value.double_value = kv.second;
outlayer.tag(outfeature, "tippecanoe:sum:" + kv.first, v_sum);
mvt_value v_count;
v_count.type = mvt_double;
v_count.numeric_value.double_value = counts[found->counter][kv.first];
outlayer.tag(outfeature, "tippecanoe:count:" + kv.first, v_count);
mvt_value v_mean;
v_mean.type = mvt_double;
v_mean.numeric_value.double_value = kv.second / counters[found->counter];
outlayer.tag(outfeature, "tippecanoe:mean:" + kv.first, v_mean);
mvt_value v_min;
v_min.type = mvt_double;
v_min.numeric_value.double_value = mins[found->counter][kv.first];
outlayer.tag(outfeature, "tippecanoe:min:" + kv.first, v_min);
mvt_value v_max;
v_max.type = mvt_double;
v_max.numeric_value.double_value = maxes[found->counter][kv.first];
outlayer.tag(outfeature, "tippecanoe:max:" + kv.first, v_max);
}
} else {
outfeature.geometry.clear();
outlayer.tag(nfeature, attrname, val);
}
active.erase(found);
@@ -1462,29 +1602,23 @@ mvt_tile assign_to_bins(mvt_tile const &features, std::vector<mvt_layer> const &
}
}
#if 0
// crunch out bin features whose geometry we cleared along the way
size_t out = 0;
for (size_t i = 0; i < outlayer.features.size(); i++) {
if (outlayer.features[i].geometry.size() > 0) {
outlayer.features[out++] = outlayer.features[i];
}
}
outlayer.features.resize(out);
#endif
mvt_tile ret;
ret.layers.push_back(outlayer);
return ret;
}
std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int ny,
int detail, int buffer, std::set<std::string> const &keep, bool do_compress,
int detail, int buffer,
std::set<std::string> const &keep,
std::set<std::string> const &exclude,
std::vector<std::string> const &exclude_prefix,
bool do_compress,
std::vector<std::pair<unsigned, unsigned>> *next_overzoomed_tiles,
bool demultiply, json_object *filter, bool preserve_input_order,
std::unordered_map<std::string, attribute_op> const &attribute_accum,
std::vector<std::string> const &unidecode_data, double simplification,
double tiny_polygon_size, std::vector<mvt_layer> const &bins) {
double tiny_polygon_size, std::vector<mvt_layer> const &bins,
std::string const &accumulate_numeric) {
mvt_tile outtile;
std::shared_ptr<std::string> tile_stringpool = std::make_shared<std::string>();
@@ -1610,7 +1744,7 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
if (flush_multiplier_cluster) {
if (pending_tile_features.size() > 0) {
feature_out(pending_tile_features, *outlayer, keep, attribute_accum, tile_stringpool);
feature_out(pending_tile_features, *outlayer, keep, exclude, exclude_prefix, attribute_accum, tile_stringpool, accumulate_numeric);
pending_tile_features.clear();
}
}
@@ -1667,7 +1801,7 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
}
if (pending_tile_features.size() > 0) {
feature_out(pending_tile_features, *outlayer, keep, attribute_accum, tile_stringpool);
feature_out(pending_tile_features, *outlayer, keep, exclude, exclude_prefix, attribute_accum, tile_stringpool, accumulate_numeric);
pending_tile_features.clear();
}
@@ -1699,8 +1833,8 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
std::string child = overzoom(sts,
nz + 1, nx * 2 + x, ny * 2 + y,
detail, buffer, keep, false, NULL,
demultiply, filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, bins);
detail, buffer, keep, exclude, exclude_prefix, false, NULL,
demultiply, filter, preserve_input_order, attribute_accum, unidecode_data, simplification, tiny_polygon_size, bins, accumulate_numeric);
if (child.size() > 0) {
next_overzoomed_tiles->emplace_back(nx * 2 + x, ny * 2 + y);
}
@@ -1710,7 +1844,8 @@ std::string overzoom(std::vector<source_tile> const &tiles, int nz, int nx, int
}
if (bins.size() > 0) {
outtile = assign_to_bins(outtile, bins, nz, nx, ny, detail);
outtile = assign_to_bins(outtile, bins, nz, nx, ny, detail, attribute_accum, accumulate_numeric,
keep, exclude, exclude_prefix);
}
for (ssize_t i = outtile.layers.size() - 1; i >= 0; i--) {