#import "NSPredicate+MGLAdditions.h" #import "MGLValueEvaluator.h" class FilterEvaluator { public: NSArray *getPredicates(std::vector filters) { NSMutableArray *predicates = [NSMutableArray arrayWithCapacity:filters.size()]; for (auto filter : filters) { [predicates addObject:mbgl::style::Filter::visit(filter, FilterEvaluator())]; } return predicates; } template NSExpression *getValues(std::vector values) { NSMutableArray *array = [NSMutableArray arrayWithCapacity:values.size()]; for (auto value : values) { 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 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; } NSPredicate *operator()(mbgl::style::EqualsFilter filter) { return [NSPredicate predicateWithFormat:@"%K == %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; } NSPredicate *operator()(mbgl::style::NotEqualsFilter filter) { return [NSPredicate predicateWithFormat:@"%K != %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; } NSPredicate *operator()(mbgl::style::GreaterThanFilter filter) { return [NSPredicate predicateWithFormat:@"%K > %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; } NSPredicate *operator()(mbgl::style::GreaterThanEqualsFilter filter) { return [NSPredicate predicateWithFormat:@"%K >= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; } NSPredicate *operator()(mbgl::style::LessThanFilter filter) { return [NSPredicate predicateWithFormat:@"%K < %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; } NSPredicate *operator()(mbgl::style::LessThanEqualsFilter filter) { return [NSPredicate predicateWithFormat:@"%K <= %@", @(filter.key.c_str()), mbgl::Value::visit(filter.value, ValueEvaluator())]; } NSPredicate *operator()(mbgl::style::InFilter filter) { return [NSPredicate predicateWithFormat:@"%K IN %@", @(filter.key.c_str()), getValues(filter.values)]; } 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); if (subpredicates.count) { return [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; } return [NSPredicate predicateWithValue:NO]; } NSPredicate *operator()(mbgl::style::AllFilter filter) { // Convert [all, [>=, key, lower], [<=, key, upper]] to key BETWEEN {lower, upper} if (filter.filters.size() == 2) { auto leftFilter = filter.filters[0]; auto rightFilter = filter.filters[1]; std::string lowerKey; std::string upperKey; mbgl::Value lowerBound; mbgl::Value upperBound; if (leftFilter.is()) { lowerKey = leftFilter.get().key; lowerBound = leftFilter.get().value; } else if (rightFilter.is()) { lowerKey = rightFilter.get().key; lowerBound = rightFilter.get().value; } if (leftFilter.is()) { upperKey = leftFilter.get().key; upperBound = leftFilter.get().value; } else if (rightFilter.is()) { upperKey = rightFilter.get().key; upperBound = rightFilter.get().value; } if (!lowerBound.is() && !upperBound.is() && lowerKey == upperKey) { return [NSPredicate predicateWithFormat:@"%K BETWEEN {%@, %@}", @(lowerKey.c_str()), mbgl::Value::visit(lowerBound, ValueEvaluator()), mbgl::Value::visit(upperBound, ValueEvaluator())]; } } NSArray *subpredicates = getPredicates(filter.filters); if (subpredicates.count) { return [NSCompoundPredicate andPredicateWithSubpredicates:subpredicates]; } return [NSPredicate predicateWithValue:YES]; } NSPredicate *operator()(mbgl::style::NoneFilter filter) { NSArray *subpredicates = getPredicates(filter.filters); if (subpredicates.count > 1) { NSCompoundPredicate *predicate = [NSCompoundPredicate orPredicateWithSubpredicates:subpredicates]; return [NSCompoundPredicate notPredicateWithSubpredicate:predicate]; } else if (subpredicates.count) { return [NSCompoundPredicate notPredicateWithSubpredicate:subpredicates.firstObject]; } else { return [NSPredicate predicateWithValue:YES]; } } NSPredicate *operator()(mbgl::style::HasFilter filter) { return [NSPredicate predicateWithFormat:@"%K != nil", @(filter.key.c_str())]; } 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) - (mbgl::style::Filter)mgl_filter { if ([self isEqual:[NSPredicate predicateWithValue:YES]]) { return mbgl::style::AllFilter(); } if ([self isEqual:[NSPredicate predicateWithValue:NO]]) { return mbgl::style::AnyFilter(); } if ([self.predicateFormat hasPrefix:@"BLOCKPREDICATE("]) { [NSException raise:NSInvalidArgumentException format:@"Block-based predicates are not supported."]; } [NSException raise:NSInvalidArgumentException format:@"Unrecognized predicate type."]; return {}; } + (instancetype)mgl_predicateWithFilter:(mbgl::style::Filter)filter { FilterEvaluator evaluator; return mbgl::style::Filter::visit(filter, evaluator); } @end