summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-06-26 15:04:18 +0200
committerKonstantin Käfer <mail@kkaefer.com>2014-06-26 15:04:18 +0200
commitaff667d7427ba341edb93386d2b6690aa8aa0a8a (patch)
treeb0dbdc391ce9296da085cb21dac7b8e6c859ef50
parentb95d023a9803f68b4f7abfba1d66ca0045c946b2 (diff)
downloadqtlocation-mapboxgl-aff667d7427ba341edb93386d2b6690aa8aa0a8a.tar.gz
add filter expressions
-rw-r--r--include/llmr/map/filter_expression.hpp181
-rw-r--r--include/llmr/map/tile_parser.hpp12
-rw-r--r--include/llmr/map/vector_tile.hpp25
-rw-r--r--include/llmr/style/bucket_description.hpp2
-rw-r--r--include/llmr/style/style_bucket.hpp3
-rw-r--r--include/llmr/style/style_parser.hpp9
-rw-r--r--include/llmr/style/value.hpp12
-rw-r--r--include/llmr/util/recursive_wrapper.hpp31
-rw-r--r--include/llmr/util/variant.hpp453
-rw-r--r--src/map/filter_expression.cpp282
-rw-r--r--src/map/map.cpp2
-rw-r--r--src/map/tile_parser.cpp18
-rw-r--r--src/map/vector_tile.cpp199
-rw-r--r--src/style/style_parser.cpp165
14 files changed, 974 insertions, 420 deletions
diff --git a/include/llmr/map/filter_expression.hpp b/include/llmr/map/filter_expression.hpp
index 50e6ce1d78..3c4ac857e5 100644
--- a/include/llmr/map/filter_expression.hpp
+++ b/include/llmr/map/filter_expression.hpp
@@ -4,125 +4,114 @@
#include <llmr/style/value.hpp>
#include <llmr/util/recursive_wrapper.hpp>
-#include <vector>
+#include <forward_list>
#include <string>
+#include <ostream>
+#include <iostream>
namespace llmr {
-enum class FilterOperator {
- Equal,
- NotEqual,
- Greater,
- GreaterEqual,
- Less,
- LessEqual
-};
+class VectorTileTagExtractor;
-enum class ExpressionOperator {
- Or,
- And
-};
+class FilterComparison {
+public:
+ enum class Operator : uint8_t {
+ Equal,
+ NotEqual,
+ Greater,
+ GreaterEqual,
+ Less,
+ LessEqual,
+ In,
+ NotIn
+ };
+
+ class Instance {
+ public:
+ Instance(Operator op, std::forward_list<Value> &&values)
+ : op(op), values(values) {}
+
+ bool compare(const Value &property_value) const;
+ bool compare(const std::forward_list<Value> &property_values) const;
+
+ private:
+ template <typename Comparer> inline bool includes(const Value &property_value, const Comparer &comparer) const;
+ template <typename Comparer> inline bool compare(const Value &property_value, const Comparer &comparer) const;
+ template <typename Comparer> inline bool all(const std::forward_list<Value> &property_values, const Comparer &comparer) const;
+
+ private:
+ Operator op = Operator::Equal;
+ std::forward_list<Value> values;
+
+ friend std::ostream& operator <<(std::ostream &, const Instance &);
+ };
+public:
+ FilterComparison(const std::string &field) : field(field) {};
-inline FilterOperator filterOperatorType(const std::string &op) {
- if (op == "!=" || op == "not") {
- return FilterOperator::NotEqual;
- } else if (op == "==" || op == "eq") {
- return FilterOperator::Equal;
- } else if (op == ">" || op == "gt") {
- return FilterOperator::Greater;
- } else if (op == ">=" || op == "gte") {
- return FilterOperator::GreaterEqual;
- } else if (op == "<" || op == "lt") {
- return FilterOperator::Less;
- } else if (op == "<=" || op == "lte") {
- return FilterOperator::LessEqual;
- } else {
- fprintf(stderr, "[WARNING] filter operator '%s' unrecognized\n", op.c_str());
- return FilterOperator::Equal;
- }
-}
+ const std::string &getField() const;
+ inline bool compare(const VectorTileTagExtractor &extractor) const;
-inline ExpressionOperator expressionOperatorType(const std::string &op) {
- if (op == "&&" || op == "and") {
- return ExpressionOperator::And;
- } else if (op == "||" || op == "or") {
- return ExpressionOperator::Or;
- } else {
- fprintf(stderr, "[WARNING] expression operator '%s' unrecognized\n", op.c_str());
- return ExpressionOperator::Or;
+ template <typename ...Args>
+ inline void add(Args&& ...args) {
+ instances.emplace_front(::std::forward<Args>(args)...);
}
-}
+private:
+ std::string field;
+ std::forward_list<Instance> instances;
-class PropertyFilter;
-class PropertyExpression;
+ friend std::ostream& operator <<(std::ostream &, const FilterComparison &);
+};
-typedef util::variant<
- util::recursive_wrapper<PropertyFilter>,
- util::recursive_wrapper<PropertyExpression>,
- std::true_type
-> PropertyFilterExpression;
+std::ostream& operator <<(std::ostream &s, const FilterComparison &comparison);
+std::ostream& operator <<(std::ostream &s, const FilterComparison::Instance &instance);
-class PropertyFilter {
-public:
- inline PropertyFilter(const std::string &field, FilterOperator op, const Value &value) : field(field), op(op), value(value) {};
- inline PropertyFilter(PropertyFilter &&filter)
- : field(std::move(filter.field)),
- op(filter.op),
- value(std::move(filter.value)) {
- }
+FilterComparison::Operator parseFilterComparisonOperator(const std::string &op);
- // 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::Equal:
- return util::relaxed_equal(other, value);
- case FilterOperator::NotEqual:
- return !util::relaxed_equal(other, value);
- case FilterOperator::Greater:
- return util::relaxed_greater(other, value);
- case FilterOperator::GreaterEqual:
- return util::relaxed_greater_equal(other, value);
- case FilterOperator::Less:
- return util::relaxed_less(other, value);
- case FilterOperator::LessEqual:
- return util::relaxed_less_equal(other, value);
- default:
- return false;
- }
- }
+class FilterExpression {
public:
- std::string field;
- FilterOperator op = FilterOperator::Equal;
- Value value;
-};
+ typedef util::recursive_wrapper<FilterExpression> Wrapper;
+
+ enum class Operator : uint8_t {
+ And,
+ Or,
+ Xor,
+ Nor
+ };
+
+ enum class GeometryType : uint8_t {
+ Any,
+ Point,
+ Line,
+ Polygon
+ };
-class PropertyExpression {
public:
- inline PropertyExpression() {}
- inline PropertyExpression(PropertyExpression &&expression)
- : op(expression.op),
- operands(std::move(expression.operands)) {
- }
+ FilterExpression() = default;
+ FilterExpression(Operator op) : op(op) {};
-public:
- ExpressionOperator op = ExpressionOperator::Or;
- std::vector<PropertyFilterExpression> operands;
+ bool empty() const;
+
+ bool compare(const VectorTileTagExtractor &extractor) const;
+ void add(const FilterComparison &comparison);
+ void add(const FilterExpression &expression);
+ void setGeometryType(GeometryType g);
+
+private:
+ Operator op = Operator::And;
+ GeometryType type = GeometryType::Any;
+ std::forward_list<FilterComparison> comparisons;
+ std::forward_list<FilterExpression::Wrapper> expressions;
+
+ friend std::ostream& operator <<(std::ostream &, const FilterExpression &);
};
+std::ostream& operator <<(std::ostream &s, const FilterExpression &expression);
+FilterExpression::GeometryType parseGeometryType(const std::string &geometry);
}
#endif
diff --git a/include/llmr/map/tile_parser.hpp b/include/llmr/map/tile_parser.hpp
index 9e763a13af..2809c6b4e7 100644
--- a/include/llmr/map/tile_parser.hpp
+++ b/include/llmr/map/tile_parser.hpp
@@ -42,13 +42,13 @@ private:
void addGlyph(uint64_t tileid, const std::string stackname, const std::u32string &string, const FontStack &fontStack, GlyphAtlas &glyphAtlas, GlyphPositions &face);
std::unique_ptr<Bucket> createBucket(std::shared_ptr<StyleBucket> bucket_desc);
- std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketFill &fill);
- std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketLine &line);
- std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketIcon &icon);
- std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketText &text);
+ std::unique_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill);
+ std::unique_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line);
+ std::unique_ptr<Bucket> createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon);
+ std::unique_ptr<Bucket> createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text);
- template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const PropertyFilterExpression &filter);
- template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const PropertyFilterExpression &filter, Args&& ...args);
+ template <class Bucket> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter);
+ template <class Bucket, typename ...Args> void addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args);
private:
const VectorTile vector_data;
diff --git a/include/llmr/map/vector_tile.hpp b/include/llmr/map/vector_tile.hpp
index b2f3d416e3..f9e01c38b6 100644
--- a/include/llmr/map/vector_tile.hpp
+++ b/include/llmr/map/vector_tile.hpp
@@ -40,6 +40,22 @@ public:
std::ostream& operator<<(std::ostream&, const VectorTileFeature& feature);
+
+class VectorTileTagExtractor {
+public:
+ VectorTileTagExtractor(const VectorTileLayer &layer);
+
+ void setTags(const pbf &pbf);
+ std::forward_list<Value> getValues(const std::string &key) const;
+ void setType(FilterExpression::GeometryType type);
+ FilterExpression::GeometryType getType() const;
+
+private:
+ const VectorTileLayer &layer_;
+ pbf tags_;
+ FilterExpression::GeometryType type_ = FilterExpression::GeometryType::Any;
+};
+
/*
* Allows iterating over the features of a VectorTileLayer using a
* BucketDescription as filter. Only features matching the descriptions will
@@ -55,11 +71,6 @@ public:
const pbf& operator*() const;
private:
- 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;
bool valid = false;
pbf feature;
@@ -67,14 +78,14 @@ public:
};
public:
- FilteredVectorTileLayer(const VectorTileLayer& layer, const PropertyFilterExpression &filterExpression);
+ FilteredVectorTileLayer(const VectorTileLayer& layer, const FilterExpression &filterExpression);
iterator begin() const;
iterator end() const;
private:
const VectorTileLayer& layer;
- const PropertyFilterExpression& filterExpression;
+ const FilterExpression& filterExpression;
};
std::ostream& operator<<(std::ostream&, const GlyphPlacement& placement);
diff --git a/include/llmr/style/bucket_description.hpp b/include/llmr/style/bucket_description.hpp
index 9b1bb5c774..6011b53990 100644
--- a/include/llmr/style/bucket_description.hpp
+++ b/include/llmr/style/bucket_description.hpp
@@ -22,7 +22,7 @@ public:
std::string source_name;
std::string source_layer;
- PropertyFilterExpression filter = std::true_type();
+ FilterExpression filter;
// Specifies how the geometry for this bucket should be created
StyleBucketRender render;
diff --git a/include/llmr/style/style_bucket.hpp b/include/llmr/style/style_bucket.hpp
index bd6dcf7147..eee9929a89 100644
--- a/include/llmr/style/style_bucket.hpp
+++ b/include/llmr/style/style_bucket.hpp
@@ -7,6 +7,7 @@
#include <llmr/util/variant.hpp>
#include <memory>
+#include <forward_list>
namespace llmr {
@@ -73,7 +74,7 @@ public:
std::string name;
std::shared_ptr<Source> source;
std::string source_layer;
- PropertyFilterExpression filter = std::true_type();
+ FilterExpression filter;
StyleBucketRender render = std::false_type();
};
diff --git a/include/llmr/style/style_parser.hpp b/include/llmr/style/style_parser.hpp
index cdfd61094e..29a41fb141 100644
--- a/include/llmr/style/style_parser.hpp
+++ b/include/llmr/style/style_parser.hpp
@@ -39,7 +39,6 @@ private:
void parseRasterize(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseReference(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer);
- void parseFilter(JSVal value, std::shared_ptr<StyleLayer> &layer);
void parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer);
// Parses optional properties into a render bucket.
@@ -54,9 +53,11 @@ private:
template <typename T>
bool parseStyleProperty(const char *property_name, const std::vector<ClassPropertyKey> &keys, ClassProperties &klass, JSVal value);
-// PropertyFilterExpression parseFilterOrExpression(JSVal value);
-// Value parseValue(JSVal value);
-//
+
+ FilterExpression parseFilter(JSVal, FilterExpression::Operator op);
+ FilterExpression parseFilter(JSVal);
+ Value parseValue(JSVal value);
+ std::forward_list<Value> parseValues(JSVal values);
private:
std::unordered_map<std::string, const rapidjson::Value *> constants;
diff --git a/include/llmr/style/value.hpp b/include/llmr/style/value.hpp
index 8734386253..bdcba4cad3 100644
--- a/include/llmr/style/value.hpp
+++ b/include/llmr/style/value.hpp
@@ -62,6 +62,8 @@ template <> struct string_to_number<uint64_t> {
template <typename Operator>
struct relaxed_operator_visitor {
+ typedef bool result_type;
+
bool operator()(bool lhs, bool rhs) const { return Operator()(lhs, rhs); }
template <typename T, class = typename std::enable_if<std::is_arithmetic<T>::value>::type>
@@ -114,23 +116,23 @@ struct relaxed_less_equal_operator {
} // end namespace detail
inline bool relaxed_equal(Value const &lhs, Value const &rhs) {
- return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_equal_operator>());
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_equal_operator>(), lhs, rhs);
}
inline bool relaxed_greater(Value const &lhs, Value const &rhs) {
- return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_greater_operator>());
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_greater_operator>(), lhs, rhs);
}
inline bool relaxed_greater_equal(Value const &lhs, Value const &rhs) {
- return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_greater_equal_operator>());
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_greater_equal_operator>(), lhs, rhs);
}
inline bool relaxed_less(Value const &lhs, Value const &rhs) {
- return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_less_operator>());
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_less_operator>(), lhs, rhs);
}
inline bool relaxed_less_equal(Value const &lhs, Value const &rhs) {
- return apply_visitor(lhs, rhs, detail::relaxed_operator_visitor<detail::relaxed_less_equal_operator>());
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_less_equal_operator>(), lhs, rhs);
}
}
diff --git a/include/llmr/util/recursive_wrapper.hpp b/include/llmr/util/recursive_wrapper.hpp
index 557b576c03..b87dc5699a 100644
--- a/include/llmr/util/recursive_wrapper.hpp
+++ b/include/llmr/util/recursive_wrapper.hpp
@@ -1,19 +1,15 @@
-#ifndef LLMR_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
-#define LLMR_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
+#ifndef MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
+#define MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
#include <utility>
-namespace llmr {
-
-namespace util {
+namespace llmr { namespace util {
template <typename T>
class recursive_wrapper
{
public:
-
- typedef T type;
-
+ using type = T;
private:
T* p_;
@@ -34,19 +30,19 @@ private:
public:
- recursive_wrapper& operator=(const recursive_wrapper& rhs)
+ inline recursive_wrapper& operator=(recursive_wrapper const& rhs)
{
assign( rhs.get() );
return *this;
}
- recursive_wrapper& operator=(const T& rhs)
+ inline recursive_wrapper& operator=(T const& rhs)
{
assign( rhs );
return *this;
}
- void swap(recursive_wrapper& operand) noexcept
+ inline void swap(recursive_wrapper& operand) noexcept
{
T* temp = operand.p_;
operand.p_ = p_;
@@ -71,10 +67,10 @@ public:
T& get() { return *get_pointer(); }
const T& get() const { return *get_pointer(); }
-
T* get_pointer() { return p_; }
const T* get_pointer() const { return p_; }
-
+ operator T const&() const { return this->get(); }
+ operator T&() { return this->get(); }
};
template <typename T>
@@ -103,8 +99,9 @@ recursive_wrapper<T>::recursive_wrapper(T const& operand)
template <typename T>
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand)
- : p_(new T( std::move(operand.get()) ))
+ : p_(operand.p_)
{
+ operand.p_ = nullptr;
}
template <typename T>
@@ -125,8 +122,6 @@ inline void swap(recursive_wrapper<T>& lhs, recursive_wrapper<T>& rhs) noexcept
lhs.swap(rhs);
}
-}
-
-}
+}}
-#endif // LLMR_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
+#endif // MAPBOX_UTIL_VARIANT_RECURSIVE_WRAPPER_HPP
diff --git a/include/llmr/util/variant.hpp b/include/llmr/util/variant.hpp
index b887ae494d..57f8593439 100644
--- a/include/llmr/util/variant.hpp
+++ b/include/llmr/util/variant.hpp
@@ -1,5 +1,5 @@
-#ifndef UTIL_VARIANT_HPP
-#define UTIL_VARIANT_HPP
+#ifndef MAPBOX_UTIL_VARIANT_HPP
+#define MAPBOX_UTIL_VARIANT_HPP
#include <utility>
#include <typeinfo>
@@ -9,50 +9,119 @@
#include <new> // operator new
#include <cstddef> // size_t
#include <iosfwd>
+#include <string>
-#ifdef NDEBUG
-#define VARIANT_INLINE inline __attribute__((always_inline))
+#include "recursive_wrapper.hpp"
+
+#ifdef _MSC_VER
+ // http://msdn.microsoft.com/en-us/library/z8y1yy88.aspx
+ #ifdef NDEBUG
+ #define VARIANT_INLINE __forceinline
+ #else
+ #define VARIANT_INLINE __declspec(noinline)
+ #endif
#else
-#define VARIANT_INLINE __attribute__((noinline))
+ #ifdef NDEBUG
+ #define VARIANT_INLINE inline __attribute__((always_inline))
+ #else
+ #define VARIANT_INLINE __attribute__((noinline))
+ #endif
#endif
-namespace llmr {
+#define VARIANT_MAJOR_VERSION 0
+#define VARIANT_MINOR_VERSION 1
+#define VARIANT_PATCH_VERSION 0
-namespace util {
+// translates to 100
+#define VARIANT_VERSION (VARIANT_MAJOR_VERSION*100000) + (VARIANT_MINOR_VERSION*100) + (VARIANT_PATCH_VERSION)
-namespace detail {
+namespace llmr { namespace util { namespace detail {
static constexpr std::size_t invalid_value = std::size_t(-1);
template <typename T, typename...Types>
-struct type_traits;
+struct direct_type;
template <typename T, typename First, typename...Types>
-struct type_traits<T, First, Types...>
+struct direct_type<T, First, Types...>
{
- static constexpr std::size_t id = std::is_same<T, First>::value ? sizeof...(Types) : type_traits<T, Types...>::id;
+ static constexpr std::size_t index = std::is_same<T, First>::value
+ ? sizeof...(Types) : direct_type<T, Types...>::index;
};
template <typename T>
-struct type_traits<T>
+struct direct_type<T>
{
- static constexpr std::size_t id = invalid_value;
+ static constexpr std::size_t index = invalid_value;
+};
+
+template <typename T, typename...Types>
+struct convertible_type;
+
+template <typename T, typename First, typename...Types>
+struct convertible_type<T, First, Types...>
+{
+ static constexpr std::size_t index = std::is_convertible<T, First>::value
+ ? sizeof...(Types) : convertible_type<T, Types...>::index;
+};
+
+template <typename T>
+struct convertible_type<T>
+{
+ static constexpr std::size_t index = invalid_value;
+};
+
+template <typename T, typename...Types>
+struct value_traits
+{
+ static constexpr std::size_t direct_index = direct_type<T, Types...>::index;
+ static constexpr std::size_t index =
+ (direct_index == invalid_value) ? convertible_type<T, Types...>::index : direct_index;
};
template <typename T, typename...Types>
struct is_valid_type;
template <typename T, typename First, typename... Types>
-struct is_valid_type<T,First,Types...>
+struct is_valid_type<T, First, Types...>
{
- static constexpr bool value = std::is_same<T,First>::value
- || is_valid_type<T,Types...>::value;
+ static constexpr bool value = std::is_convertible<T, First>::value
+ || is_valid_type<T, Types...>::value;
};
template <typename T>
struct is_valid_type<T> : std::false_type {};
-}
+template <std::size_t N, typename ... Types>
+struct select_type
+{
+ static_assert(N < sizeof...(Types), "index out of bounds");
+};
+
+template <std::size_t N, typename T, typename ... Types>
+struct select_type<N, T, Types...>
+{
+ using type = typename select_type<N - 1, Types...>::type;
+};
+
+template <typename T, typename ... Types>
+struct select_type<0, T, Types...>
+{
+ using type = T;
+};
+
+} // namespace detail
+
+// static visitor
+template <typename R = void>
+struct static_visitor
+{
+ using result_type = R;
+protected:
+ static_visitor() {}
+ ~static_visitor() {}
+};
+
template <std::size_t arg1, std::size_t ... others>
struct static_max;
@@ -125,37 +194,77 @@ template<> struct variant_helper<>
namespace detail {
+template <typename T>
+struct unwrapper
+{
+ T const& operator() (T const& obj) const
+ {
+ return obj;
+ }
+
+ T& operator() (T & obj) const
+ {
+ return obj;
+ }
+};
+
+
+template <typename T>
+struct unwrapper<recursive_wrapper<T>>
+{
+ auto operator() (recursive_wrapper<T> const& obj) const
+ -> typename recursive_wrapper<T>::type const&
+ {
+ return obj.get();
+ }
+};
+
+
template <typename F, typename V, typename...Types>
struct dispatcher;
template <typename F, typename V, typename T, typename...Types>
-struct dispatcher<F,V,T,Types...>
+struct dispatcher<F, V, T, Types...>
{
- typedef typename std::result_of<F(V const&)>::type result_type;
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const& v, F f)
+ {
+ if (v.get_type_index() == sizeof...(Types))
+ {
+ return f(unwrapper<T>()(v. template get<T>()));
+ }
+ else
+ {
+ return dispatcher<F, V, Types...>::apply_const(v, f);
+ }
+ }
- VARIANT_INLINE static result_type apply(V const& v, F f)
+ VARIANT_INLINE static result_type apply(V & v, F f)
{
- if (v.get_type_id() == sizeof...(Types))
+ if (v.get_type_index() == sizeof...(Types))
{
- return f(v. template get<T>());
+ return f(unwrapper<T>()(v. template get<T>()));
}
else
{
- return dispatcher<F,V,Types...>::apply(v, f);
+ return dispatcher<F, V, Types...>::apply(v, f);
}
}
};
-template<typename F,typename V>
-struct dispatcher<F,V>
+template<typename F, typename V>
+struct dispatcher<F, V>
{
- typedef typename std::result_of<F(V const&)>::type result_type;
-
- VARIANT_INLINE static result_type apply(V const&, F)
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const&, F)
{
- throw std::runtime_error("unary dispatch: FAIL");
+ throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
}
+ VARIANT_INLINE static result_type apply(V &, F)
+ {
+ throw std::runtime_error(std::string("unary dispatch: FAIL ") + typeid(V).name());
+ }
};
@@ -163,28 +272,46 @@ template <typename F, typename V, typename T, typename...Types>
struct binary_dispatcher_rhs;
template <typename F, typename V, typename T0, typename T1, typename...Types>
-struct binary_dispatcher_rhs<F,V,T0,T1,Types...>
+struct binary_dispatcher_rhs<F, V, T0, T1, Types...>
{
- typedef typename std::result_of<F(V const&, V const&)>::type result_type;
- VARIANT_INLINE static result_type apply(V const& lhs, V const& rhs, F f)
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
+ {
+ if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
+ {
+ return f(unwrapper<T0>()(lhs. template get<T0>()),
+ unwrapper<T1>()(rhs. template get<T1>()));
+ }
+ else
+ {
+ return binary_dispatcher_rhs<F, V, T0, Types...>::apply_const(lhs, rhs, f);
+ }
+ }
+
+ VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f)
{
- if (rhs.get_type_id() == sizeof...(Types)) // call binary functor
+ if (rhs.get_type_index() == sizeof...(Types)) // call binary functor
{
- return f(lhs. template get<T0>(), rhs. template get<T1>());
+ return f(unwrapper<T0>()(lhs. template get<T0>()),
+ unwrapper<T1>()(rhs. template get<T1>()));
}
else
{
- return binary_dispatcher_rhs<F,V,T0,Types...>::apply(lhs, rhs, f);
+ return binary_dispatcher_rhs<F, V, T0, Types...>::apply(lhs, rhs, f);
}
}
+
};
-template<typename F,typename V, typename T>
-struct binary_dispatcher_rhs<F,V,T>
+template<typename F, typename V, typename T>
+struct binary_dispatcher_rhs<F, V, T>
{
- typedef typename std::result_of<F(V const&, V const&)>::type result_type;
-
- VARIANT_INLINE static result_type apply(V const&, V const&, F)
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
+ {
+ throw std::runtime_error("binary dispatch: FAIL");
+ }
+ VARIANT_INLINE static result_type apply(V &, V &, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
@@ -195,28 +322,45 @@ template <typename F, typename V, typename T, typename...Types>
struct binary_dispatcher_lhs;
template <typename F, typename V, typename T0, typename T1, typename...Types>
-struct binary_dispatcher_lhs<F,V,T0,T1,Types...>
+struct binary_dispatcher_lhs<F, V, T0, T1, Types...>
{
- typedef typename std::result_of<F(V const&, V const&)>::type binary_result_type;
- VARIANT_INLINE static binary_result_type apply(V const& lhs, V const& rhs, F f)
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const& lhs, V const& rhs, F f)
{
- if (lhs.get_type_id() == sizeof...(Types)) // call binary functor
+ if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
{
return f(lhs. template get<T1>(), rhs. template get<T0>());
}
else
{
- return binary_dispatcher_lhs<F,V,T0,Types...>::apply(lhs, rhs, f);
+ return binary_dispatcher_lhs<F, V, T0, Types...>::apply_const(lhs, rhs, f);
}
}
+
+ VARIANT_INLINE static result_type apply(V & lhs, V & rhs, F f)
+ {
+ if (lhs.get_type_index() == sizeof...(Types)) // call binary functor
+ {
+ return f(lhs. template get<T1>(), rhs. template get<T0>());
+ }
+ else
+ {
+ return binary_dispatcher_lhs<F, V, T0, Types...>::apply(lhs, rhs, f);
+ }
+ }
+
};
-template<typename F,typename V, typename T>
-struct binary_dispatcher_lhs<F,V,T>
+template<typename F, typename V, typename T>
+struct binary_dispatcher_lhs<F, V, T>
{
- typedef typename std::result_of<F(V const&, V const&)>::type result_type;
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
+ {
+ throw std::runtime_error("binary dispatch: FAIL");
+ }
- VARIANT_INLINE static result_type apply(V const&, V const&, F)
+ VARIANT_INLINE static result_type apply(V &, V &, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
@@ -226,37 +370,60 @@ template <typename F, typename V, typename...Types>
struct binary_dispatcher;
template <typename F, typename V, typename T, typename...Types>
-struct binary_dispatcher<F,V,T,Types...>
+struct binary_dispatcher<F, V, T, Types...>
{
- typedef typename std::result_of<F(V const&, V const&)>::type binary_result_type;
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const& v0, V const& v1, F f)
+ {
+ if (v0.get_type_index() == sizeof...(Types))
+ {
+ if (v0.get_type_index() == v1.get_type_index())
+ {
+ return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
+ }
+ else
+ {
+ return binary_dispatcher_rhs<F, V, T, Types...>::apply_const(v0, v1, f);
+ }
+ }
+ else if (v1.get_type_index() == sizeof...(Types))
+ {
+ return binary_dispatcher_lhs<F, V, T, Types...>::apply_const(v0, v1, f);
+ }
+ return binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f);
+ }
- VARIANT_INLINE static binary_result_type apply(V const& v0, V const& v1, F f)
+ VARIANT_INLINE static result_type apply(V & v0, V & v1, F f)
{
- if (v0.get_type_id() == sizeof...(Types))
+ if (v0.get_type_index() == sizeof...(Types))
{
- if (v0.get_type_id() == v1.get_type_id())
+ if (v0.get_type_index() == v1.get_type_index())
{
return f(v0. template get<T>(), v1. template get<T>()); // call binary functor
}
else
{
- return binary_dispatcher_rhs<F,V,T,Types...>::apply(v0, v1, f);
+ return binary_dispatcher_rhs<F, V, T, Types...>::apply(v0, v1, f);
}
}
- else if (v1.get_type_id() == sizeof...(Types))
+ else if (v1.get_type_index() == sizeof...(Types))
{
- return binary_dispatcher_lhs<F,V,T,Types...>::apply(v0, v1, f);
+ return binary_dispatcher_lhs<F, V, T, Types...>::apply(v0, v1, f);
}
- return binary_dispatcher<F,V,Types...>::apply(v0, v1, f);
+ return binary_dispatcher<F, V, Types...>::apply(v0, v1, f);
}
};
-template<typename F,typename V>
-struct binary_dispatcher<F,V>
+template<typename F, typename V>
+struct binary_dispatcher<F, V>
{
- typedef typename std::result_of<F(V const&, V const&)>::type binary_result_type;
+ using result_type = typename F::result_type;
+ VARIANT_INLINE static result_type apply_const(V const&, V const&, F)
+ {
+ throw std::runtime_error("binary dispatch: FAIL");
+ }
- VARIANT_INLINE static binary_result_type apply(V const&, V const&, F)
+ VARIANT_INLINE static result_type apply(V &, V &, F)
{
throw std::runtime_error("binary dispatch: FAIL");
}
@@ -275,19 +442,19 @@ struct equal_comp
struct less_comp
{
template <typename T>
- bool operator()(const T& lhs, const T& rhs) const
+ bool operator()(T const& lhs, T const& rhs) const
{
return lhs < rhs;
}
};
template <typename Variant, typename Comp>
-class comparer
+class comparer : public static_visitor<bool>
{
public:
explicit comparer(Variant const& lhs) noexcept
: lhs_(lhs) {}
- comparer& operator=(const comparer&) = delete;
+ comparer& operator=(comparer const&) = delete;
// visitor
template<typename T>
bool operator()(T const& rhs_content) const
@@ -301,16 +468,16 @@ private:
// operator<< helper
template <typename Out>
-class printer
+class printer : public static_visitor<>
{
public:
explicit printer(Out & out)
: out_(out) {}
- printer& operator=(printer const &) = delete;
+ printer& operator=(printer const&) = delete;
// visitor
template <typename T>
- void operator()( T const & operand) const
+ void operator()(T const& operand) const
{
out_ << operand;
}
@@ -318,7 +485,7 @@ private:
Out & out_;
};
-} // detail
+} // namespace detail
template<typename... Types>
class variant
@@ -329,81 +496,106 @@ private:
static const std::size_t data_align = static_max<alignof(Types)...>::value;
using data_type = typename std::aligned_storage<data_size, data_align>::type;
-
using helper_type = variant_helper<Types...>;
- std::size_t type_id;
+ std::size_t type_index;
data_type data;
public:
VARIANT_INLINE variant()
- : type_id(detail::invalid_value) {}
+ : type_index(sizeof...(Types) - 1)
+ {
+ new (&data) typename detail::select_type<0, Types...>::type();
+ }
- template <typename T>
+ template <typename T, class = typename std::enable_if<
+ detail::is_valid_type<T, Types...>::value>::type>
VARIANT_INLINE explicit variant(T const& val) noexcept
- : type_id(detail::type_traits<T, Types...>::id)
+ : type_index(detail::value_traits<T, Types...>::index)
{
- static_assert(detail::is_valid_type<T,Types...>::value, "Not a valid type for this variant");
- new (&data) T(val);
+ constexpr std::size_t index = sizeof...(Types) - detail::value_traits<T, Types...>::index - 1;
+ using target_type = typename detail::select_type<index, Types...>::type;
+ new (&data) target_type(val);
}
- template <typename T>
+ template <typename T, class = typename std::enable_if<
+ detail::is_valid_type<T, Types...>::value>::type>
VARIANT_INLINE variant(T && val) noexcept
- : type_id(detail::type_traits<T,Types...>::id)
+ : type_index(detail::value_traits<T, Types...>::index)
{
- static_assert(detail::is_valid_type<T,Types...>::value, "Not a valid type for this variant");
- new (&data) T(std::forward<T>(val)); // nothrow
+ constexpr std::size_t index = sizeof...(Types) - detail::value_traits<T, Types...>::index - 1;
+ using target_type = typename detail::select_type<index, Types...>::type;
+ new (&data) target_type(std::forward<T>(val)); // nothrow
}
VARIANT_INLINE variant(variant<Types...> const& old)
- : type_id(old.type_id)
+ : type_index(old.type_index)
{
- helper_type::copy(old.type_id, &old.data, &data);
+ helper_type::copy(old.type_index, &old.data, &data);
}
VARIANT_INLINE variant(variant<Types...>&& old) noexcept
- : type_id(old.type_id)
+ : type_index(old.type_index)
{
- helper_type::move(old.type_id, &old.data, &data);
+ helper_type::move(old.type_index, &old.data, &data);
}
friend void swap(variant<Types...> & first, variant<Types...> & second)
{
using std::swap; //enable ADL
- swap(first.type_id, second.type_id);
+ swap(first.type_index, second.type_index);
swap(first.data, second.data);
}
- VARIANT_INLINE variant<Types...>& operator= (variant<Types...> other)
+ VARIANT_INLINE variant<Types...>& operator=(variant<Types...> other)
{
swap(*this, other);
return *this;
}
+ // conversions
+ // move-assign
+ template <typename T>
+ VARIANT_INLINE variant<Types...>& operator=(T && rhs) noexcept
+ {
+ variant<Types...> temp(std::move(rhs));
+ swap(*this, temp);
+ return *this;
+ }
+
+ // copy-assign
+ template <typename T>
+ VARIANT_INLINE variant<Types...>& operator=(T const& rhs)
+ {
+ variant<Types...> temp(rhs);
+ swap(*this, temp);
+ return *this;
+ }
+
template<typename T>
VARIANT_INLINE bool is() const
{
- return (type_id == detail::type_traits<T, Types...>::id);
+ return (type_index == detail::direct_type<T, Types...>::index);
}
VARIANT_INLINE bool valid() const
{
- return (type_id != detail::invalid_value);
+ return (type_index != detail::invalid_value);
}
template<typename T, typename... Args>
VARIANT_INLINE void set(Args&&... args)
{
- helper_type::destroy(type_id, &data);
+ helper_type::destroy(type_index, &data);
new (&data) T(std::forward<Args>(args)...);
- type_id = detail::type_traits<T,Types...>::id;
+ type_index = detail::direct_type<T, Types...>::index;
}
template<typename T>
VARIANT_INLINE T& get()
{
- if (type_id == detail::type_traits<T,Types...>::id)
+ if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T*>(&data);
}
@@ -416,7 +608,7 @@ public:
template<typename T>
VARIANT_INLINE T const& get() const
{
- if (type_id == detail::type_traits<T,Types...>::id)
+ if (type_index == detail::direct_type<T, Types...>::index)
{
return *reinterpret_cast<T const*>(&data);
}
@@ -426,37 +618,57 @@ public:
}
}
- VARIANT_INLINE std::size_t get_type_id() const
+ VARIANT_INLINE std::size_t get_type_index() const
{
- return type_id;
+ return type_index;
}
// visitor
// unary
template <typename F, typename V>
- typename std::result_of<F(V const&)>::type
- VARIANT_INLINE static visit(V const& v, F f)
+ auto VARIANT_INLINE
+ static visit(V const& v, F f)
+ -> decltype(detail::dispatcher<F, V, Types...>::apply_const(v, f))
+ {
+ return detail::dispatcher<F, V, Types...>::apply_const(v, f);
+ }
+ // non-const
+ template <typename F, typename V>
+ auto VARIANT_INLINE
+ static visit(V & v, F f)
+ -> decltype(detail::dispatcher<F, V, Types...>::apply(v, f))
{
return detail::dispatcher<F, V, Types...>::apply(v, f);
}
+
// binary
+ // const
+ template <typename F, typename V>
+ auto VARIANT_INLINE
+ static binary_visit(V const& v0, V const& v1, F f)
+ -> decltype(detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f))
+ {
+ return detail::binary_dispatcher<F, V, Types...>::apply_const(v0, v1, f);
+ }
+ // non-const
template <typename F, typename V>
- typename std::result_of<F(V const&, V const& )>::type
- VARIANT_INLINE static binary_visit(V const& v0, V const& v1, F f)
+ auto VARIANT_INLINE
+ static binary_visit(V& v0, V& v1, F f)
+ -> decltype(detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f))
{
return detail::binary_dispatcher<F, V, Types...>::apply(v0, v1, f);
}
~variant() noexcept
{
- helper_type::destroy(type_id, &data);
+ helper_type::destroy(type_index, &data);
}
// comparison operators
// equality
VARIANT_INLINE bool operator==(variant const& rhs) const
{
- if (this->get_type_id() != rhs.get_type_id())
+ if (this->get_type_index() != rhs.get_type_index())
return false;
detail::comparer<variant, detail::equal_comp> visitor(*this);
return visit(rhs, visitor);
@@ -464,9 +676,9 @@ public:
// less than
VARIANT_INLINE bool operator<(variant const& rhs) const
{
- if (this->get_type_id() != rhs.get_type_id())
+ if (this->get_type_index() != rhs.get_type_index())
{
- return this->get_type_id() < rhs.get_type_id();
+ return this->get_type_index() < rhs.get_type_index();
// ^^ borrowed from boost::variant
}
detail::comparer<variant, detail::less_comp> visitor(*this);
@@ -475,17 +687,30 @@ public:
};
// unary visitor interface
+
+// const
+template <typename V, typename F>
+auto VARIANT_INLINE static apply_visitor(F f, V const& v) -> decltype(V::visit(v, f))
+{
+ return V::visit(v, f);
+}
+// non-const
template <typename V, typename F>
-typename std::result_of<F(V const&)>::type
-VARIANT_INLINE static apply_visitor(V const& v, F f)
+auto VARIANT_INLINE static apply_visitor(F f, V & v) -> decltype(V::visit(v, f))
{
- return V::visit(v,f);
+ return V::visit(v, f);
}
// binary visitor interface
+// const
+template <typename V, typename F>
+auto VARIANT_INLINE static apply_visitor(F f, V const& v0, V const& v1) -> decltype(V::binary_visit(v0, v1, f))
+{
+ return V::binary_visit(v0, v1, f);
+}
+// non-const
template <typename V, typename F>
-typename std::result_of<F(V const&, V const&)>::type
-VARIANT_INLINE static apply_visitor(V const& v0, V const& v1, F f)
+auto VARIANT_INLINE static apply_visitor(F f, V & v0, V & v1) -> decltype(V::binary_visit(v0, v1, f))
{
return V::binary_visit(v0, v1, f);
}
@@ -493,14 +718,14 @@ VARIANT_INLINE static apply_visitor(V const& v0, V const& v1, F f)
// operator<<
template <typename charT, typename traits, typename Variant>
-VARIANT_INLINE std::basic_ostream<charT,traits>&
-operator<< (std::basic_ostream<charT,traits>& out, Variant const& rhs)
+VARIANT_INLINE std::basic_ostream<charT, traits>&
+operator<< (std::basic_ostream<charT, traits>& out, Variant const& rhs)
{
- detail::printer<std::basic_ostream<charT,traits> > visitor(out);
- apply_visitor(rhs, visitor);
+ detail::printer<std::basic_ostream<charT, traits>> visitor(out);
+ apply_visitor(visitor, rhs);
return out;
}
-}
-}
-#endif // UTIL_VARIANT_HPP
+}}
+
+#endif // MAPBOX_UTIL_VARIANT_HPP
diff --git a/src/map/filter_expression.cpp b/src/map/filter_expression.cpp
new file mode 100644
index 0000000000..9e293b0026
--- /dev/null
+++ b/src/map/filter_expression.cpp
@@ -0,0 +1,282 @@
+#include <llmr/map/filter_expression.hpp>
+#include <llmr/map/vector_tile.hpp>
+
+namespace llmr {
+
+
+template <typename Comparer>
+inline bool FilterComparison::Instance::includes(const Value &property_value, const Comparer &comparer) const {
+ for (const Value &filter_value : values) {
+ if (comparer(property_value, filter_value)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <typename Comparer>
+inline bool FilterComparison::Instance::compare(const Value &property_value, const Comparer &comparer) const {
+ for (const Value &filter_value : values) {
+ if (!comparer(property_value, filter_value)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <typename Comparer>
+inline bool FilterComparison::Instance::all(const std::forward_list<Value> &property_values, const Comparer &comparer) const {
+ for (const Value &property_value : property_values) {
+ if (!compare(property_value, comparer)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+bool FilterComparison::Instance::compare(const Value &property_value) const {
+ switch (op) {
+ case Operator::Equal:
+ case Operator::In:
+ return includes(property_value, util::relaxed_equal);
+ case Operator::NotEqual:
+ case Operator::NotIn:
+ return !includes(property_value, util::relaxed_equal);
+ case Operator::Greater:
+ return compare(property_value, util::relaxed_greater);
+ case Operator::GreaterEqual:
+ return compare(property_value, util::relaxed_greater_equal);
+ case Operator::Less:
+ return compare(property_value, util::relaxed_less);
+ case Operator::LessEqual:
+ return compare(property_value, util::relaxed_less_equal);
+ default:
+ return false;
+ }
+}
+
+bool FilterComparison::Instance::compare(const std::forward_list<Value> &property_values) const {
+ switch (op) {
+ case Operator::Equal:
+ for (const Value &property_value : property_values) {
+ if (!includes(property_value, util::relaxed_equal)) {
+ return false;
+ }
+ }
+ return true;
+ case Operator::NotEqual:
+ for (const Value &property_value : property_values) {
+ if (includes(property_value, util::relaxed_equal)) {
+ return false;
+ }
+ }
+ return true;
+ case Operator::In:
+ for (const Value &property_value : property_values) {
+ if (includes(property_value, util::relaxed_equal)) {
+ return true;
+ }
+ }
+ return false;
+ case Operator::NotIn:
+ for (const Value &property_value : property_values) {
+ if (!includes(property_value, util::relaxed_equal)) {
+ return true;
+ }
+ }
+ return false;
+ case Operator::Greater:
+ return all(property_values, util::relaxed_greater);
+ case Operator::GreaterEqual:
+ return all(property_values, util::relaxed_greater_equal);
+ case Operator::Less:
+ return all(property_values, util::relaxed_less);
+ case Operator::LessEqual:
+ return all(property_values, util::relaxed_less_equal);
+ }
+}
+
+
+const std::string &FilterComparison::getField() const {
+ return field;
+}
+
+inline bool FilterComparison::compare(const VectorTileTagExtractor &extractor) const {
+ const std::forward_list<Value> values = extractor.getValues(field);
+
+ // All instances are ANDed together.
+ for (const Instance &instance : instances) {
+ if (!instance.compare(values)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+
+
+std::ostream& operator <<(std::ostream &s, const FilterComparison &comparison) {
+ s << "comparison" << std::endl;
+ for (const FilterComparison::Instance &instance : comparison.instances) {
+ s << " - " << comparison.field << " " << instance << std::endl;
+ }
+ return s;
+}
+
+
+std::ostream& operator <<(std::ostream &s, const FilterComparison::Instance &instance) {
+ switch (instance.op) {
+ case FilterComparison::Operator::Equal: s << "=="; break;
+ case FilterComparison::Operator::NotEqual: s << "!="; break;
+ case FilterComparison::Operator::Greater: s << ">"; break;
+ case FilterComparison::Operator::GreaterEqual: s << ">="; break;
+ case FilterComparison::Operator::Less: s << "<"; break;
+ case FilterComparison::Operator::LessEqual: s << "<="; break;
+ case FilterComparison::Operator::In: s << "in"; break;
+ case FilterComparison::Operator::NotIn: s << "!in"; break;
+ }
+
+ s << " [ ";
+ for (const Value &value : instance.values) {
+ s << toString(value) << " ";
+ }
+ s << "]";
+ return s;
+}
+
+
+FilterComparison::Operator parseFilterComparisonOperator(const std::string &op) {
+ if (op == "==") return FilterComparison::Operator::Equal;
+ if (op == "!=") return FilterComparison::Operator::NotEqual;
+ if (op == ">") return FilterComparison::Operator::Greater;
+ if (op == ">=") return FilterComparison::Operator::GreaterEqual;
+ if (op == "<") return FilterComparison::Operator::Less;
+ if (op == "<=") return FilterComparison::Operator::LessEqual;
+ if (op == "in") return FilterComparison::Operator::In;
+ if (op == "!in") return FilterComparison::Operator::NotIn;
+ return FilterComparison::Operator::Equal;
+}
+
+
+std::ostream& operator <<(std::ostream &s, FilterExpression::Operator op) {
+ switch (op) {
+ case FilterExpression::Operator::And: s << "AND"; break;
+ case FilterExpression::Operator::Or: s << "OR"; break;
+ case FilterExpression::Operator::Xor: s << "XOR"; break;
+ case FilterExpression::Operator::Nor: s << "NOR"; break;
+ }
+ return s;
+}
+
+
+std::ostream& operator <<(std::ostream &s, FilterExpression::GeometryType type) {
+ switch (type) {
+ case FilterExpression::GeometryType::Point: s << "point"; break;
+ case FilterExpression::GeometryType::Line: s << "line"; break;
+ case FilterExpression::GeometryType::Polygon: s << "polygon"; break;
+ case FilterExpression::GeometryType::Any: s << "any"; break;
+ }
+ return s;
+}
+
+
+
+bool FilterExpression::compare(const VectorTileTagExtractor &extractor) const {
+ if (type != GeometryType::Any && extractor.getType() != type && extractor.getType() != GeometryType::Any) {
+ return false;
+ }
+
+ switch (op) {
+ case Operator::And:
+ for (const FilterComparison &comparison : comparisons) {
+ if (!comparison.compare(extractor)) {
+ return false;
+ }
+ }
+ for (const FilterExpression &expression: expressions) {
+ if (!expression.compare(extractor)) {
+ return false;
+ }
+ }
+ return true;
+ case Operator::Or:
+ for (const FilterComparison &comparison : comparisons) {
+ if (comparison.compare(extractor)) {
+ return true;
+ }
+ }
+ for (const FilterExpression &expression: expressions) {
+ if (expression.compare(extractor)) {
+ return true;
+ }
+ }
+ return false;
+ case Operator::Xor: {
+ int count = 0;
+ for (const FilterComparison &comparison : comparisons) {
+ count += comparison.compare(extractor);
+ if (count > 1) {
+ return false;
+ }
+ }
+ for (const FilterExpression &expression: expressions) {
+ count += expression.compare(extractor);
+ if (count > 1) {
+ return false;
+ }
+ }
+ return count == 1;
+ }
+ case Operator::Nor:
+ for (const FilterComparison &comparison : comparisons) {
+ if (comparison.compare(extractor)) {
+ return false;
+ }
+ }
+ for (const FilterExpression &expression: expressions) {
+ if (expression.compare(extractor)) {
+ return false;
+ }
+ }
+ return true;
+ default:
+ return true;
+ }
+}
+
+bool FilterExpression::empty() const {
+ return type == GeometryType::Any && comparisons.empty() && expressions.empty();
+}
+
+void FilterExpression::add(const FilterComparison &comparison) {
+ comparisons.emplace_front(comparison);
+}
+
+void FilterExpression::add(const FilterExpression &expression) {
+ expressions.emplace_front(expression);
+}
+
+void FilterExpression::setGeometryType(GeometryType g) {
+ type = g;
+}
+
+FilterExpression::GeometryType parseGeometryType(const std::string &geometry) {
+ if (geometry == "point") return FilterExpression::GeometryType::Point;
+ if (geometry == "line") return FilterExpression::GeometryType::Line;
+ if (geometry == "polygon") return FilterExpression::GeometryType::Polygon;
+ return FilterExpression::GeometryType::Any;
+}
+
+std::ostream& operator <<(std::ostream &s, const FilterExpression &expression) {
+ s << "expression " << expression.op << std::endl;
+ s << " - $type = " << expression.type << std::endl;
+ for (const FilterComparison &comparison : expression.comparisons) {
+ s << comparison;
+ }
+ s << "end expression" << std::endl;
+ return s;
+}
+
+}
diff --git a/src/map/map.cpp b/src/map/map.cpp
index 01f7ac6072..db94ca97db 100644
--- a/src/map/map.cpp
+++ b/src/map/map.cpp
@@ -169,8 +169,6 @@ void Map::setup() {
view.make_active();
painter.setup();
-
- setStyleJSON(styleJSON);
}
void Map::setStyleJSON(std::string newStyleJSON) {
diff --git a/src/map/tile_parser.cpp b/src/map/tile_parser.cpp
index b4041ada4e..e834e6fa95 100644
--- a/src/map/tile_parser.cpp
+++ b/src/map/tile_parser.cpp
@@ -132,7 +132,7 @@ std::unique_ptr<Bucket> TileParser::createBucket(std::shared_ptr<StyleBucket> bu
}
template <class Bucket>
-void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const PropertyFilterExpression &filter) {
+void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter) {
FilteredVectorTileLayer filtered_layer(layer, filter);
for (pbf feature : filtered_layer) {
if (obsolete())
@@ -150,7 +150,7 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer,
}
template <class Bucket, typename... Args>
-void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const PropertyFilterExpression &filter, Args&& ...args) {
+void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer, const FilterExpression &filter, Args&& ...args) {
FilteredVectorTileLayer filtered_layer(layer, filter);
for (const pbf &feature_pbf : filtered_layer) {
if (obsolete()) {
@@ -161,25 +161,25 @@ void TileParser::addBucketFeatures(Bucket& bucket, const VectorTileLayer& layer,
}
-std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketFill &fill) {
+std::unique_ptr<Bucket> TileParser::createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill) {
std::unique_ptr<FillBucket> bucket = std::make_unique<FillBucket>(tile.fillVertexBuffer, tile.triangleElementsBuffer, tile.lineElementsBuffer, fill);
addBucketFeatures(bucket, layer, filter);
return obsolete() ? nullptr : std::move(bucket);
}
-std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketLine &line) {
+std::unique_ptr<Bucket> TileParser::createLineBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketLine &line) {
std::unique_ptr<LineBucket> bucket = std::make_unique<LineBucket>(tile.lineVertexBuffer, tile.triangleElementsBuffer, tile.pointElementsBuffer, line);
addBucketFeatures(bucket, layer, filter);
return obsolete() ? nullptr : std::move(bucket);
}
-std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketIcon &icon) {
+std::unique_ptr<Bucket> TileParser::createIconBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketIcon &icon) {
std::unique_ptr<IconBucket> bucket = std::make_unique<IconBucket>(tile.iconVertexBuffer, icon);
addBucketFeatures(bucket, layer, filter, *spriteAtlas);
return obsolete() ? nullptr : std::move(bucket);
}
-std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& layer, const PropertyFilterExpression &filter, const StyleBucketText &text) {
+std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketText &text) {
const StyleBucketText &properties = text;
@@ -235,9 +235,3 @@ std::unique_ptr<Bucket> TileParser::createTextBucket(const VectorTileLayer& laye
return std::move(bucket);
}
-
-
-
-
-//typedef std::pair<uint16_t, uint16_t> GlyphRange;
-//
diff --git a/src/map/vector_tile.cpp b/src/map/vector_tile.cpp
index 24f03e1a97..54a971cd87 100644
--- a/src/map/vector_tile.cpp
+++ b/src/map/vector_tile.cpp
@@ -102,7 +102,7 @@ VectorTileLayer::VectorTileLayer(pbf layer) : data(layer) {
}
}
-FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, const PropertyFilterExpression &filterExpression)
+FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, const FilterExpression &filterExpression)
: layer(layer),
filterExpression(filterExpression) {
}
@@ -122,120 +122,167 @@ FilteredVectorTileLayer::iterator::iterator(const FilteredVectorTileLayer& paren
operator++();
}
-bool FilteredVectorTileLayer::iterator::matchesFilterExpression(const PropertyFilterExpression &filterExpression, const pbf &tags_pbf) {
- if (filterExpression.is<util::recursive_wrapper<PropertyFilter>>()) {
- return matchesFilter(filterExpression.get<util::recursive_wrapper<PropertyFilter>>().get(), tags_pbf);
- } else if (filterExpression.is<util::recursive_wrapper<PropertyExpression>>()) {
- return matchesExpression(filterExpression.get<util::recursive_wrapper<PropertyExpression>>().get(), tags_pbf);
- } else if (filterExpression.is<std::true_type>()) {
- return true;
- } else {
- return false;
- }
+//bool FilteredVectorTileLayer::iterator::matchesFilterExpression(const FilterExpression &filterExpression, const pbf &tags_pbf) {
+// if (filterExpression.empty()) {
+// return true;
+// }
+//
+//
+//
+//
+// if (filterExpression.is<util::recursive_wrapper<PropertyFilter>>()) {
+// return matchesFilter(filterExpression.get<util::recursive_wrapper<PropertyFilter>>().get(), tags_pbf);
+// } else if (filterExpression.is<util::recursive_wrapper<PropertyExpression>>()) {
+// return matchesExpression(filterExpression.get<util::recursive_wrapper<PropertyExpression>>().get(), tags_pbf);
+// } else if (filterExpression.is<std::true_type>()) {
+// return true;
+// } else {
+// return false;
+// }
+// return true;
+//}
+
+//
+//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;
+//
+// // Now loop through all the key/value pair tags.
+// // tags are packed varints. They should have an even length.
+// pbf tags_pbf = const_tags_pbf;
+// while (tags_pbf) {
+// uint32_t tag_key = tags_pbf.varint();
+// if (!tags_pbf) {
+// // This should not happen; otherwise the vector tile
+// // is invalid.
+// throw std::runtime_error("uneven number of feature tag ids");
+// }
+// uint32_t tag_val = tags_pbf.varint();
+//
+// if (tag_key == filter_key) {
+// if (parent.layer.values.size() > tag_val) {
+// const Value &value = parent.layer.values[tag_val];
+// return filter.compare(value);
+// } else {
+// throw std::runtime_error("feature references out of range value");
+// }
+// }
+// }
+// }
+//
+// // The feature doesn't contain the field that we're looking to compare.
+// // Depending on the filter, this may still be okay.
+// return filter.isMissingFieldOkay();
+//}
+//
+//bool FilteredVectorTileLayer::iterator::matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf) {
+// if (expression.op == ExpressionOperator::Or) {
+// for (const PropertyFilterExpression &filterExpression : expression.operands) {
+// if (matchesFilterExpression(filterExpression, tags_pbf)) {
+// return true;
+// }
+// }
+// return false;
+// } else if (expression.op == ExpressionOperator::And) {
+// for (const PropertyFilterExpression &filterExpression : expression.operands) {
+// if (!matchesFilterExpression(filterExpression, tags_pbf)) {
+// return false;
+// }
+// }
+// return true;
+// } else {
+// return false;
+// }
+//}
+//
+
+VectorTileTagExtractor::VectorTileTagExtractor(const VectorTileLayer &layer) : layer_(layer) {}
+
+
+void VectorTileTagExtractor::setTags(const pbf &pbf) {
+ tags_ = pbf;
}
+std::forward_list<Value> VectorTileTagExtractor::getValues(const std::string &key) const {
+ std::forward_list<Value> values;
-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()) {
+ auto field_it = layer_.key_index.find(key);
+ if (field_it != layer_.key_index.end()) {
+ auto it = values.before_begin();
const uint32_t filter_key = field_it->second;
// Now loop through all the key/value pair tags.
// tags are packed varints. They should have an even length.
- pbf tags_pbf = const_tags_pbf;
+ pbf tags_pbf = tags_;
+ uint32_t tag_key, tag_val;
while (tags_pbf) {
- uint32_t tag_key = tags_pbf.varint();
+ tag_key = tags_pbf.varint();
if (!tags_pbf) {
- // This should not happen; otherwise the vector tile
- // is invalid.
- throw std::runtime_error("uneven number of feature tag ids");
+ // This should not happen; otherwise the vector tile is invalid.
+ fprintf(stderr, "[WARNING] uneven number of feature tag ids\n");
+ return values;
}
- uint32_t tag_val = tags_pbf.varint();
+ // Note: We need to run this command in all cases, even if the keys don't match.
+ tag_val = tags_pbf.varint();
if (tag_key == filter_key) {
- if (parent.layer.values.size() > tag_val) {
- const Value &value = parent.layer.values[tag_val];
- return filter.compare(value);
+ if (layer_.values.size() > tag_val) {
+ it = values.emplace_after(it, layer_.values[tag_val]);
} else {
- throw std::runtime_error("feature references out of range value");
+ fprintf(stderr, "[WARNING] feature references out of range value\n");
+ break;
}
}
}
}
- // The feature doesn't contain the field that we're looking to compare.
- // Depending on the filter, this may still be okay.
- return filter.isMissingFieldOkay();
+ return values;
}
-bool FilteredVectorTileLayer::iterator::matchesExpression(const PropertyExpression &expression, const pbf &tags_pbf) {
- if (expression.op == ExpressionOperator::Or) {
- for (const PropertyFilterExpression &filterExpression : expression.operands) {
- if (matchesFilterExpression(filterExpression, tags_pbf)) {
- return true;
- }
- }
- return false;
- } else if (expression.op == ExpressionOperator::And) {
- for (const PropertyFilterExpression &filterExpression : expression.operands) {
- if (!matchesFilterExpression(filterExpression, tags_pbf)) {
- return false;
- }
- }
- return true;
- } else {
- return false;
- }
+void VectorTileTagExtractor::setType(FilterExpression::GeometryType type) {
+ type_ = type;
}
-
+FilterExpression::GeometryType VectorTileTagExtractor::getType() const {
+ return type_;
+}
void FilteredVectorTileLayer::iterator::operator++() {
valid = false;
- const PropertyFilterExpression &expression = parent.filterExpression;
+ const FilterExpression &expression = parent.filterExpression;
while (data.next(2)) { // feature
feature = data.message();
pbf feature_pbf = feature;
- BucketType type = BucketType::None;
-
bool matched = false;
// Treat the absence of any expression filters as a match.
- if (!expression.valid() || expression.is<std::true_type>()) {
+ if (expression.empty()) {
matched = true;
}
- while (feature_pbf.next()) {
- if (feature_pbf.tag == 2) { // tags
- if (matched) {
- // There is no filter, so we want to parse the entire layer anyway.
- feature_pbf.skip();
- } else {
- // We only want to parse some features.
- const pbf tags_pbf = feature_pbf.message();
- matched = matchesFilterExpression(expression, tags_pbf);
- if (!matched) {
- break; // feature_pbf loop early
+ if (!matched) {
+ VectorTileTagExtractor extractor(parent.layer);
+
+ // Retrieve the basic information
+ while (feature_pbf.next()) {
+ if (feature_pbf.tag == 2) { // tags
+ extractor.setTags(feature_pbf.message());
+ } else if (feature_pbf.tag == 3) { // geometry type
+ switch (FeatureType(feature_pbf.varint())) {
+ case FeatureType::Point: extractor.setType(FilterExpression::GeometryType::Point); break;
+ case FeatureType::LineString: extractor.setType(FilterExpression::GeometryType::Line); break;
+ case FeatureType::Polygon: extractor.setType(FilterExpression::GeometryType::Polygon); break;
+ default: break;
}
+ } else {
+ feature_pbf.skip();
}
- } else if (feature_pbf.tag == 3) { // geometry type
- switch (feature_pbf.varint()) {
- case 1 /* Point */: type = BucketType::Icon; break;
- case 2 /* LineString */: type = BucketType::Line; break;
- case 3 /* Polygon */: type = BucketType::Fill; break;
- default: type = BucketType::None; break;
- }
-
- // TODO: Parse feature type
-// if (type != parent.bucket_desc.feature_type) {
-// matched = false;
-// break; // feature_pbf loop early
-// }
- } else {
- feature_pbf.skip();
}
+
+ matched = expression.compare(extractor);
}
if (matched) {
diff --git a/src/style/style_parser.cpp b/src/style/style_parser.cpp
index 390096e9de..34aa7f2228 100644
--- a/src/style/style_parser.cpp
+++ b/src/style/style_parser.cpp
@@ -622,7 +622,7 @@ void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) {
if (value.HasMember("filter")) {
JSVal value_filter = replaceConstant(value["filter"]);
- parseFilter(value_filter, layer);
+ layer->bucket->filter = parseFilter(value_filter);
}
if (value.HasMember("render")) {
@@ -631,8 +631,92 @@ void StyleParser::parseBucket(JSVal value, std::shared_ptr<StyleLayer> &layer) {
}
}
-void StyleParser::parseFilter(JSVal value, std::shared_ptr<StyleLayer> &layer) {
- // TODO
+FilterExpression StyleParser::parseFilter(JSVal value) {
+ return parseFilter(value, value.IsArray() ? FilterExpression::Operator::Or : FilterExpression::Operator::And);
+}
+
+FilterExpression StyleParser::parseFilter(JSVal value, FilterExpression::Operator op) {
+ FilterExpression expression(op);
+ if (value.IsObject()) {
+ rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
+ for (; itr != value.MemberEnd(); ++itr) {
+ const std::string name { itr->name.GetString(), itr->name.GetStringLength() };
+ if (name == "&") {
+ expression.add(parseFilter(replaceConstant(itr->value), FilterExpression::Operator::And));
+ } else if (name == "|") {
+ expression.add(parseFilter(replaceConstant(itr->value), FilterExpression::Operator::Or));
+ } else if (name == "^") {
+ expression.add(parseFilter(replaceConstant(itr->value), FilterExpression::Operator::Xor));
+ } else if (name == "!") {
+ expression.add(parseFilter(replaceConstant(itr->value), FilterExpression::Operator::Nor));
+ } else if (name == "$type") {
+ JSVal type = replaceConstant(itr->value);
+ if (type.IsString()) {
+ expression.setGeometryType(parseGeometryType({ type.GetString(), type.GetStringLength() }));
+ }
+ } else {
+ FilterComparison comparison(name);
+ JSVal filterValue = replaceConstant(itr->value);
+ if (filterValue.IsObject()) {
+ rapidjson::Value::ConstMemberIterator itr = filterValue.MemberBegin();
+ for (; itr != filterValue.MemberEnd(); ++itr) {
+ comparison.add(
+ parseFilterComparisonOperator({ itr->name.GetString(), itr->name.GetStringLength() }),
+ parseValues(replaceConstant(itr->value))
+ );
+ }
+ } else if (filterValue.IsArray()) {
+ comparison.add(FilterComparison::Operator::In, parseValues(filterValue));
+ } else {
+ comparison.add(FilterComparison::Operator::Equal, std::forward_list<Value>({ parseValue(filterValue) }));
+ }
+ expression.add(comparison);
+ }
+ }
+ } else if (value.IsArray()) {
+ for (rapidjson::SizeType i = 0; i < value.Size(); i++) {
+ expression.add(parseFilter(replaceConstant(value[i])));
+ }
+ } else {
+ fprintf(stderr, "[WARNING] expression must be either an array or an object\n");
+ }
+
+ return expression;
+}
+
+Value StyleParser::parseValue(JSVal value) {
+ switch (value.GetType()) {
+ case rapidjson::kNullType:
+ case rapidjson::kFalseType:
+ return false;
+
+ case rapidjson::kTrueType:
+ return true;
+
+ case rapidjson::kStringType:
+ return std::string { value.GetString(), value.GetStringLength() };
+
+ case rapidjson::kNumberType:
+ if (value.IsUint64()) return value.GetUint64();
+ if (value.IsInt64()) return value.GetInt64();
+ return value.GetDouble();
+
+ default:
+ return false;
+ }
+}
+
+std::forward_list<Value> StyleParser::parseValues(JSVal value) {
+ std::forward_list<Value> values;
+ if (value.IsArray()) {
+ auto it = values.before_begin();
+ for (rapidjson::SizeType i = 0; i < value.Size(); i++) {
+ it = values.emplace_after(it, parseValue(replaceConstant(value[i])));
+ }
+ } else {
+ values.emplace_front(parseValue(value));
+ }
+ return values;
}
void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) {
@@ -724,79 +808,4 @@ void StyleParser::parseRender(JSVal value, std::shared_ptr<StyleLayer> &layer) {
}
}
-//PropertyFilterExpression StyleParser::parseFilterOrExpression(JSVal value) {
-// if (value.IsArray()) {
-// // This is an expression.
-// util::recursive_wrapper<PropertyExpression> expression;
-// for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
-// JSVal filter_item = value[i];
-//
-// if (filter_item.IsString()) {
-// expression.get().op = expressionOperatorType({ filter_item.GetString(), filter_item.GetStringLength() });
-// } else {
-// expression.get().operands.emplace_back(parseFilterOrExpression(filter_item));
-// }
-// }
-// return 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.
-// util::recursive_wrapper<PropertyExpression> expression;
-// for (rapidjson::SizeType i = 0; i < val.Size(); ++i) {
-// expression.get().operands.emplace_back(util::recursive_wrapper<PropertyFilter>(PropertyFilter { field_name, op, parseValue(val[i]) }));
-// }
-//
-// return std::move(expression);
-// } else {
-// // The filter only has a single value, so it is a real filter.
-// return std::move(util::recursive_wrapper<PropertyFilter>(PropertyFilter { field_name, op, parseValue(val) }));
-// }
-// }
-//
-// return std::true_type();
-
-//Value StyleParser::parseValue(JSVal value) {
-// switch (value.GetType()) {
-// case rapidjson::kNullType:
-// case rapidjson::kFalseType:
-// return false;
-//
-// case rapidjson::kTrueType:
-// return true;
-//
-// case rapidjson::kStringType:
-// return std::string { value.GetString(), value.GetStringLength() };
-//
-// case rapidjson::kNumberType:
-// if (value.IsUint64()) return value.GetUint64();
-// if (value.IsInt64()) return value.GetInt64();
-// return value.GetDouble();
-//
-// case rapidjson::kObjectType:
-// case rapidjson::kArrayType:
-// throw Style::exception("value cannot be an object or array");
-// return false;
-// }
-// throw Style::exception("unhandled value type in style");
-// return false;
-//}
-
}