summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/style/conversion/filter.hpp144
-rw-r--r--include/mbgl/style/filter.hpp101
-rw-r--r--include/mbgl/style/filter_evaluator.hpp92
m---------mapbox-gl-js0
-rw-r--r--platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm105
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.h3
-rw-r--r--platform/darwin/src/NSExpression+MGLAdditions.mm61
-rw-r--r--platform/darwin/src/NSPredicate+MGLAdditions.mm72
-rw-r--r--platform/darwin/test/MGLExpressionTests.mm19
-rw-r--r--platform/darwin/test/MGLPredicateTests.mm155
-rw-r--r--platform/ios/CHANGELOG.md3
-rw-r--r--platform/macos/CHANGELOG.md3
-rw-r--r--src/mbgl/style/conversion/stringify.hpp86
13 files changed, 780 insertions, 64 deletions
diff --git a/include/mbgl/style/conversion/filter.hpp b/include/mbgl/style/conversion/filter.hpp
index 3ab91e5bbc..2c4eeb4ac7 100644
--- a/include/mbgl/style/conversion/filter.hpp
+++ b/include/mbgl/style/conversion/filter.hpp
@@ -27,9 +27,9 @@ public:
}
if (*op == "==") {
- return convertBinaryFilter<EqualsFilter>(value);
+ return convertEqualityFilter<EqualsFilter, TypeEqualsFilter, IdentifierEqualsFilter>(value);
} else if (*op == "!=") {
- return convertBinaryFilter<NotEqualsFilter>(value);
+ return convertEqualityFilter<NotEqualsFilter, TypeNotEqualsFilter, IdentifierNotEqualsFilter>(value);
} else if (*op == ">") {
return convertBinaryFilter<GreaterThanFilter>(value);
} else if (*op == ">=") {
@@ -39,9 +39,9 @@ public:
} else if (*op == "<=") {
return convertBinaryFilter<LessThanEqualsFilter>(value);
} else if (*op == "in") {
- return convertSetFilter<InFilter>(value);
+ return convertSetFilter<InFilter, TypeInFilter, IdentifierInFilter>(value);
} else if (*op == "!in") {
- return convertSetFilter<NotInFilter>(value);
+ return convertSetFilter<NotInFilter, TypeNotInFilter, IdentifierNotInFilter>(value);
} else if (*op == "all") {
return convertCompoundFilter<AllFilter>(value);
} else if (*op == "any") {
@@ -49,32 +49,57 @@ public:
} else if (*op == "none") {
return convertCompoundFilter<NoneFilter>(value);
} else if (*op == "has") {
- return convertUnaryFilter<HasFilter>(value);
+ return convertUnaryFilter<HasFilter, HasIdentifierFilter>(value);
} else if (*op == "!has") {
- return convertUnaryFilter<NotHasFilter>(value);
+ return convertUnaryFilter<NotHasFilter, NotHasIdentifierFilter>(value);
}
return Error { "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\", \"has\", or \"!has\"" };
}
private:
- Result<Value> normalizeValue(const std::string& key, const optional<Value>& value) const {
+ Result<Value> normalizeValue(const optional<Value>& value) const {
if (!value) {
return Error { "filter expression value must be a boolean, number, or string" };
- } else if (key != "$type") {
+ } else {
return *value;
- } else if (*value == std::string("Point")) {
- return Value(uint64_t(FeatureType::Point));
- } else if (*value == std::string("LineString")) {
- return Value(uint64_t(FeatureType::LineString));
- } else if (*value == std::string("Polygon")) {
- return Value(uint64_t(FeatureType::Polygon));
+ }
+ }
+
+ template <class V>
+ Result<FeatureType> toFeatureType(const V& value) const {
+ optional<std::string> type = toString(value);
+ if (!type) {
+ return Error { "value for $type filter must be a string" };
+ } else if (*type == "Point") {
+ return FeatureType::Point;
+ } else if (*type == "LineString") {
+ return FeatureType::LineString;
+ } else if (*type == "Polygon") {
+ return FeatureType::Polygon;
} else {
return Error { "value for $type filter must be Point, LineString, or Polygon" };
}
}
- template <class FilterType, class V>
+ template <class V>
+ Result<FeatureIdentifier> toFeatureIdentifier(const V& value) const {
+ optional<Value> identifier = toValue(value);
+ if (!identifier) {
+ return Error { "filter expression value must be a boolean, number, or string" };
+ } else {
+ return (*identifier).match(
+ [] (uint64_t t) -> Result<FeatureIdentifier> { return t; },
+ [] ( int64_t t) -> Result<FeatureIdentifier> { return t; },
+ [] ( double t) -> Result<FeatureIdentifier> { return t; },
+ [] (const std::string& t) -> Result<FeatureIdentifier> { return t; },
+ [] (const auto&) -> Result<FeatureIdentifier> {
+ return Error { "filter expression value must be a boolean, number, or string" };
+ });
+ }
+ }
+
+ template <class FilterType, class IdentifierFilterType, class V>
Result<Filter> convertUnaryFilter(const V& value) const {
if (arrayLength(value) < 2) {
return Error { "filter expression must have 2 elements" };
@@ -85,7 +110,48 @@ private:
return Error { "filter expression key must be a string" };
}
- return FilterType { *key };
+ if (*key == "$id") {
+ return IdentifierFilterType {};
+ } else {
+ return FilterType { *key };
+ }
+ }
+
+ template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
+ Result<Filter> convertEqualityFilter(const V& value) const {
+ if (arrayLength(value) < 3) {
+ return Error { "filter expression must have 3 elements" };
+ }
+
+ optional<std::string> key = toString(arrayMember(value, 1));
+ if (!key) {
+ return Error { "filter expression key must be a string" };
+ }
+
+ if (*key == "$type") {
+ Result<FeatureType> filterValue = toFeatureType(arrayMember(value, 2));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+
+ return TypeFilterType { *filterValue };
+
+ } else if (*key == "$id") {
+ Result<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, 2));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+
+ return IdentifierFilterType { *filterValue };
+
+ } else {
+ Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+
+ return FilterType { *key, *filterValue };
+ }
}
template <class FilterType, class V>
@@ -99,7 +165,7 @@ private:
return Error { "filter expression key must be a string" };
}
- Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, 2)));
+ Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, 2)));
if (!filterValue) {
return filterValue.error();
}
@@ -107,7 +173,7 @@ private:
return FilterType { *key, *filterValue };
}
- template <class FilterType, class V>
+ template <class FilterType, class TypeFilterType, class IdentifierFilterType, class V>
Result<Filter> convertSetFilter(const V& value) const {
if (arrayLength(value) < 2) {
return Error { "filter expression must at least 2 elements" };
@@ -118,16 +184,42 @@ private:
return Error { "filter expression key must be a string" };
}
- std::vector<Value> values;
- for (std::size_t i = 2; i < arrayLength(value); ++i) {
- Result<Value> filterValue = normalizeValue(*key, toValue(arrayMember(value, i)));
- if (!filterValue) {
- return filterValue.error();
+ if (*key == "$type") {
+ std::vector<FeatureType> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ Result<FeatureType> filterValue = toFeatureType(arrayMember(value, i));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+ values.push_back(*filterValue);
}
- values.push_back(*filterValue);
- }
- return FilterType { *key, std::move(values) };
+ return TypeFilterType { std::move(values) };
+
+ } else if (*key == "$id") {
+ std::vector<FeatureIdentifier> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ Result<FeatureIdentifier> filterValue = toFeatureIdentifier(arrayMember(value, i));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+ values.push_back(*filterValue);
+ }
+
+ return IdentifierFilterType { std::move(values) };
+
+ } else {
+ std::vector<Value> values;
+ for (std::size_t i = 2; i < arrayLength(value); ++i) {
+ Result<Value> filterValue = normalizeValue(toValue(arrayMember(value, i)));
+ if (!filterValue) {
+ return filterValue.error();
+ }
+ values.push_back(*filterValue);
+ }
+
+ return FilterType { *key, std::move(values) };
+ }
}
template <class FilterType, class V>
diff --git a/include/mbgl/style/filter.hpp b/include/mbgl/style/filter.hpp
index e5d8081d82..5e61adf064 100644
--- a/include/mbgl/style/filter.hpp
+++ b/include/mbgl/style/filter.hpp
@@ -145,6 +145,95 @@ public:
}
};
+
+class TypeEqualsFilter {
+public:
+ FeatureType value;
+
+ friend bool operator==(const TypeEqualsFilter& lhs, const TypeEqualsFilter& rhs) {
+ return lhs.value == rhs.value;
+ }
+};
+
+class TypeNotEqualsFilter {
+public:
+ FeatureType value;
+
+ friend bool operator==(const TypeNotEqualsFilter& lhs, const TypeNotEqualsFilter& rhs) {
+ return lhs.value == rhs.value;
+ }
+};
+
+class TypeInFilter {
+public:
+ std::vector<FeatureType> values;
+
+ friend bool operator==(const TypeInFilter& lhs, const TypeInFilter& rhs) {
+ return lhs.values == rhs.values;
+ }
+};
+
+class TypeNotInFilter {
+public:
+ std::vector<FeatureType> values;
+
+ friend bool operator==(const TypeNotInFilter& lhs, const TypeNotInFilter& rhs) {
+ return lhs.values == rhs.values;
+ }
+};
+
+
+class IdentifierEqualsFilter {
+public:
+ FeatureIdentifier value;
+
+ friend bool operator==(const IdentifierEqualsFilter& lhs, const IdentifierEqualsFilter& rhs) {
+ return lhs.value == rhs.value;
+ }
+};
+
+class IdentifierNotEqualsFilter {
+public:
+ FeatureIdentifier value;
+
+ friend bool operator==(const IdentifierNotEqualsFilter& lhs, const IdentifierNotEqualsFilter& rhs) {
+ return lhs.value == rhs.value;
+ }
+};
+
+class IdentifierInFilter {
+public:
+ std::vector<FeatureIdentifier> values;
+
+ friend bool operator==(const IdentifierInFilter& lhs, const IdentifierInFilter& rhs) {
+ return lhs.values == rhs.values;
+ }
+};
+
+class IdentifierNotInFilter {
+public:
+ std::vector<FeatureIdentifier> values;
+
+ friend bool operator==(const IdentifierNotInFilter& lhs, const IdentifierNotInFilter& rhs) {
+ return lhs.values == rhs.values;
+ }
+};
+
+class HasIdentifierFilter {
+public:
+ friend bool operator==(const HasIdentifierFilter&, const HasIdentifierFilter&) {
+ return true;
+ }
+};
+
+class NotHasIdentifierFilter {
+public:
+ friend bool operator==(const NotHasIdentifierFilter&, const NotHasIdentifierFilter&) {
+ return true;
+ }
+};
+
+
using FilterBase = variant<
class NullFilter,
class EqualsFilter,
@@ -159,7 +248,17 @@ using FilterBase = variant<
class AllFilter,
class NoneFilter,
class HasFilter,
- class NotHasFilter>;
+ class NotHasFilter,
+ class TypeEqualsFilter,
+ class TypeNotEqualsFilter,
+ class TypeInFilter,
+ class TypeNotInFilter,
+ class IdentifierEqualsFilter,
+ class IdentifierNotEqualsFilter,
+ class IdentifierInFilter,
+ class IdentifierNotInFilter,
+ class HasIdentifierFilter,
+ class NotHasIdentifierFilter>;
class Filter : public FilterBase {
public:
diff --git a/include/mbgl/style/filter_evaluator.hpp b/include/mbgl/style/filter_evaluator.hpp
index 659f554bba..370064445a 100644
--- a/include/mbgl/style/filter_evaluator.hpp
+++ b/include/mbgl/style/filter_evaluator.hpp
@@ -31,37 +31,37 @@ public:
}
bool operator()(const EqualsFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
return actual && equal(*actual, filter.value);
}
bool operator()(const NotEqualsFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
return !actual || !equal(*actual, filter.value);
}
bool operator()(const LessThanFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ < rhs_; });
}
bool operator()(const LessThanEqualsFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ <= rhs_; });
}
bool operator()(const GreaterThanFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ > rhs_; });
}
bool operator()(const GreaterThanEqualsFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
return actual && compare(*actual, filter.value, [] (const auto& lhs_, const auto& rhs_) { return lhs_ >= rhs_; });
}
bool operator()(const InFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
if (!actual)
return false;
for (const auto& v: filter.values) {
@@ -73,7 +73,7 @@ public:
}
bool operator()(const NotInFilter& filter) const {
- optional<Value> actual = getValue(filter.key);
+ optional<Value> actual = propertyAccessor(filter.key);
if (!actual)
return true;
for (const auto& v: filter.values) {
@@ -112,30 +112,76 @@ public:
}
bool operator()(const HasFilter& filter) const {
- return bool(getValue(filter.key));
+ return bool(propertyAccessor(filter.key));
}
bool operator()(const NotHasFilter& filter) const {
- return !getValue(filter.key);
+ return !propertyAccessor(filter.key);
}
-private:
- optional<Value> getValue(const std::string& key_) const {
- if (key_ == "$type") {
- return optional<Value>(uint64_t(featureType));
- } else if (key_ == "$id") {
- if (featureIdentifier) {
- return FeatureIdentifier::visit(*featureIdentifier, [] (auto id) {
- return Value(std::move(id));
- });
- } else {
- return optional<Value>();
+
+ bool operator()(const TypeEqualsFilter& filter) const {
+ return featureType == filter.value;
+ }
+
+ bool operator()(const TypeNotEqualsFilter& filter) const {
+ return featureType != filter.value;
+ }
+
+ bool operator()(const TypeInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (featureType == v) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool operator()(const TypeNotInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (featureType == v) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+
+ bool operator()(const IdentifierEqualsFilter& filter) const {
+ return featureIdentifier == filter.value;
+ }
+
+ bool operator()(const IdentifierNotEqualsFilter& filter) const {
+ return featureIdentifier != filter.value;
+ }
+
+ bool operator()(const IdentifierInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (featureIdentifier == v) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ bool operator()(const IdentifierNotInFilter& filter) const {
+ for (const auto& v: filter.values) {
+ if (featureIdentifier == v) {
+ return false;
}
- } else {
- return propertyAccessor(key_);
}
+ return true;
}
+ bool operator()(const HasIdentifierFilter&) const {
+ return bool(featureIdentifier);
+ }
+
+ bool operator()(const NotHasIdentifierFilter&) const {
+ return !featureIdentifier;
+ }
+
+private:
template <class Op>
struct Comparator {
const Op& op;
diff --git a/mapbox-gl-js b/mapbox-gl-js
-Subproject cf031a6d072bda98b51c57021dfd767eb598ab4
+Subproject 3c52ddba23124a1844162a7fc2417ac8671fa91
diff --git a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
index 295b3b21f8..ac2d598d05 100644
--- a/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSComparisonPredicate+MGLAdditions.mm
@@ -5,8 +5,7 @@
@implementation NSComparisonPredicate (MGLAdditions)
-- (mbgl::style::Filter)mgl_filter
-{
+- (mbgl::style::Filter)mgl_filter {
NSExpression *leftExpression = self.leftExpression;
NSExpression *rightExpression = self.rightExpression;
NSExpressionType leftType = leftExpression.expressionType;
@@ -18,6 +17,25 @@
mbgl::style::EqualsFilter eqFilter;
eqFilter.key = self.mgl_keyPath.UTF8String;
eqFilter.value = self.mgl_constantValue;
+
+ // Convert $type == to TypeEqualsFilter.
+ if (eqFilter.key == "$type") {
+ mbgl::style::TypeEqualsFilter typeEqFilter;
+ typeEqFilter.value = self.mgl_featureType;
+ return typeEqFilter;
+ }
+
+ // Convert $id == to IdentifierEqualsFilter.
+ if (eqFilter.key == "$id") {
+ // Convert $id == nil to NotHasIdentifierFilter.
+ if (eqFilter.value.is<mbgl::NullValue>()) {
+ return mbgl::style::NotHasIdentifierFilter();
+ }
+
+ mbgl::style::IdentifierEqualsFilter idEqFilter;
+ idEqFilter.value = self.mgl_featureIdentifier;
+ return idEqFilter;
+ }
// Convert == nil to NotHasFilter.
if (eqFilter.value.is<mbgl::NullValue>()) {
@@ -32,6 +50,25 @@
mbgl::style::NotEqualsFilter neFilter;
neFilter.key = self.mgl_keyPath.UTF8String;
neFilter.value = self.mgl_constantValue;
+
+ // Convert $type != to TypeNotEqualsFilter.
+ if (neFilter.key == "$type") {
+ mbgl::style::TypeNotEqualsFilter typeNeFilter;
+ typeNeFilter.value = self.mgl_featureType;
+ return typeNeFilter;
+ }
+
+ // Convert $id != to IdentifierNotEqualsFilter.
+ if (neFilter.key == "$id") {
+ // Convert $id != nil to HasIdentifierFilter.
+ if (neFilter.value.is<mbgl::NullValue>()) {
+ return mbgl::style::HasIdentifierFilter();
+ }
+
+ mbgl::style::IdentifierNotEqualsFilter idNeFilter;
+ idNeFilter.value = self.mgl_featureIdentifier;
+ return idNeFilter;
+ }
// Convert != nil to HasFilter.
if (neFilter.value.is<mbgl::NullValue>()) {
@@ -103,6 +140,21 @@
[NSException raise:NSInvalidArgumentException
format:@"Predicate cannot compare values IN attribute."];
}
+
+ // Convert $type IN to TypeInFilter.
+ if ([leftExpression.keyPath isEqualToString:@"$type"]) {
+ mbgl::style::TypeInFilter typeInFilter;
+ typeInFilter.values = rightExpression.mgl_aggregateFeatureType;
+ return typeInFilter;
+ }
+
+ // Convert $id IN to IdentifierInFilter.
+ if ([leftExpression.keyPath isEqualToString:@"$id"]) {
+ mbgl::style::IdentifierInFilter idInFilter;
+ idInFilter.values = rightExpression.mgl_aggregateFeatureIdentifier;
+ return idInFilter;
+ }
+
mbgl::style::InFilter inFilter;
inFilter.key = leftExpression.keyPath.UTF8String;
inFilter.values = rightExpression.mgl_aggregateMBGLValue;
@@ -117,6 +169,21 @@
[NSException raise:NSInvalidArgumentException
format:@"Predicate cannot compare attribute CONTAINS values."];
}
+
+ // Convert CONTAINS $type to TypeInFilter.
+ if ([rightExpression.keyPath isEqualToString:@"$type"]) {
+ mbgl::style::TypeInFilter typeInFilter;
+ typeInFilter.values = leftExpression.mgl_aggregateFeatureType;
+ return typeInFilter;
+ }
+
+ // Convert CONTAINS $id to IdentifierInFilter.
+ if ([rightExpression.keyPath isEqualToString:@"$id"]) {
+ mbgl::style::IdentifierInFilter idInFilter;
+ idInFilter.values = leftExpression.mgl_aggregateFeatureIdentifier;
+ return idInFilter;
+ }
+
mbgl::style::InFilter inFilter;
inFilter.key = rightExpression.keyPath.UTF8String;
inFilter.values = leftExpression.mgl_aggregateMBGLValue;
@@ -192,4 +259,38 @@
return value;
}
+- (mbgl::FeatureType)mgl_featureType {
+ NSExpression *leftExpression = self.leftExpression;
+ NSExpression *rightExpression = self.rightExpression;
+ NSExpressionType leftType = leftExpression.expressionType;
+ NSExpressionType rightType = rightExpression.expressionType;
+ mbgl::FeatureType type;
+ if (leftType == NSKeyPathExpressionType && rightType == NSConstantValueExpressionType) {
+ type = rightExpression.mgl_featureType;
+ } else if (leftType == NSConstantValueExpressionType && rightType == NSKeyPathExpressionType) {
+ type = leftExpression.mgl_featureType;
+ } else {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
+ }
+ return type;
+}
+
+- (mbgl::FeatureIdentifier)mgl_featureIdentifier {
+ NSExpression *leftExpression = self.leftExpression;
+ NSExpression *rightExpression = self.rightExpression;
+ NSExpressionType leftType = leftExpression.expressionType;
+ NSExpressionType rightType = rightExpression.expressionType;
+ mbgl::FeatureIdentifier identifier;
+ if (leftType == NSKeyPathExpressionType && rightType == NSConstantValueExpressionType) {
+ identifier = rightExpression.mgl_featureIdentifier;
+ } else if (leftType == NSConstantValueExpressionType && rightType == NSKeyPathExpressionType) {
+ identifier = leftExpression.mgl_featureIdentifier;
+ } else {
+ [NSException raise:NSInvalidArgumentException
+ format:@"Comparison predicate must compare an attribute (as a key path) to a constant or vice versa."];
+ }
+ return identifier;
+}
+
@end
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.h b/platform/darwin/src/NSExpression+MGLAdditions.h
index c60d6d78ba..491ed5a536 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.h
+++ b/platform/darwin/src/NSExpression+MGLAdditions.h
@@ -8,7 +8,10 @@ NS_ASSUME_NONNULL_BEGIN
@property (nonatomic, readonly) mbgl::Value mgl_constantMBGLValue;
@property (nonatomic, readonly) std::vector<mbgl::Value> mgl_aggregateMBGLValue;
+@property (nonatomic, readonly) mbgl::FeatureType mgl_featureType;
+@property (nonatomic, readonly) std::vector<mbgl::FeatureType> mgl_aggregateFeatureType;
@property (nonatomic, readonly) mbgl::FeatureIdentifier mgl_featureIdentifier;
+@property (nonatomic, readonly) std::vector<mbgl::FeatureIdentifier> mgl_aggregateFeatureIdentifier;
@end
diff --git a/platform/darwin/src/NSExpression+MGLAdditions.mm b/platform/darwin/src/NSExpression+MGLAdditions.mm
index f19a41327a..c54102b8c9 100644
--- a/platform/darwin/src/NSExpression+MGLAdditions.mm
+++ b/platform/darwin/src/NSExpression+MGLAdditions.mm
@@ -63,6 +63,67 @@
return {};
}
+- (std::vector<mbgl::FeatureType>)mgl_aggregateFeatureType {
+ if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
+ std::vector<mbgl::FeatureType> convertedValues;
+ for (id value in self.constantValue) {
+ NSExpression *expression = value;
+ if (![expression isKindOfClass:[NSExpression class]]) {
+ expression = [NSExpression expressionForConstantValue:expression];
+ }
+ convertedValues.push_back(expression.mgl_featureType);
+ }
+ return convertedValues;
+ }
+ [NSException raise:NSInvalidArgumentException
+ format:@"Constant value expression must contain an array or set."];
+ return {};
+}
+
+- (mbgl::FeatureType)mgl_featureType {
+ id value = self.constantValue;
+ if ([value isKindOfClass:NSString.class]) {
+ if ([value isEqualToString:@"Point"]) {
+ return mbgl::FeatureType::Point;
+ }
+ if ([value isEqualToString:@"LineString"]) {
+ return mbgl::FeatureType::LineString;
+ }
+ if ([value isEqualToString:@"Polygon"]) {
+ return mbgl::FeatureType::Polygon;
+ }
+ } else if ([value isKindOfClass:NSNumber.class]) {
+ switch ([value integerValue]) {
+ case 1:
+ return mbgl::FeatureType::Point;
+ case 2:
+ return mbgl::FeatureType::LineString;
+ case 3:
+ return mbgl::FeatureType::Polygon;
+ default:
+ break;
+ }
+ }
+ return mbgl::FeatureType::Unknown;
+}
+
+- (std::vector<mbgl::FeatureIdentifier>)mgl_aggregateFeatureIdentifier {
+ if ([self.constantValue isKindOfClass:[NSArray class]] || [self.constantValue isKindOfClass:[NSSet class]]) {
+ std::vector<mbgl::FeatureIdentifier> convertedValues;
+ for (id value in self.constantValue) {
+ NSExpression *expression = value;
+ if (![expression isKindOfClass:[NSExpression class]]) {
+ expression = [NSExpression expressionForConstantValue:expression];
+ }
+ convertedValues.push_back(expression.mgl_featureIdentifier);
+ }
+ return convertedValues;
+ }
+ [NSException raise:NSInvalidArgumentException
+ format:@"Constant value expression must contain an array or set."];
+ return {};
+}
+
- (mbgl::FeatureIdentifier)mgl_featureIdentifier {
mbgl::Value mbglValue = self.mgl_constantMBGLValue;
diff --git a/platform/darwin/src/NSPredicate+MGLAdditions.mm b/platform/darwin/src/NSPredicate+MGLAdditions.mm
index 9fd6639b0a..e0511d8740 100644
--- a/platform/darwin/src/NSPredicate+MGLAdditions.mm
+++ b/platform/darwin/src/NSPredicate+MGLAdditions.mm
@@ -13,14 +13,41 @@ public:
return predicates;
}
- NSExpression *getValues(std::vector<mbgl::Value> values) {
+ template <typename MBGLType>
+ NSExpression *getValues(std::vector<MBGLType> values) {
NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()];
for (auto value : values) {
- id constantValue = mbgl::Value::visit(value, ValueEvaluator());
+ id constantValue = MBGLType::visit(value, ValueEvaluator());
[array addObject:[NSExpression expressionForConstantValue:constantValue]];
}
return [NSExpression expressionForAggregate:array];
}
+
+ NSString *getFeatureTypeString(mbgl::FeatureType type) {
+ switch (type) {
+ case mbgl::FeatureType::Point:
+ return @"Point";
+
+ case mbgl::FeatureType::LineString:
+ return @"LineString";
+
+ case mbgl::FeatureType::Polygon:
+ return @"Polygon";
+
+ default:
+ NSCAssert(NO, @"Unrecognized feature type %hhu", type);
+ return nil;
+ }
+ }
+
+ NSExpression *getFeatureTypeStrings(std::vector<mbgl::FeatureType> values) {
+ NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()];
+ for (auto value : values) {
+ id typeString = getFeatureTypeString(value);
+ [array addObject:[NSExpression expressionForConstantValue:typeString]];
+ }
+ return [NSExpression expressionForAggregate:array];
+ }
NSPredicate *operator()(mbgl::style::NullFilter filter) {
return nil;
@@ -57,6 +84,38 @@ public:
NSPredicate *operator()(mbgl::style::NotInFilter filter) {
return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @(filter.key.c_str()), getValues(filter.values)];
}
+
+ NSPredicate *operator()(mbgl::style::TypeEqualsFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K == %@", @"$type", getFeatureTypeString(filter.value)];
+ }
+
+ NSPredicate *operator()(mbgl::style::TypeNotEqualsFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K != %@", @"$type", getFeatureTypeString(filter.value)];
+ }
+
+ NSPredicate *operator()(mbgl::style::TypeInFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", getFeatureTypeStrings(filter.values)];
+ }
+
+ NSPredicate *operator()(mbgl::style::TypeNotInFilter filter) {
+ return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$type", getFeatureTypeStrings(filter.values)];
+ }
+
+ NSPredicate *operator()(mbgl::style::IdentifierEqualsFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K == %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())];
+ }
+
+ NSPredicate *operator()(mbgl::style::IdentifierNotEqualsFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K != %@", @"$id", mbgl::FeatureIdentifier::visit(filter.value, ValueEvaluator())];
+ }
+
+ NSPredicate *operator()(mbgl::style::IdentifierInFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", getValues(filter.values)];
+ }
+
+ NSPredicate *operator()(mbgl::style::IdentifierNotInFilter filter) {
+ return [NSPredicate predicateWithFormat:@"NOT %K IN %@", @"$id", getValues(filter.values)];
+ }
NSPredicate *operator()(mbgl::style::AnyFilter filter) {
NSArray *subpredicates = getPredicates(filter.filters);
@@ -127,7 +186,14 @@ public:
NSPredicate *operator()(mbgl::style::NotHasFilter filter) {
return [NSPredicate predicateWithFormat:@"%K == nil", @(filter.key.c_str())];
}
-
+
+ NSPredicate *operator()(mbgl::style::HasIdentifierFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
+ }
+
+ NSPredicate *operator()(mbgl::style::NotHasIdentifierFilter filter) {
+ return [NSPredicate predicateWithFormat:@"%K == nil", @"$id"];
+ }
};
@implementation NSPredicate (MGLAdditions)
diff --git a/platform/darwin/test/MGLExpressionTests.mm b/platform/darwin/test/MGLExpressionTests.mm
index 00b57c15f0..47315b97d6 100644
--- a/platform/darwin/test/MGLExpressionTests.mm
+++ b/platform/darwin/test/MGLExpressionTests.mm
@@ -212,4 +212,23 @@
XCTAssertTrue(convertedValue.is<mbgl::NullValue>());
}
+#pragma mark - Feature type tests
+
+- (void)testFeatureType {
+ XCTAssertEqual([NSExpression expressionWithFormat:@"'Point'"].mgl_featureType, mbgl::FeatureType::Point);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"'LineString'"].mgl_featureType, mbgl::FeatureType::LineString);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"'Polygon'"].mgl_featureType, mbgl::FeatureType::Polygon);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"'Unknown'"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"''"].mgl_featureType, mbgl::FeatureType::Unknown);
+
+ XCTAssertEqual([NSExpression expressionWithFormat:@"1"].mgl_featureType, mbgl::FeatureType::Point);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"2"].mgl_featureType, mbgl::FeatureType::LineString);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"3"].mgl_featureType, mbgl::FeatureType::Polygon);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"0"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"-1"].mgl_featureType, mbgl::FeatureType::Unknown);
+ XCTAssertEqual([NSExpression expressionWithFormat:@"4"].mgl_featureType, mbgl::FeatureType::Unknown);
+
+ XCTAssertEqual([NSExpression expressionWithFormat:@"nil"].mgl_featureType, mbgl::FeatureType::Unknown);
+}
+
@end
diff --git a/platform/darwin/test/MGLPredicateTests.mm b/platform/darwin/test/MGLPredicateTests.mm
index f34b480a25..6e6951dcdd 100644
--- a/platform/darwin/test/MGLPredicateTests.mm
+++ b/platform/darwin/test/MGLPredicateTests.mm
@@ -41,12 +41,48 @@ namespace mbgl {
mbgl::style::EqualsFilter expected = { .key = "a", .value = std::string("b") };
MGLAssertEqualFilters(actual, expected);
}
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"].mgl_filter;
+ mbgl::style::TypeEqualsFilter expected = { .value = mbgl::FeatureType::Point };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"].mgl_filter;
+ mbgl::style::IdentifierEqualsFilter expected = { .value = UINT64_C(67086180) };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"].mgl_filter;
+ mbgl::style::NotHasIdentifierFilter expected;
+ MGLAssertEqualFilters(actual, expected);
+ }
{
auto actual = [NSPredicate predicateWithFormat:@"a = nil"].mgl_filter;
mbgl::style::NotHasFilter expected = { .key = "a" };
MGLAssertEqualFilters(actual, expected);
}
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"].mgl_filter;
+ mbgl::style::TypeNotEqualsFilter expected = { .value = mbgl::FeatureType::Point };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"].mgl_filter;
+ mbgl::style::IdentifierNotEqualsFilter expected = { .value = UINT64_C(67086180) };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"].mgl_filter;
+ mbgl::style::HasIdentifierFilter expected;
+ MGLAssertEqualFilters(actual, expected);
+ }
{
auto actual = [NSPredicate predicateWithFormat:@"a != 'b'"].mgl_filter;
@@ -117,6 +153,30 @@ namespace mbgl {
mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
MGLAssertEqualFilters(actual, expected);
}
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"].mgl_filter;
+ mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$type", @[@"LineString", @"Polygon"]].mgl_filter;
+ mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"].mgl_filter;
+ mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%K IN %@", @"$id", @[@67086180, @3709678893, @3352016856, @4189833989]].mgl_filter;
+ mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
+ MGLAssertEqualFilters(actual, expected);
+ }
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"'Mapbox' IN a"].mgl_filter, NSException, NSInvalidArgumentException);
@@ -131,6 +191,24 @@ namespace mbgl {
mbgl::style::InFilter expected = { .key = "a", .values = { std::string("b"), std::string("c") } };
MGLAssertEqualFilters(actual, expected);
}
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@"LineString", @"Polygon"], @"$type"].mgl_filter;
+ mbgl::style::TypeInFilter expected = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"{67086180, 3709678893, 3352016856, 4189833989} CONTAINS %K", @"$id"].mgl_filter;
+ mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
+ MGLAssertEqualFilters(actual, expected);
+ }
+
+ {
+ auto actual = [NSPredicate predicateWithFormat:@"%@ CONTAINS %K", @[@67086180, @3709678893, @3352016856, @4189833989], @"$id"].mgl_filter;
+ mbgl::style::IdentifierInFilter expected = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
+ MGLAssertEqualFilters(actual, expected);
+ }
XCTAssertThrowsSpecificNamed([NSPredicate predicateWithFormat:@"a CONTAINS 'Mapbox'"].mgl_filter, NSException, NSInvalidArgumentException);
@@ -238,6 +316,41 @@ namespace mbgl {
mbgl::style::EqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a = 'b'"]);
}
+
+ {
+ mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Point };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Point'", @"$type"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::LineString };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'LineString'", @"$type"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Polygon };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 'Polygon'", @"$type"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::IdentifierEqualsFilter filter = { .value = UINT64_C(67086180) };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = 67086180", @"$id"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::NotHasIdentifierFilter filter;
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K = nil", @"$id"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::TypeEqualsFilter filter = { .value = mbgl::FeatureType::Unknown };
+ XCTAssertThrowsSpecificNamed([NSPredicate mgl_predicateWithFilter:filter], NSException, NSInternalInconsistencyException);
+ }
{
mbgl::style::NotHasFilter filter = { .key = "a" };
@@ -248,6 +361,24 @@ namespace mbgl {
mbgl::style::NotEqualsFilter filter = { .key = "a", .value = std::string("b") };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], [NSPredicate predicateWithFormat:@"a != 'b'"]);
}
+
+ {
+ mbgl::style::TypeNotEqualsFilter filter = { .value = mbgl::FeatureType::Point };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 'Point'", @"$type"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::IdentifierNotEqualsFilter filter = { .value = UINT64_C(67086180) };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != 67086180", @"$id"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
+
+ {
+ mbgl::style::HasIdentifierFilter filter;
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K != nil", @"$id"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
{
mbgl::style::HasFilter filter = { .key = "a" };
@@ -298,11 +429,35 @@ namespace mbgl {
mbgl::style::InFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"a IN {'b', 'c'}"].predicateFormat);
}
+
+ {
+ mbgl::style::TypeInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {'LineString', 'Polygon'}", @"$type"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat);
+ }
+
+ {
+ mbgl::style::IdentifierInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"%K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
{
mbgl::style::NotInFilter filter = { .key = "a", .values = { std::string("b"), std::string("c") } };
XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, [NSPredicate predicateWithFormat:@"NOT a IN {'b', 'c'}"].predicateFormat);
}
+
+ {
+ mbgl::style::TypeNotInFilter filter = { .values = { mbgl::FeatureType::LineString, mbgl::FeatureType::Polygon } };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {'LineString', 'Polygon'}", @"$type"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter].predicateFormat, expected.predicateFormat);
+ }
+
+ {
+ mbgl::style::IdentifierNotInFilter filter = { .values = { UINT64_C(67086180), UINT64_C(3709678893), UINT64_C(3352016856), UINT64_C(4189833989) } };
+ NSPredicate *expected = [NSPredicate predicateWithFormat:@"NOT %K IN {67086180, 3709678893, 3352016856, 4189833989}", @"$id"];
+ XCTAssertEqualObjects([NSPredicate mgl_predicateWithFilter:filter], expected);
+ }
{
mbgl::style::AllFilter filter;
diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md
index 5b4a81c6e3..f7e617b27e 100644
--- a/platform/ios/CHANGELOG.md
+++ b/platform/ios/CHANGELOG.md
@@ -23,7 +23,8 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT
* Removed support for the `ref` property in layers in style JSON files. ([#7586](https://github.com/mapbox/mapbox-gl-native/pull/7586))
* Fixed an issue that collapsed consecutive newlines within text labels. ([#7446](https://github.com/mapbox/mapbox-gl-native/pull/7446))
* Fixed artifacts when drawing particularly acute line joins. ([#7786](https://github.com/mapbox/mapbox-gl-native/pull/7786))
-* Fixed an issue in which a vector style layer predicate involving the `$id` key path would exclude all features from the layer. ([#7989](https://github.com/mapbox/mapbox-gl-native/pull/7989))
+* Fixed an issue in which a vector style layer predicate involving the `$id` key path would exclude all features from the layer. ([#7989](https://github.com/mapbox/mapbox-gl-native/pull/7989), [#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
+* Fixed an issue causing vector style layer predicates to be evaluated as if each feature had a `$type` attribute of 1, 2, or 3. The `$type` key path can now be compared to `Point`, `LineString`, or `Polygon`, as described in the documentation. ([#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
### Networking and offline maps
diff --git a/platform/macos/CHANGELOG.md b/platform/macos/CHANGELOG.md
index dc2c93a64a..3db58e3047 100644
--- a/platform/macos/CHANGELOG.md
+++ b/platform/macos/CHANGELOG.md
@@ -21,7 +21,8 @@
* Removed support for the `ref` property in layers in style JSON files. ([#7586](https://github.com/mapbox/mapbox-gl-native/pull/7586))
* Fixed an issue that collapsed consecutive newlines within text labels. ([#7446](https://github.com/mapbox/mapbox-gl-native/pull/7446))
* Fixed artifacts when drawing particularly acute line joins. ([#7786](https://github.com/mapbox/mapbox-gl-native/pull/7786))
-* Fixed an issue in which a vector style layer predicate involving the `$id` key path would exclude all features from the layer. ([#7989](https://github.com/mapbox/mapbox-gl-native/pull/7989))
+* Fixed an issue in which a vector style layer predicate involving the `$id` key path would exclude all features from the layer. ([#7989](https://github.com/mapbox/mapbox-gl-native/pull/7989), [#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
+* Fixed an issue causing vector style layer predicates to be evaluated as if each feature had a `$type` attribute of 1, 2, or 3. The `$type` key path can now be compared to `Point`, `LineString`, or `Polygon`, as described in the documentation. ([#7971](https://github.com/mapbox/mapbox-gl-native/pull/7971))
### Networking and offline maps
diff --git a/src/mbgl/style/conversion/stringify.hpp b/src/mbgl/style/conversion/stringify.hpp
index 4b9cf6d26e..4afbf198e5 100644
--- a/src/mbgl/style/conversion/stringify.hpp
+++ b/src/mbgl/style/conversion/stringify.hpp
@@ -103,6 +103,29 @@ void stringify(Writer& writer, const Value& v) {
}
template <class Writer>
+void stringify(Writer& writer, FeatureType type) {
+ switch (type) {
+ case FeatureType::Unknown:
+ writer.String("Unknown");
+ break;
+ case FeatureType::Point:
+ writer.String("Point");
+ break;
+ case FeatureType::LineString:
+ writer.String("LineString");
+ break;
+ case FeatureType::Polygon:
+ writer.String("Polygon");
+ break;
+ }
+}
+
+template <class Writer>
+void stringify(Writer& writer, const FeatureIdentifier& id) {
+ FeatureIdentifier::visit(id, [&] (const auto& id_) { stringify(writer, id_); });
+}
+
+template <class Writer>
class StringifyFilter {
public:
Writer& writer;
@@ -156,28 +179,78 @@ public:
}
void operator()(const HasFilter& f) {
- stringifyUnaryFilter(f, "has");
+ stringifyUnaryFilter("has", f.key);
}
void operator()(const NotHasFilter& f) {
- stringifyUnaryFilter(f, "!has");
+ stringifyUnaryFilter("!has", f.key);
+ }
+
+ void operator()(const TypeEqualsFilter& f) {
+ stringifyBinaryFilter(f, "==", "$type");
+ }
+
+ void operator()(const TypeNotEqualsFilter& f) {
+ stringifyBinaryFilter(f, "!=", "$type");
+ }
+
+ void operator()(const TypeInFilter& f) {
+ stringifySetFilter(f, "in", "$type");
+ }
+
+ void operator()(const TypeNotInFilter& f) {
+ stringifySetFilter(f, "!in", "$type");
+ }
+
+ void operator()(const IdentifierEqualsFilter& f) {
+ stringifyBinaryFilter(f, "==", "$id");
+ }
+
+ void operator()(const IdentifierNotEqualsFilter& f) {
+ stringifyBinaryFilter(f, "!=", "$id");
+ }
+
+ void operator()(const IdentifierInFilter& f) {
+ stringifySetFilter(f, "in", "$id");
+ }
+
+ void operator()(const IdentifierNotInFilter& f) {
+ stringifySetFilter(f, "!in", "$id");
+ }
+
+ void operator()(const HasIdentifierFilter&) {
+ stringifyUnaryFilter("has", "$id");
+ }
+
+ void operator()(const NotHasIdentifierFilter&) {
+ stringifyUnaryFilter("!has", "$id");
}
private:
template <class F>
void stringifyBinaryFilter(const F& f, const char * op) {
+ stringifyBinaryFilter(f, op, f.key);
+ }
+
+ template <class F>
+ void stringifyBinaryFilter(const F& f, const char * op, const std::string& key) {
writer.StartArray();
writer.String(op);
- writer.String(f.key);
+ writer.String(key);
stringify(writer, f.value);
writer.EndArray();
}
template <class F>
void stringifySetFilter(const F& f, const char * op) {
+ stringifySetFilter(f, op, f.key);
+ }
+
+ template <class F>
+ void stringifySetFilter(const F& f, const char * op, const std::string& key) {
writer.StartArray();
writer.String(op);
- writer.String(f.key);
+ writer.String(key);
for (const auto& value : f.values) {
stringify(writer, value);
}
@@ -194,11 +267,10 @@ private:
writer.EndArray();
}
- template <class F>
- void stringifyUnaryFilter(const F& f, const char * op) {
+ void stringifyUnaryFilter(const char * op, const std::string& key) {
writer.StartArray();
writer.String(op);
- writer.String(f.key);
+ writer.String(key);
writer.EndArray();
}
};