simple expression support
This commit is contained in:
@@ -91,6 +91,7 @@ VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) {
|
||||
name = layer.string();
|
||||
} else if (layer.tag == 3) { // keys
|
||||
keys.emplace_back(layer.string());
|
||||
key_index.emplace(keys.back(), keys.size() - 1);
|
||||
} else if (layer.tag == 4) { // values
|
||||
values.emplace_back(std::move(parseValue(layer.message())));
|
||||
} else if (layer.tag == 5) { // extent
|
||||
@@ -104,81 +105,108 @@ VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) {
|
||||
FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, const BucketDescription& bucket_desc)
|
||||
: layer(layer),
|
||||
bucket_desc(bucket_desc) {
|
||||
// If we filter further by field/value, parse the key/value indices first
|
||||
// because protobuf doesn't mandate a particular key order.
|
||||
if (bucket_desc.source_field.size()) {
|
||||
// Get the ID of the desired layer key.
|
||||
auto key_it = std::find(layer.keys.begin(), layer.keys.end(), bucket_desc.source_field);
|
||||
if (key_it != layer.keys.end()) {
|
||||
key = (int32_t)(key_it - layer.keys.begin());
|
||||
}
|
||||
// Get the ID of the desired layer value(s), comparing as strings in
|
||||
// order to avoid numeric type mismatches between the layer and the
|
||||
// style definition.
|
||||
for (const Value& source_value : bucket_desc.source_value) {
|
||||
auto value_it = std::find_if(layer.values.begin(), layer.values.end(), util::relaxed_equal(source_value));
|
||||
if (value_it != layer.values.end()) {
|
||||
values.insert((uint32_t)(value_it - layer.values.begin()));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
FilteredVectorTileLayer::iterator FilteredVectorTileLayer::begin() const {
|
||||
if (bucket_desc.source_field.size() && (key < 0 || values.empty())) {
|
||||
// There are no valid values that we could possibly find. Abort early.
|
||||
return end();
|
||||
} else {
|
||||
return iterator(*this, layer.data);
|
||||
}
|
||||
return iterator(*this, layer.data);
|
||||
}
|
||||
|
||||
FilteredVectorTileLayer::iterator FilteredVectorTileLayer::end() const {
|
||||
return iterator(*this, pbf(layer.data.end, 0));
|
||||
}
|
||||
|
||||
FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& filter, const pbf& data)
|
||||
: filter(filter),
|
||||
FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& parent, const pbf& data)
|
||||
: parent(parent),
|
||||
feature(pbf()),
|
||||
data(data) {
|
||||
operator++();
|
||||
}
|
||||
|
||||
bool FilteredVectorTileLayer::iterator::matchesFilter(const BucketFilter &filter, const pbf &const_tags_pbf) {
|
||||
bool missing = true;
|
||||
|
||||
auto field_it = parent.layer.key_index.find(filter.field);
|
||||
if (field_it != parent.layer.key_index.end()) {
|
||||
const uint32_t filter_key = field_it->second;
|
||||
|
||||
// Now loop through all the key/value pair tags.
|
||||
// tags are packed varints. They should have an even length.
|
||||
pbf tags_pbf = const_tags_pbf;
|
||||
while (tags_pbf) {
|
||||
uint32_t tag_key = tags_pbf.varint();
|
||||
if (!tags_pbf) {
|
||||
// This should not happen; otherwise the vector tile
|
||||
// is invalid.
|
||||
throw std::runtime_error("uneven number of feature tag ids");
|
||||
}
|
||||
uint32_t tag_val = tags_pbf.varint();
|
||||
|
||||
if (tag_key == filter_key) {
|
||||
if (parent.layer.values.size() > tag_val) {
|
||||
const Value &value = parent.layer.values[tag_val];
|
||||
return filter.compare(value);
|
||||
} else {
|
||||
throw std::runtime_error("feature references out of range value");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The feature doesn't contain the field that we're looking to compare.
|
||||
// Depending on the filter, this may still be okay.
|
||||
return filter.isMissingFieldOkay();
|
||||
}
|
||||
|
||||
bool FilteredVectorTileLayer::iterator::matchesExpression(const BucketExpression &expression, const pbf &tags_pbf) {
|
||||
if (expression.op == ExpressionOperator::Or) {
|
||||
for (const BucketFilter &filter : expression.operands) {
|
||||
if (matchesFilter(filter, tags_pbf)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} else if (expression.op == ExpressionOperator::And) {
|
||||
for (const BucketFilter &filter : expression.operands) {
|
||||
if (!matchesFilter(filter, tags_pbf)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void FilteredVectorTileLayer::iterator::operator++() {
|
||||
valid = false;
|
||||
|
||||
BucketExpression expression = parent.bucket_desc.filter;
|
||||
|
||||
while (data.next(2)) { // feature
|
||||
feature = data.message();
|
||||
pbf feature_pbf = feature;
|
||||
BucketType type = BucketType::None;
|
||||
|
||||
bool skip = false;
|
||||
while (!skip && feature_pbf.next()) {
|
||||
bool matched = false;
|
||||
|
||||
// Treat the absence of any expression filters as a match.
|
||||
if (expression.operands.size() == 0) {
|
||||
matched = true;
|
||||
}
|
||||
|
||||
while (feature_pbf.next()) {
|
||||
if (feature_pbf.tag == 2) { // tags
|
||||
if (filter.key < 0) {
|
||||
// We want to parse the entire layer anyway
|
||||
if (matched) {
|
||||
// There is no filter, so we want to parse the entire layer anyway.
|
||||
feature_pbf.skip();
|
||||
} else {
|
||||
// We only want to parse some features.
|
||||
skip = true;
|
||||
|
||||
// tags are packed varints. They should have an even length.
|
||||
pbf tags_pbf = feature_pbf.message();
|
||||
while (tags_pbf) {
|
||||
int32_t tag_key = tags_pbf.varint();
|
||||
if (tags_pbf) {
|
||||
uint32_t tag_val = tags_pbf.varint();
|
||||
// Now check if the tag_key/tag_val pair is included
|
||||
// in the set of key/values that we are looking for.
|
||||
if (filter.key == tag_key && filter.values.find(tag_val) != filter.values.end()) {
|
||||
skip = false;
|
||||
break; // feature_pbf loop
|
||||
}
|
||||
} else {
|
||||
// This should not happen; otherwise the vector tile
|
||||
// is invalid.
|
||||
throw std::runtime_error("uneven number of feature tag ids");
|
||||
}
|
||||
const pbf tags_pbf = feature_pbf.message();
|
||||
matched = matchesExpression(expression, tags_pbf);
|
||||
if (!matched) {
|
||||
break; // feature_pbf loop early
|
||||
}
|
||||
}
|
||||
} else if (feature_pbf.tag == 3) { // geometry type
|
||||
@@ -189,7 +217,8 @@ void FilteredVectorTileLayer::iterator::operator++() {
|
||||
default: type = BucketType::None; break;
|
||||
}
|
||||
|
||||
if (type != filter.bucket_desc.feature_type) {
|
||||
if (type != parent.bucket_desc.feature_type) {
|
||||
matched = false;
|
||||
break; // feature_pbf loop early
|
||||
}
|
||||
} else {
|
||||
@@ -197,7 +226,7 @@ void FilteredVectorTileLayer::iterator::operator++() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip && type == filter.bucket_desc.feature_type) {
|
||||
if (matched) {
|
||||
valid = true;
|
||||
return; // data loop
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user