Lazy linear scan of attributes instead of building a map

This commit is contained in:
Erica Fischer
2024-01-24 14:18:50 -08:00
parent 70d99d865a
commit abacc17574
5 changed files with 80 additions and 54 deletions
+1 -1
View File
@@ -920,7 +920,7 @@ std::string overzoom(mvt_tile tile, int oz, int ox, int oy, int nz, int nx, int
} }
std::set<std::string> exclude_attributes; std::set<std::string> exclude_attributes;
if (!evaluate(feature, layer, filter, exclude_attributes, nz)) { if (filter != NULL && !evaluate(feature, layer, filter, exclude_attributes, nz)) {
continue; continue;
} }
+60 -37
View File
@@ -26,6 +26,7 @@ static std::string mvt_value_to_string(mvt_value one, bool &fail) {
case mvt_null: case mvt_null:
fail = true; // null op string => null fail = true; // null op string => null
return ""; return "";
case mvt_no_such_key:
default: default:
fprintf(stderr, "unhandled mvt_type %d\n", one.type); fprintf(stderr, "unhandled mvt_type %d\n", one.type);
exit(EXIT_IMPOSSIBLE); exit(EXIT_IMPOSSIBLE);
@@ -127,6 +128,8 @@ int compare_fsl(mvt_value one, json_object *two, bool &fail) {
case mvt_null: case mvt_null:
fail = true; // null op bool => null fail = true; // null op bool => null
return 0; return 0;
case mvt_no_such_key:
default: default:
fprintf(stderr, "unhandled mvt_type %d\n", one.type); fprintf(stderr, "unhandled mvt_type %d\n", one.type);
exit(EXIT_IMPOSSIBLE); exit(EXIT_IMPOSSIBLE);
@@ -177,6 +180,7 @@ int compare(mvt_value one, json_object *two, bool &fail) {
case mvt_sint: case mvt_sint:
v = one.numeric_value.sint_value; v = one.numeric_value.sint_value;
break; break;
case mvt_no_such_key:
default: default:
fprintf(stderr, "Internal error: bad mvt type %d\n", one.type); fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
exit(EXIT_IMPOSSIBLE); exit(EXIT_IMPOSSIBLE);
@@ -208,6 +212,10 @@ int compare(mvt_value one, json_object *two, bool &fail) {
} }
return 0; // null equals null return 0; // null equals null
case mvt_no_such_key:
default:
break;
} }
fprintf(stderr, "Internal error: bad mvt type %d\n", one.type); fprintf(stderr, "Internal error: bad mvt type %d\n", one.type);
@@ -217,7 +225,7 @@ int compare(mvt_value one, json_object *two, bool &fail) {
// 0: false // 0: false
// 1: true // 1: true
// -1: incomparable (sql null), treated as false in final output // -1: incomparable (sql null), treated as false in final output
static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_object *f, std::set<std::string> &exclude_attributes) { static int eval(std::function<mvt_value(std::string const &)> feature, json_object *f, std::set<std::string> &exclude_attributes) {
if (f != NULL) { if (f != NULL) {
if (f->type == JSON_TRUE) { if (f->type == JSON_TRUE) {
return 1; return 1;
@@ -263,10 +271,10 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
if (f->value.array.array[0]->type == JSON_STRING) { if (f->value.array.array[0]->type == JSON_STRING) {
// if LHS of a boolean conjunction is a string, it is an attribute reference // if LHS of a boolean conjunction is a string, it is an attribute reference
auto ff = feature.find(std::string(f->value.array.array[0]->value.string.string)); mvt_value ff = feature(std::string(f->value.array.array[0]->value.string.string));
if (ff != feature.end()) { if (ff.type != mvt_no_such_key) {
if (ff->second.type == mvt_bool) { if (ff.type == mvt_bool) {
lhs = ff->second.numeric_value.bool_value; lhs = ff.numeric_value.bool_value;
} else { } else {
lhs = -1; // not boolean: null lhs = -1; // not boolean: null
} }
@@ -307,9 +315,9 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
false)) { false)) {
mvt_value lhs; mvt_value lhs;
lhs.type = mvt_null; // attributes that aren't found are nulls lhs.type = mvt_null; // attributes that aren't found are nulls
auto ff = feature.find(std::string(f->value.array.array[0]->value.string.string)); mvt_value ff = feature(std::string(f->value.array.array[0]->value.string.string));
if (ff != feature.end()) { if (ff.type != mvt_no_such_key) {
lhs = ff->second; lhs = ff;
} }
if (f->value.array.array[2]->type == JSON_NULL && strcmp(f->value.array.array[1]->value.string.string, "is") == 0) { if (f->value.array.array[2]->type == JSON_NULL && strcmp(f->value.array.array[1]->value.string.string, "is") == 0) {
@@ -368,7 +376,7 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
} }
} }
int cmp = compare_fsl(ff->second, f->value.array.array[2], fail); int cmp = compare_fsl(ff, f->value.array.array[2], fail);
if (fail) { if (fail) {
printf("cast fail\n"); printf("cast fail\n");
return -1; // null return -1; // null
@@ -414,7 +422,7 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f)); fprintf(stderr, "\"has\" key is not a string: %s\n", json_stringify(f));
exit(EXIT_FILTER); exit(EXIT_FILTER);
} }
return feature.count(std::string(f->value.array.array[1]->value.string.string)) != 0; return feature(std::string(f->value.array.array[1]->value.string.string)).type != mvt_no_such_key;
} }
if (strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) { if (strcmp(f->value.array.array[0]->value.string.string, "!has") == 0) {
@@ -422,7 +430,7 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f)); fprintf(stderr, "\"!has\" key is not a string: %s\n", json_stringify(f));
exit(EXIT_FILTER); exit(EXIT_FILTER);
} }
return feature.count(std::string(f->value.array.array[1]->value.string.string)) == 0; return feature(std::string(f->value.array.array[1]->value.string.string)).type == mvt_no_such_key;
} }
} }
@@ -441,8 +449,8 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
exit(EXIT_FILTER); exit(EXIT_FILTER);
} }
auto ff = feature.find(std::string(f->value.array.array[1]->value.string.string)); mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string));
if (ff == feature.end()) { if (ff.type == mvt_no_such_key) {
static bool warned = false; static bool warned = false;
if (!warned) { if (!warned) {
const char *s = json_stringify(f); const char *s = json_stringify(f);
@@ -457,7 +465,7 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
} }
bool fail = false; bool fail = false;
int cmp = compare(ff->second, f->value.array.array[2], fail); int cmp = compare(ff, f->value.array.array[2], fail);
if (fail) { if (fail) {
static bool warned = false; static bool warned = false;
@@ -544,8 +552,8 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
exit(EXIT_FILTER); exit(EXIT_FILTER);
} }
auto ff = feature.find(std::string(f->value.array.array[1]->value.string.string)); mvt_value ff = feature(std::string(f->value.array.array[1]->value.string.string));
if (ff == feature.end()) { if (ff.type == mvt_no_such_key) {
static bool warned = false; static bool warned = false;
if (!warned) { if (!warned) {
const char *s = json_stringify(f); const char *s = json_stringify(f);
@@ -562,7 +570,7 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
bool found = false; bool found = false;
for (size_t i = 2; i < f->value.array.length; i++) { for (size_t i = 2; i < f->value.array.length; i++) {
bool fail = false; bool fail = false;
int cmp = compare(ff->second, f->value.array.array[i], fail); int cmp = compare(ff, f->value.array.array[i], fail);
if (fail) { if (fail) {
static bool warned = false; static bool warned = false;
@@ -611,7 +619,7 @@ static int eval(std::unordered_map<std::string, mvt_value> const &feature, json_
exit(EXIT_FILTER); exit(EXIT_FILTER);
} }
bool evaluate(std::unordered_map<std::string, mvt_value> const &feature, std::string const &layer, json_object *filter, std::set<std::string> &exclude_attributes) { bool evaluate(std::function<mvt_value(std::string const &)> feature, std::string const &layer, json_object *filter, std::set<std::string> &exclude_attributes) {
if (filter == NULL || filter->type != JSON_HASH) { if (filter == NULL || filter->type != JSON_HASH) {
fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter)); fprintf(stderr, "Error: filter is not a hash: %s\n", json_stringify(filter));
exit(EXIT_JSON); exit(EXIT_JSON);
@@ -665,25 +673,32 @@ json_object *parse_filter(const char *s) {
return filter; return filter;
} }
bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object *filter, std::set<std::string> &exclude_attributes, int z) { bool evaluate(std::unordered_map<std::string, mvt_value> const &feature, std::string const &layer, json_object *filter, std::set<std::string> &exclude_attributes) {
if (filter != NULL) { std::function<mvt_value(std::string const &)> getter = [&](std::string const &key) {
std::unordered_map<std::string, mvt_value> attributes; auto f = feature.find(key);
if (f != feature.end()) {
for (size_t t = 0; t + 1 < feat.tags.size(); t += 2) { return f->second;
std::string key = layer.keys[feat.tags[t]]; } else {
const mvt_value &val = layer.values[feat.tags[t + 1]]; mvt_value v;
v.type = mvt_no_such_key;
attributes.emplace(key, val); v.numeric_value.null_value = 0;
return v;
} }
};
if (feat.has_id) { return evaluate(getter, layer, filter, exclude_attributes);
}
bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object *filter, std::set<std::string> &exclude_attributes, int z) {
std::function<mvt_value(std::string const &)> getter = [&](std::string const &key) {
if (key == "$id" && feat.has_id) {
mvt_value v; mvt_value v;
v.type = mvt_uint; v.type = mvt_uint;
v.numeric_value.uint_value = feat.id; v.numeric_value.uint_value = feat.id;
return v;
attributes.emplace("$id", v);
} }
if (key == "$type") {
mvt_value v; mvt_value v;
v.type = mvt_string; v.type = mvt_string;
@@ -694,19 +709,27 @@ bool evaluate(mvt_feature const &feat, mvt_layer const &layer, json_object *filt
} else if (feat.type == mvt_polygon) { } else if (feat.type == mvt_polygon) {
v.string_value = "Polygon"; v.string_value = "Polygon";
} }
return v;
}
attributes.emplace("$type", v); if (key == "$zoom") {
mvt_value v2; mvt_value v2;
v2.type = mvt_uint; v2.type = mvt_uint;
v2.numeric_value.uint_value = z; v2.numeric_value.uint_value = z;
return v2;
}
attributes.emplace("$zoom", v2); for (size_t i = 0; i + 1 < feat.tags.size(); i += 2) {
if (layer.keys[feat.tags[i]] == key) {
if (!evaluate(attributes, layer.name, filter, exclude_attributes)) { return layer.values[feat.tags[i + 1]];
return false;
} }
} }
return true; mvt_value v;
v.type = mvt_no_such_key;
v.numeric_value.null_value = 0;
return v;
};
return evaluate(getter, layer.name, filter, exclude_attributes);
} }
+2
View File
@@ -68,6 +68,8 @@ enum mvt_value_type {
mvt_sint, mvt_sint,
mvt_bool, mvt_bool,
mvt_null, mvt_null,
mvt_no_such_key,
}; };
struct mvt_value { struct mvt_value {
+1 -1
View File
@@ -140,7 +140,7 @@ void append_tile(std::string message, int z, unsigned x, unsigned y, std::map<st
mvt_feature feat = layer.features[f]; mvt_feature feat = layer.features[f];
std::set<std::string> exclude_attributes; std::set<std::string> exclude_attributes;
if (!evaluate(feat, layer, filter, exclude_attributes, z)) { if (filter != NULL && !evaluate(feat, layer, filter, exclude_attributes, z)) {
continue; continue;
} }
+1
View File
@@ -1594,6 +1594,7 @@ serial_feature next_feature(decompressor *geoms, std::atomic<long long> *geompos
} }
if (filter != NULL) { if (filter != NULL) {
printf("%s\n", json_stringify(filter));
std::unordered_map<std::string, mvt_value> attributes; std::unordered_map<std::string, mvt_value> attributes;
std::string layername = (*layer_unmaps)[sf.segment][sf.layer]; std::string layername = (*layer_unmaps)[sf.segment][sf.layer];
std::set<std::string> exclude_attributes; std::set<std::string> exclude_attributes;