implement nested expression parsing

This commit is contained in:
Konstantin Käfer
2014-05-31 23:21:15 +02:00
parent 3bc5e03253
commit 606ad56220
6 changed files with 89 additions and 100 deletions

View File

@@ -263,8 +263,11 @@ module.exports = {
"contour_label": {
"source": "outdoors",
"layer": "contour",
"field": "index",
"value": [5, 10],
"filter": [
"and",
{ "field": "index", "value": [ 5, 10 ] },
{ "field": "ele", "operator": "not", "value": 0 }
],
"path": "curve",
"text_field": "{{ele}} m",
"font": "Open Sans Regular, Arial Unicode MS Regular",

View File

@@ -5,6 +5,7 @@
#include <llmr/util/vec.hpp>
#include <llmr/style/value.hpp>
#include <llmr/text/glyph.hpp>
#include <llmr/map/filter_expression.hpp>
#include <vector>
#include <map>
#include <unordered_map>
@@ -14,8 +15,6 @@
namespace llmr {
class BucketDescription;
class BucketExpression;
class BucketFilter;
class VectorTileLayer;
struct pbf;
@@ -56,8 +55,9 @@ public:
const pbf& operator*() const;
private:
bool matchesExpression(const BucketExpression &expression, const pbf &tags_pbf);
bool matchesFilter(const BucketFilter &filter, const pbf &tags_pbf);
bool matchesFilterExpression(const PropertyFilterExpression &filterExpression, const pbf &tags_pbf);
bool matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf);
bool matchesFilter(const PropertyFilter &filter, const pbf &tags_pbf);
private:
const FilteredVectorTileLayer& parent;

View File

@@ -5,8 +5,9 @@
#include <vector>
#include <cmath>
#include "value.hpp"
#include "../util/vec.hpp"
#include <llmr/util/vec.hpp>
#include <llmr/map/filter_expression.hpp>
#include <llmr/style/value.hpp>
namespace llmr {
@@ -38,16 +39,6 @@ enum class TextPathType {
Curve = 1
};
enum class FilterOperator {
Equal,
NotEqual
};
enum class ExpressionOperator {
Or,
And
};
inline BucketType bucketType(const std::string& type) {
if (type == "fill") return BucketType::Fill;
@@ -91,22 +82,6 @@ inline float verticalAlignmentType(const std::string& alignment) {
else return 0.5;
};
inline FilterOperator filterOperatorType(const std::string &op) {
if (op == "!=" || op == "not") {
return FilterOperator::NotEqual;
} else {
return FilterOperator::Equal;
}
}
inline ExpressionOperator expressionOperatorType(const std::string &op) {
if (op == "&&" || op == "and") {
return ExpressionOperator::And;
} else {
return ExpressionOperator::Or;
}
}
class BucketGeometryDescription {
public:
@@ -133,41 +108,6 @@ public:
};
class BucketFilter {
public:
inline BucketFilter(const std::string &field, const Value &value) : field(field), value(value) {};
// Returns true if the filter passes, even if the key is missing.
inline bool isMissingFieldOkay() const {
switch (op) {
case FilterOperator::NotEqual:
return true;
default:
return false;
}
}
inline bool compare(const Value &other) const {
switch (op) {
case FilterOperator::NotEqual:
return !util::relaxed_equal(value)(other);
default:
return util::relaxed_equal(value)(other);
}
}
public:
std::string field;
Value value;
FilterOperator op = FilterOperator::Equal;
};
class BucketExpression {
public:
ExpressionOperator op = ExpressionOperator::Or;
std::vector<BucketFilter> operands;
};
class BucketDescription {
public:
BucketType feature_type = BucketType::None;
@@ -177,7 +117,7 @@ public:
std::string source_name;
std::string source_layer;
BucketExpression filter;
PropertyFilterExpression filter;
// Specifies how the geometry for this bucket should be created
BucketGeometryDescription geometry;

View File

@@ -39,6 +39,8 @@ private:
RasterClass parseRasterClass(JSVal value);
CompositeClass parseCompositeClass(JSVal value);
PropertyFilterExpression parseFilterOrExpression(JSVal value);
bool parseBoolean(JSVal value);
std::string parseString(JSVal value);
JSVal replaceConstant(JSVal value);

View File

@@ -122,9 +122,18 @@ FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& paren
operator++();
}
bool FilteredVectorTileLayer::iterator::matchesFilter(const BucketFilter &filter, const pbf &const_tags_pbf) {
bool missing = true;
bool FilteredVectorTileLayer::iterator::matchesFilterExpression(const PropertyFilterExpression &filterExpression, const pbf &tags_pbf) {
if (filterExpression.is<PropertyFilter>()) {
return matchesFilter(filterExpression.get<PropertyFilter>(), tags_pbf);
} else if (filterExpression.is<util::recursive_wrapper<PropertyExpression>>()) {
return matchesExpression(filterExpression.get<util::recursive_wrapper<PropertyExpression>>().get(), tags_pbf);
} else {
return false;
}
}
bool FilteredVectorTileLayer::iterator::matchesFilter(const PropertyFilter &filter, const pbf &const_tags_pbf) {
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;
@@ -157,17 +166,17 @@ bool FilteredVectorTileLayer::iterator::matchesFilter(const BucketFilter &filter
return filter.isMissingFieldOkay();
}
bool FilteredVectorTileLayer::iterator::matchesExpression(const BucketExpression &expression, const pbf &tags_pbf) {
bool FilteredVectorTileLayer::iterator::matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf) {
if (expression.op == ExpressionOperator::Or) {
for (const BucketFilter &filter : expression.operands) {
if (matchesFilter(filter, tags_pbf)) {
for (const PropertyFilterExpression &filterExpression : expression.operands) {
if (matchesFilterExpression(filterExpression, tags_pbf)) {
return true;
}
}
return false;
} else if (expression.op == ExpressionOperator::And) {
for (const BucketFilter &filter : expression.operands) {
if (!matchesFilter(filter, tags_pbf)) {
for (const PropertyFilterExpression &filterExpression : expression.operands) {
if (!matchesFilterExpression(filterExpression, tags_pbf)) {
return false;
}
}
@@ -182,7 +191,7 @@ bool FilteredVectorTileLayer::iterator::matchesExpression(const BucketExpression
void FilteredVectorTileLayer::iterator::operator++() {
valid = false;
BucketExpression expression = parent.bucket_desc.filter;
const PropertyFilterExpression &expression = parent.bucket_desc.filter;
while (data.next(2)) { // feature
feature = data.message();
@@ -192,7 +201,7 @@ void FilteredVectorTileLayer::iterator::operator++() {
bool matched = false;
// Treat the absence of any expression filters as a match.
if (expression.operands.size() == 0) {
if (expression.is<util::recursive_wrapper<PropertyExpression>>() && expression.get<util::recursive_wrapper<PropertyExpression>>().get().operands.size() == 0) {
matched = true;
}
@@ -204,7 +213,7 @@ void FilteredVectorTileLayer::iterator::operator++() {
} else {
// We only want to parse some features.
const pbf tags_pbf = feature_pbf.message();
matched = matchesExpression(expression, tags_pbf);
matched = matchesFilterExpression(expression, tags_pbf);
if (!matched) {
break; // feature_pbf loop early
}

View File

@@ -20,6 +20,56 @@ void StyleParser::parseBuckets(JSVal value, std::map<std::string, BucketDescript
}
}
PropertyFilterExpression StyleParser::parseFilterOrExpression(JSVal value) {
if (value.IsArray()) {
// This is an expression.
PropertyExpression expression;
for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
JSVal filter_item = value[i];
if (filter_item.IsString()) {
expression.op = expressionOperatorType({ filter_item.GetString(), filter_item.GetStringLength() });
} else {
expression.operands.emplace_back(parseFilterOrExpression(filter_item));
}
}
return util::recursive_wrapper<PropertyExpression>(std::move(expression));
} else if (value.IsObject() && value.HasMember("field") && value.HasMember("value")) {
// This is a filter.
JSVal field = value["field"];
JSVal val = value["value"];
if (!field.IsString()) {
throw Style::exception("field name must be a string");
}
const std::string field_name { field.GetString(), field.GetStringLength() };
const FilterOperator op = [&]{
if (value.HasMember("operator")) {
JSVal op_val = value["operator"];
return filterOperatorType({ op_val.GetString(), op_val.GetStringLength() });
} else {
return FilterOperator::Equal;
}
}();
if (val.IsArray()) {
// The filter has several values, so it's an OR sub-expression.
PropertyExpression expression;
for (rapidjson::SizeType i = 0; i < val.Size(); ++i) {
expression.operands.emplace_back(PropertyFilter { field_name, op, parseValue(val[i]) });
}
return util::recursive_wrapper<PropertyExpression>(std::move(expression));
} else {
// The filter only has a single value, so it is a real filter.
return PropertyFilter { field_name, op, parseValue(val) };
}
}
return util::recursive_wrapper<PropertyExpression>();
}
BucketDescription StyleParser::parseBucket(JSVal value) {
BucketDescription bucket;
@@ -153,26 +203,11 @@ BucketDescription StyleParser::parseBucket(JSVal value) {
}
if (value.HasMember("field") && value.HasMember("value")) {
JSVal field = value["field"];
JSVal val = value["value"];
if (!field.IsString()) {
throw Style::exception("field name must be a string");
}
const std::string field_name { field.GetString(), field.GetStringLength() };
bucket.filter = {};
if (val.IsArray()) {
for (rapidjson::SizeType i = 0; i < val.Size(); ++i) {
bucket.filter.operands.emplace_back(field_name, parseValue(val[i]));
}
} else {
bucket.filter.operands.emplace_back(field_name, parseValue(val));
}
if (value.HasMember("filter")) {
bucket.filter = std::move(parseFilterOrExpression(value["filter"]));
} else {
// Allow a filter triple to be specified at the bucket root as well.
bucket.filter = std::move(parseFilterOrExpression(value));
}
if (bucket.feature_type == BucketType::None) {