Skip to content
This repository has been archived by the owner on Aug 8, 2023. It is now read-only.

Commit

Permalink
[core] Add support for $id key to filters
Browse files Browse the repository at this point in the history
  • Loading branch information
jfirebaugh committed Jul 15, 2016
1 parent 5c037f4 commit 007a61b
Show file tree
Hide file tree
Showing 7 changed files with 101 additions and 76 deletions.
4 changes: 3 additions & 1 deletion include/mbgl/style/filter.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,8 +107,10 @@ class Filter : public FilterBase {
public:
using FilterBase::FilterBase;

bool operator()(const Feature&) const;

template <class PropertyAccessor>
bool operator()(FeatureType type, PropertyAccessor accessor) const;
bool operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const;
};

} // namespace style
Expand Down
30 changes: 25 additions & 5 deletions include/mbgl/style/filter_evaluator.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ template <class PropertyAccessor>
class FilterEvaluator {
public:
const FeatureType featureType;
const optional<FeatureIdentifier> featureIdentifier;
const PropertyAccessor propertyAccessor;

bool operator()(const NullFilter&) const {
Expand Down Expand Up @@ -123,9 +124,19 @@ class FilterEvaluator {

private:
optional<Value> getValue(const std::string& key_) const {
return key_ == "$type"
? optional<Value>(uint64_t(featureType))
: propertyAccessor(key_);
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>();
}
} else {
return propertyAccessor(key_);
}
}

template <class Op>
Expand Down Expand Up @@ -183,9 +194,18 @@ class FilterEvaluator {
}
};

inline bool Filter::operator()(const Feature& feature) const {
return operator()(apply_visitor(ToFeatureType(), feature.geometry), feature.id, [&] (const std::string& key) -> optional<Value> {
auto it = feature.properties.find(key);
if (it == feature.properties.end())
return {};
return it->second;
});
}

template <class PropertyAccessor>
bool Filter::operator()(FeatureType type, PropertyAccessor accessor) const {
return FilterBase::visit(*this, FilterEvaluator<PropertyAccessor> { type, accessor });
bool Filter::operator()(FeatureType type, optional<FeatureIdentifier> id, PropertyAccessor accessor) const {
return FilterBase::visit(*this, FilterEvaluator<PropertyAccessor> { type, id, accessor });
}

} // namespace style
Expand Down
6 changes: 1 addition & 5 deletions include/mbgl/util/feature.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,6 @@ using Value = mapbox::geometry::value;
using NullValue = mapbox::geometry::null_value_t;
using PropertyMap = mapbox::geometry::property_map;
using FeatureIdentifier = mapbox::geometry::identifier;
class Feature : public mapbox::geometry::feature<double> {
public:
Feature(geometry_type&& geometry_)
: mapbox::geometry::feature<double> { std::move(geometry_) } {}
};
using Feature = mapbox::geometry::feature<double>;

} // namespace mbgl
2 changes: 1 addition & 1 deletion src/mbgl/annotation/shape_annotation_impl.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ void ShapeAnnotationImpl::updateTileData(const CanonicalTileID& tileID, Annotati
if (!shapeTiler) {
mapbox::geometry::feature_collection<double> features;
features.emplace_back(ShapeAnnotationGeometry::visit(geometry(), [] (auto&& geom) {
return Feature(std::move(geom));
return Feature { std::move(geom) };
}));
mapbox::geojsonvt::Options options;
options.maxZoom = util::clamp<uint8_t>(maxZoom, 0, 18);
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/renderer/symbol_bucket.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ void SymbolBucket::parseFeatures(const GeometryTileLayer& layer, const Filter& f
const GLsizei featureCount = static_cast<GLsizei>(layer.featureCount());
for (GLsizei i = 0; i < featureCount; i++) {
auto feature = layer.getFeature(i);
if (!filter(feature->getType(), [&] (const auto& key) { return feature->getValue(key); }))
if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
continue;

SymbolFeature ft;
Expand Down
2 changes: 1 addition & 1 deletion src/mbgl/style/bucket_parameters.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ void BucketParameters::eachFilteredFeature(const Filter& filter,
auto name = layer.getName();
for (std::size_t i = 0; !cancelled() && i < layer.featureCount(); i++) {
auto feature = layer.getFeature(i);
if (!filter(feature->getType(), [&] (const auto& key) { return feature->getValue(key); }))
if (!filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); }))
continue;
function(*feature, i, name);
}
Expand Down
131 changes: 69 additions & 62 deletions test/style/filter.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
#include <mbgl/test/util.hpp>
#include <mbgl/util/feature.hpp>
#include <mbgl/util/geometry.hpp>

#include <mbgl/style/filter.hpp>
#include <mbgl/style/filter_evaluator.hpp>
Expand All @@ -17,97 +19,102 @@ Filter parse(const char * expression) {
return *conversion::convert<Filter>(doc);
}

bool evaluate(const Filter& filter, const PropertyMap& properties, FeatureType type = FeatureType::Unknown) {
return filter(type, [&] (const std::string& key) -> optional<Value> {
auto it = properties.find(key);
if (it == properties.end())
return {};
return it->second;
});
Feature feature(const PropertyMap& properties, const Geometry<double>& geometry = Point<double>()) {
Feature result { geometry };
result.properties = properties;
return result;
}

TEST(Filter, EqualsString) {
Filter f = parse("[\"==\", \"foo\", \"bar\"]");
ASSERT_TRUE(evaluate(f, {{ "foo", std::string("bar") }}));
ASSERT_FALSE(evaluate(f, {{ "foo", std::string("baz") }}));
ASSERT_TRUE(f(feature({{ "foo", std::string("bar") }})));
ASSERT_FALSE(f(feature({{ "foo", std::string("baz") }})));
}

TEST(Filter, EqualsNumber) {
Filter f = parse("[\"==\", \"foo\", 0]");
ASSERT_TRUE(evaluate(f, {{ "foo", int64_t(0) }}));
ASSERT_TRUE(evaluate(f, {{ "foo", uint64_t(0) }}));
ASSERT_TRUE(evaluate(f, {{ "foo", double(0) }}));
ASSERT_FALSE(evaluate(f, {{ "foo", int64_t(1) }}));
ASSERT_FALSE(evaluate(f, {{ "foo", uint64_t(1) }}));
ASSERT_FALSE(evaluate(f, {{ "foo", double(1) }}));
ASSERT_FALSE(evaluate(f, {{ "foo", std::string("0") }}));
ASSERT_FALSE(evaluate(f, {{ "foo", false }}));
ASSERT_FALSE(evaluate(f, {{ "foo", true }}));
ASSERT_FALSE(evaluate(f, {{ "foo", nullptr }}));
ASSERT_FALSE(evaluate(f, {{}}));
ASSERT_TRUE(f(feature({{ "foo", int64_t(0) }})));
ASSERT_TRUE(f(feature({{ "foo", uint64_t(0) }})));
ASSERT_TRUE(f(feature({{ "foo", double(0) }})));
ASSERT_FALSE(f(feature({{ "foo", int64_t(1) }})));
ASSERT_FALSE(f(feature({{ "foo", uint64_t(1) }})));
ASSERT_FALSE(f(feature({{ "foo", double(1) }})));
ASSERT_FALSE(f(feature({{ "foo", std::string("0") }})));
ASSERT_FALSE(f(feature({{ "foo", false }})));
ASSERT_FALSE(f(feature({{ "foo", true }})));
ASSERT_FALSE(f(feature({{ "foo", nullptr }})));
ASSERT_FALSE(f(feature({{}})));
}

TEST(Filter, EqualsType) {
Filter f = parse("[\"==\", \"$type\", \"LineString\"]");
ASSERT_FALSE(evaluate(f, {{}}, FeatureType::Point));
ASSERT_TRUE(evaluate(f, {{}}, FeatureType::LineString));
ASSERT_FALSE(f(feature({{}}, Point<double>())));
ASSERT_TRUE(f(feature({{}}, LineString<double>())));
}

TEST(Filter, InType) {
Filter f = parse("[\"in\", \"$type\", \"LineString\", \"Polygon\"]");
ASSERT_FALSE(evaluate(f, {{}}, FeatureType::Point));
ASSERT_TRUE(evaluate(f, {{}}, FeatureType::LineString));
ASSERT_TRUE(evaluate(f, {{}}, FeatureType::Polygon));
ASSERT_FALSE(f(feature({{}}, Point<double>())));
ASSERT_TRUE(f(feature({{}}, LineString<double>())));
ASSERT_TRUE(f(feature({{}}, Polygon<double>())));
}

TEST(Filter, Any) {
ASSERT_FALSE(evaluate(parse("[\"any\"]"), {{}}));
ASSERT_TRUE(evaluate(parse("[\"any\", [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_FALSE(evaluate(parse("[\"any\", [\"==\", \"foo\", 0]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_TRUE(evaluate(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_FALSE(parse("[\"any\"]")(feature({{}})));
ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 1]]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_FALSE(parse("[\"any\", [\"==\", \"foo\", 0]]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_TRUE(parse("[\"any\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
feature({{ std::string("foo"), int64_t(1) }})));
}

TEST(Filter, All) {
ASSERT_TRUE(evaluate(parse("[\"all\"]"), {{}}));
ASSERT_TRUE(evaluate(parse("[\"all\", [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_FALSE(evaluate(parse("[\"all\", [\"==\", \"foo\", 0]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_FALSE(evaluate(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_TRUE(parse("[\"all\"]")(feature({{}})));
ASSERT_TRUE(parse("[\"all\", [\"==\", \"foo\", 1]]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0]]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_FALSE(parse("[\"all\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
feature({{ std::string("foo"), int64_t(1) }})));
}

TEST(Filter, None) {
ASSERT_TRUE(evaluate(parse("[\"none\"]"), {{}}));
ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_TRUE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_FALSE(evaluate(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_TRUE(parse("[\"none\"]")(feature({{}})));
ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 1]]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_TRUE(parse("[\"none\", [\"==\", \"foo\", 0]]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_FALSE(parse("[\"none\", [\"==\", \"foo\", 0], [\"==\", \"foo\", 1]]")(
feature({{ std::string("foo"), int64_t(1) }})));
}

TEST(Filter, Has) {
ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
{{ std::string("foo"), int64_t(0) }}));
ASSERT_TRUE(evaluate(parse("[\"has\", \"foo\"]"),
{{ std::string("foo"), false }}));
ASSERT_FALSE(evaluate(parse("[\"has\", \"foo\"]"),
{{}}));
ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
feature({{ std::string("foo"), int64_t(0) }})));
ASSERT_TRUE(parse("[\"has\", \"foo\"]")(
feature({{ std::string("foo"), false }})));
ASSERT_FALSE(parse("[\"has\", \"foo\"]")(
feature({{}})));
}

TEST(Filter, NotHas) {
ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
{{ std::string("foo"), int64_t(1) }}));
ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
{{ std::string("foo"), int64_t(0) }}));
ASSERT_FALSE(evaluate(parse("[\"!has\", \"foo\"]"),
{{ std::string("foo"), false }}));
ASSERT_TRUE(evaluate(parse("[\"!has\", \"foo\"]"),
{{}}));
ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
feature({{ std::string("foo"), int64_t(1) }})));
ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
feature({{ std::string("foo"), int64_t(0) }})));
ASSERT_FALSE(parse("[\"!has\", \"foo\"]")(
feature({{ std::string("foo"), false }})));
ASSERT_TRUE(parse("[\"!has\", \"foo\"]")(
feature({{}})));
}

TEST(Filter, ID) {
Feature feature { Point<double>() };
feature.id = { 1234 };

ASSERT_TRUE(parse("[\"==\", \"$id\", 1234]")(feature));
ASSERT_FALSE(parse("[\"==\", \"$id\", \"1234\"]")(feature));
}

0 comments on commit 007a61b

Please sign in to comment.