From 606ad5622013b8dd36a1dcf6b0f05c4c321de3d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Konstantin=20Ka=CC=88fer?= Date: Sat, 31 May 2014 23:21:15 +0200 Subject: [PATCH] implement nested expression parsing --- bin/style.js | 7 ++- include/llmr/map/vector_tile.hpp | 8 +-- include/llmr/style/bucket_description.hpp | 68 ++------------------ include/llmr/style/style_parser.hpp | 2 + src/map/vector_tile.cpp | 29 ++++++--- src/style/style_parser.cpp | 75 +++++++++++++++++------ 6 files changed, 89 insertions(+), 100 deletions(-) diff --git a/bin/style.js b/bin/style.js index d851c1f16..09fd7c0e6 100644 --- a/bin/style.js +++ b/bin/style.js @@ -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", diff --git a/include/llmr/map/vector_tile.hpp b/include/llmr/map/vector_tile.hpp index 8c70016bc..904cc9a2e 100644 --- a/include/llmr/map/vector_tile.hpp +++ b/include/llmr/map/vector_tile.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include #include @@ -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; diff --git a/include/llmr/style/bucket_description.hpp b/include/llmr/style/bucket_description.hpp index e7b8a63e7..4ce25d238 100644 --- a/include/llmr/style/bucket_description.hpp +++ b/include/llmr/style/bucket_description.hpp @@ -5,8 +5,9 @@ #include #include -#include "value.hpp" -#include "../util/vec.hpp" +#include +#include +#include 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 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; diff --git a/include/llmr/style/style_parser.hpp b/include/llmr/style/style_parser.hpp index c00c49677..10f367475 100644 --- a/include/llmr/style/style_parser.hpp +++ b/include/llmr/style/style_parser.hpp @@ -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); diff --git a/src/map/vector_tile.cpp b/src/map/vector_tile.cpp index bc6386e85..0e96cccd1 100644 --- a/src/map/vector_tile.cpp +++ b/src/map/vector_tile.cpp @@ -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()) { + return matchesFilter(filterExpression.get(), tags_pbf); + } else if (filterExpression.is>()) { + return matchesExpression(filterExpression.get>().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>() && expression.get>().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 } diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp index 21840397c..18b2ab3af 100644 --- a/src/style/style_parser.cpp +++ b/src/style/style_parser.cpp @@ -20,6 +20,56 @@ void StyleParser::parseBuckets(JSVal value, std::map(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(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(); +} + 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) {