summaryrefslogtreecommitdiff
path: root/src/mbgl/style
diff options
context:
space:
mode:
authorKonstantin Käfer <mail@kkaefer.com>2014-12-04 18:29:42 +0100
committerKonstantin Käfer <mail@kkaefer.com>2014-12-04 20:02:50 +0100
commitabafb52f37beb5659efc2105ccd1568e1f754898 (patch)
tree6a60636d3497560ca61e5aae5f6d7061c4f18553 /src/mbgl/style
parentbff6aeb4da41dee1f5f1cfa0be81b6c257257253 (diff)
downloadqtlocation-mapboxgl-abafb52f37beb5659efc2105ccd1568e1f754898.tar.gz
make most headers private
Diffstat (limited to 'src/mbgl/style')
-rw-r--r--src/mbgl/style/applied_class_properties.cpp52
-rw-r--r--src/mbgl/style/applied_class_properties.hpp39
-rw-r--r--src/mbgl/style/class_dictionary.cpp51
-rw-r--r--src/mbgl/style/class_dictionary.hpp37
-rw-r--r--src/mbgl/style/class_properties.cpp14
-rw-r--r--src/mbgl/style/class_properties.hpp43
-rw-r--r--src/mbgl/style/filter_expression.cpp123
-rw-r--r--src/mbgl/style/filter_expression.hpp125
-rw-r--r--src/mbgl/style/filter_expression_private.hpp118
-rw-r--r--src/mbgl/style/function_properties.cpp68
-rw-r--r--src/mbgl/style/function_properties.hpp55
-rw-r--r--src/mbgl/style/property_fallback.cpp61
-rw-r--r--src/mbgl/style/property_fallback.hpp29
-rw-r--r--src/mbgl/style/property_key.hpp70
-rw-r--r--src/mbgl/style/property_transition.hpp15
-rw-r--r--src/mbgl/style/property_value.hpp21
-rw-r--r--src/mbgl/style/style.cpp107
-rw-r--r--src/mbgl/style/style.hpp68
-rw-r--r--src/mbgl/style/style_bucket.cpp15
-rw-r--r--src/mbgl/style/style_bucket.hpp112
-rw-r--r--src/mbgl/style/style_layer.cpp284
-rw-r--r--src/mbgl/style/style_layer.hpp89
-rw-r--r--src/mbgl/style/style_layer_group.cpp34
-rw-r--r--src/mbgl/style/style_layer_group.hpp23
-rw-r--r--src/mbgl/style/style_parser.cpp845
-rw-r--r--src/mbgl/style/style_parser.hpp113
-rw-r--r--src/mbgl/style/style_properties.cpp11
-rw-r--r--src/mbgl/style/style_properties.hpp114
-rw-r--r--src/mbgl/style/style_source.cpp77
-rw-r--r--src/mbgl/style/style_source.hpp41
-rw-r--r--src/mbgl/style/types.cpp0
-rw-r--r--src/mbgl/style/types.hpp196
-rw-r--r--src/mbgl/style/value.cpp60
-rw-r--r--src/mbgl/style/value.hpp45
-rw-r--r--src/mbgl/style/value_comparison.hpp109
35 files changed, 3264 insertions, 0 deletions
diff --git a/src/mbgl/style/applied_class_properties.cpp b/src/mbgl/style/applied_class_properties.cpp
new file mode 100644
index 0000000000..9037c6ad5d
--- /dev/null
+++ b/src/mbgl/style/applied_class_properties.cpp
@@ -0,0 +1,52 @@
+#include <mbgl/style/applied_class_properties.hpp>
+
+namespace mbgl {
+
+AppliedClassProperty::AppliedClassProperty(ClassID class_id, timestamp begin_, timestamp end_, const PropertyValue &value_)
+ : name(class_id),
+ begin(begin_),
+ end(end_),
+ value(value_) {}
+
+// Returns thie ID of the most recent
+ClassID AppliedClassProperties::mostRecent() const {
+ return properties.size() ? properties.back().name : ClassID::Fallback;
+}
+
+void AppliedClassProperties::add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value) {
+ properties.emplace_back(class_id, begin, end, value);
+}
+
+bool AppliedClassProperties::hasTransitions() const {
+ return properties.size() > 1;
+}
+
+// Erase all items in the property list that are before a completed transition.
+// Then, if the only remaining property is a Fallback value, remove it too.
+void AppliedClassProperties::cleanup(timestamp now) {
+ // Iterate backwards, but without using the rbegin/rend interface since we need forward
+ // iterators to use .erase().
+ for (auto it = properties.end(), begin = properties.begin(); it != begin;) {
+ // If the property is finished, break iteration and delete all remaining items.
+ if ((--it)->end <= now) {
+ // Removes all items that precede the current iterator, but *not* the element currently
+ // pointed to by the iterator. This preserves the last completed transition as the
+ // first element in the property list.
+ properties.erase(begin, it);
+
+ // Also erase the pivot element if it's a fallback value. This means we can remove the
+ // entire applied properties object as well, because we already have the fallback
+ // value set as the default.
+ if (it->name == ClassID::Fallback) {
+ properties.erase(it);
+ }
+ break;
+ }
+ }
+}
+
+bool AppliedClassProperties::empty() const {
+ return properties.empty();
+}
+
+} \ No newline at end of file
diff --git a/src/mbgl/style/applied_class_properties.hpp b/src/mbgl/style/applied_class_properties.hpp
new file mode 100644
index 0000000000..827f15a2a1
--- /dev/null
+++ b/src/mbgl/style/applied_class_properties.hpp
@@ -0,0 +1,39 @@
+#ifndef MBGL_STYLE_APPLIED_CLASS_PROPERTIES
+#define MBGL_STYLE_APPLIED_CLASS_PROPERTIES
+
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/class_dictionary.hpp>
+#include <mbgl/util/time.hpp>
+
+#include <list>
+
+namespace mbgl {
+
+class AppliedClassProperty {
+public:
+ AppliedClassProperty(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value);
+
+public:
+ const ClassID name;
+ const timestamp begin;
+ const timestamp end;
+ const PropertyValue value;
+};
+
+
+class AppliedClassProperties {
+public:
+ std::list<AppliedClassProperty> properties;
+
+public:
+ // Returns thie ID of the most recent
+ ClassID mostRecent() const;
+ void add(ClassID class_id, timestamp begin, timestamp end, const PropertyValue &value);
+ bool hasTransitions() const;
+ void cleanup(timestamp now);
+ bool empty() const;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/class_dictionary.cpp b/src/mbgl/style/class_dictionary.cpp
new file mode 100644
index 0000000000..ba7c0d55be
--- /dev/null
+++ b/src/mbgl/style/class_dictionary.cpp
@@ -0,0 +1,51 @@
+#include <mbgl/style/class_dictionary.hpp>
+
+#include <uv.h>
+
+namespace mbgl {
+
+ClassDictionary::ClassDictionary() {}
+
+ClassDictionary &ClassDictionary::Get() {
+ // Note: We should eventually switch to uv_key_* functions, but libuv 0.10 doesn't have these
+ // yet. Instead, we're using the pthread functions directly for now.
+ static pthread_once_t store_once = PTHREAD_ONCE_INIT;
+ static pthread_key_t store_key;
+
+ // Create the key.
+ pthread_once(&store_once, []() {
+ pthread_key_create(&store_key, [](void *ptr) {
+ delete reinterpret_cast<ClassDictionary *>(ptr);
+ });
+ });
+
+ ClassDictionary *ptr = reinterpret_cast<ClassDictionary *>(pthread_getspecific(store_key));
+ if (ptr == nullptr) {
+ ptr = new ClassDictionary();
+ pthread_setspecific(store_key, ptr);
+ }
+
+ return *ptr;
+}
+
+ClassID ClassDictionary::lookup(const std::string &class_name) {
+ auto it = store.find(class_name);
+ if (it == store.end()) {
+ // Insert the class name into the store.
+ ClassID id = ClassID(uint32_t(ClassID::Named) + offset++);
+ store.emplace(class_name, id);
+ return id;
+ } else {
+ return it->second;
+ }
+}
+
+ClassID ClassDictionary::normalize(ClassID id) {
+ if (id >= ClassID::Named) {
+ return ClassID::Named;
+ } else {
+ return id;
+ }
+}
+
+}
diff --git a/src/mbgl/style/class_dictionary.hpp b/src/mbgl/style/class_dictionary.hpp
new file mode 100644
index 0000000000..ecf80be3e3
--- /dev/null
+++ b/src/mbgl/style/class_dictionary.hpp
@@ -0,0 +1,37 @@
+#ifndef MBGL_STYLE_CLASS_DICTIONARY
+#define MBGL_STYLE_CLASS_DICTIONARY
+
+#include <cstdint>
+#include <string>
+#include <unordered_map>
+
+namespace mbgl {
+
+enum class ClassID : uint32_t {
+ Fallback = 0, // These values are from the fallback properties
+ Default = 1, // These values are from the default style for a layer
+ Named = 2 // These values (and all subsequent IDs) are from a named style from the layer
+};
+
+class ClassDictionary {
+private:
+ ClassDictionary();
+
+public:
+ static ClassDictionary &Get();
+
+ // Returns an ID for a class name. If the class name does not yet have an ID, one is
+ // auto-generated and stored for future reference.
+ ClassID lookup(const std::string &class_name);
+
+ // Returns either Fallback, Default or Named, depending on the type of the class id.
+ ClassID normalize(ClassID id);
+
+private:
+ std::unordered_map<std::string, ClassID> store = { { "", ClassID::Default } };
+ uint32_t offset = 0;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/class_properties.cpp b/src/mbgl/style/class_properties.cpp
new file mode 100644
index 0000000000..e7bf855bfc
--- /dev/null
+++ b/src/mbgl/style/class_properties.cpp
@@ -0,0 +1,14 @@
+#include <mbgl/style/class_properties.hpp>
+
+namespace mbgl {
+
+const PropertyTransition &ClassProperties::getTransition(PropertyKey key, const PropertyTransition &defaultTransition) const {
+ auto it = transitions.find(key);
+ if (it == transitions.end()) {
+ return defaultTransition;
+ } else {
+ return it->second;
+ }
+}
+
+}
diff --git a/src/mbgl/style/class_properties.hpp b/src/mbgl/style/class_properties.hpp
new file mode 100644
index 0000000000..888a90c5d7
--- /dev/null
+++ b/src/mbgl/style/class_properties.hpp
@@ -0,0 +1,43 @@
+#ifndef MBGL_STYLE_CLASS_PROPERTIES
+#define MBGL_STYLE_CLASS_PROPERTIES
+
+#include <mbgl/style/property_key.hpp>
+#include <mbgl/style/property_value.hpp>
+#include <mbgl/style/property_transition.hpp>
+
+#include <map>
+
+namespace mbgl {
+
+class ClassProperties {
+public:
+ inline ClassProperties() {}
+ inline ClassProperties(ClassProperties &&properties_)
+ : properties(std::move(properties_.properties)) {}
+
+ inline void set(PropertyKey key, const PropertyValue &value) {
+ properties.emplace(key, value);
+ }
+
+ inline void set(PropertyKey key, const PropertyTransition &transition) {
+ transitions.emplace(key, transition);
+ }
+
+ const PropertyTransition &getTransition(PropertyKey key, const PropertyTransition &defaultTransition) const;
+
+ // Route-through iterable interface so that you can iterate on the object as is.
+ inline std::map<PropertyKey, PropertyValue>::const_iterator begin() const {
+ return properties.begin();
+ }
+ inline std::map<PropertyKey, PropertyValue>::const_iterator end() const {
+ return properties.end();
+ }
+
+public:
+ std::map<PropertyKey, PropertyValue> properties;
+ std::map<PropertyKey, PropertyTransition> transitions;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/filter_expression.cpp b/src/mbgl/style/filter_expression.cpp
new file mode 100644
index 0000000000..7d4f60b3ed
--- /dev/null
+++ b/src/mbgl/style/filter_expression.cpp
@@ -0,0 +1,123 @@
+#include <mbgl/map/vector_tile.hpp>
+#include <mbgl/platform/log.hpp>
+
+namespace mbgl {
+
+Value parseFeatureType(const Value& value) {
+ 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));
+ } else {
+ Log::Warning(Event::ParseStyle, "value for $type filter must be Point, LineString, or Polygon");
+ return Value(uint64_t(FeatureType::Unknown));
+ }
+}
+
+template <class Expression>
+FilterExpression parseBinaryFilter(const rapidjson::Value& value) {
+ FilterExpression empty;
+
+ if (value.Size() < 3) {
+ Log::Warning(Event::ParseStyle, "filter expression must have 3 elements");
+ return empty;
+ }
+
+ if (!value[1u].IsString()) {
+ Log::Warning(Event::ParseStyle, "filter expression key must be a string");
+ return empty;
+ }
+
+ Expression expression;
+ expression.key = { value[1u].GetString(), value[1u].GetStringLength() };
+ expression.value = parseValue(value[2u]);
+
+ if (expression.key == "$type") {
+ expression.value = parseFeatureType(expression.value);
+ }
+
+ return expression;
+}
+
+template <class Expression>
+FilterExpression parseSetFilter(const rapidjson::Value& value) {
+ FilterExpression empty;
+
+ if (value.Size() < 2) {
+ Log::Warning(Event::ParseStyle, "filter expression must at least 2 elements");
+ return empty;
+ }
+
+ if (!value[1u].IsString()) {
+ Log::Warning(Event::ParseStyle, "filter expression key must be a string");
+ return empty;
+ }
+
+ Expression expression;
+ expression.key = { value[1u].GetString(), value[1u].GetStringLength() };
+ for (rapidjson::SizeType i = 2; i < value.Size(); ++i) {
+ expression.values.push_back(parseValue(value[i]));
+ }
+ return expression;
+}
+
+template <class Expression>
+FilterExpression parseCompoundFilter(const rapidjson::Value& value) {
+ Expression expression;
+ for (rapidjson::SizeType i = 1; i < value.Size(); ++i) {
+ expression.expressions.push_back(parseFilterExpression(value[i]));
+ }
+ return expression;
+}
+
+FilterExpression parseFilterExpression(const rapidjson::Value& value) {
+ FilterExpression empty;
+
+ if (!value.IsArray()) {
+ Log::Warning(Event::ParseStyle, "filter expression must be an array");
+ return empty;
+ }
+
+ if (value.Size() < 1) {
+ Log::Warning(Event::ParseStyle, "filter expression must have at least 1 element");
+ return empty;
+ }
+
+ if (!value[0u].IsString()) {
+ Log::Warning(Event::ParseStyle, "filter operator must be a string");
+ return empty;
+ }
+
+ std::string op = { value[0u].GetString(), value[0u].GetStringLength() };
+
+ if (op == "==") {
+ return parseBinaryFilter<EqualsExpression>(value);
+ } else if (op == "!=") {
+ return parseBinaryFilter<NotEqualsExpression>(value);
+ } else if (op == ">") {
+ return parseBinaryFilter<GreaterThanExpression>(value);
+ } else if (op == ">=") {
+ return parseBinaryFilter<GreaterThanEqualsExpression>(value);
+ } else if (op == "<") {
+ return parseBinaryFilter<LessThanExpression>(value);
+ } else if (op == "<=") {
+ return parseBinaryFilter<LessThanEqualsExpression>(value);
+ } else if (op == "in") {
+ return parseSetFilter<InExpression>(value);
+ } else if (op == "!in") {
+ return parseSetFilter<NotInExpression>(value);
+ } else if (op == "all") {
+ return parseCompoundFilter<AllExpression>(value);
+ } else if (op == "any") {
+ return parseCompoundFilter<AnyExpression>(value);
+ } else if (op == "none") {
+ return parseCompoundFilter<NoneExpression>(value);
+ } else {
+ Log::Warning(Event::ParseStyle, "filter operator must be one of \"==\", \"!=\", \">\", \">=\", \"<\", \"<=\", \"in\", \"!in\", \"all\", \"any\", \"none\"");
+ return empty;
+ }
+}
+
+}
diff --git a/src/mbgl/style/filter_expression.hpp b/src/mbgl/style/filter_expression.hpp
new file mode 100644
index 0000000000..8c6f447770
--- /dev/null
+++ b/src/mbgl/style/filter_expression.hpp
@@ -0,0 +1,125 @@
+#ifndef MBGL_STYLE_FILTER_EXPRESSION
+#define MBGL_STYLE_FILTER_EXPRESSION
+
+#include <mbgl/style/value.hpp>
+
+#include <rapidjson/document.h>
+
+#include <string>
+#include <vector>
+
+namespace mbgl {
+
+typedef mapbox::util::variant<
+ struct NullExpression,
+ struct EqualsExpression,
+ struct NotEqualsExpression,
+ struct LessThanExpression,
+ struct LessThanEqualsExpression,
+ struct GreaterThanExpression,
+ struct GreaterThanEqualsExpression,
+ struct InExpression,
+ struct NotInExpression,
+ struct AnyExpression,
+ struct AllExpression,
+ struct NoneExpression
+ > FilterExpression;
+
+FilterExpression parseFilterExpression(const rapidjson::Value&);
+
+template <class Extractor>
+bool evaluate(const FilterExpression&, const Extractor&);
+
+struct NullExpression {
+ template <class Extractor>
+ bool evaluate(const Extractor&) const { return true; }
+};
+
+struct EqualsExpression {
+ std::string key;
+ Value value;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct NotEqualsExpression {
+ std::string key;
+ Value value;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct LessThanExpression {
+ std::string key;
+ Value value;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct LessThanEqualsExpression {
+ std::string key;
+ Value value;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct GreaterThanExpression {
+ std::string key;
+ Value value;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct GreaterThanEqualsExpression {
+ std::string key;
+ Value value;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct InExpression {
+ std::string key;
+ std::vector<Value> values;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct NotInExpression {
+ std::string key;
+ std::vector<Value> values;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct AnyExpression {
+ std::vector<FilterExpression> expressions;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct AllExpression {
+ std::vector<FilterExpression> expressions;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+struct NoneExpression {
+ std::vector<FilterExpression> expressions;
+
+ template <class Extractor>
+ bool evaluate(const Extractor&) const;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/filter_expression_private.hpp b/src/mbgl/style/filter_expression_private.hpp
new file mode 100644
index 0000000000..381f8f617c
--- /dev/null
+++ b/src/mbgl/style/filter_expression_private.hpp
@@ -0,0 +1,118 @@
+#include <mbgl/util/optional.hpp>
+#include <mbgl/style/value_comparison.hpp>
+
+namespace mbgl {
+
+template <class Extractor>
+struct Evaluator : public mapbox::util::static_visitor<bool>
+{
+ const Extractor& extractor;
+
+ Evaluator(const Extractor& extractor_)
+ : extractor(extractor_) {}
+
+ template <class E>
+ bool operator()(const E& e) const { return e.evaluate(extractor); }
+};
+
+template <class Extractor>
+bool evaluate(const FilterExpression& expression, const Extractor& extractor) {
+ return mapbox::util::apply_visitor(Evaluator<Extractor>(extractor), expression);
+};
+
+template <class Extractor>
+bool EqualsExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ return actual && util::relaxed_equal(*actual, value);
+}
+
+template <class Extractor>
+bool NotEqualsExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ return !actual || util::relaxed_not_equal(*actual, value);
+}
+
+template <class Extractor>
+bool LessThanExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ return actual && util::relaxed_less(*actual, value);
+}
+
+template <class Extractor>
+bool LessThanEqualsExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ return actual && util::relaxed_less_equal(*actual, value);
+}
+
+template <class Extractor>
+bool GreaterThanExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ return actual && util::relaxed_greater(*actual, value);
+}
+
+template <class Extractor>
+bool GreaterThanEqualsExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ return actual && util::relaxed_greater_equal(*actual, value);
+}
+
+template <class Extractor>
+bool InExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ if (!actual)
+ return false;
+ for (const auto& v: values) {
+ if (util::relaxed_equal(*actual, v)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class Extractor>
+bool NotInExpression::evaluate(const Extractor& extractor) const {
+ mapbox::util::optional<Value> actual = extractor.getValue(key);
+ if (!actual)
+ return true;
+ for (const auto& v: values) {
+ if (util::relaxed_equal(*actual, v)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <class Extractor>
+bool AnyExpression::evaluate(const Extractor& extractor) const {
+ Evaluator<Extractor> evaluator(extractor);
+ for (const auto& e: expressions) {
+ if (mapbox::util::apply_visitor(evaluator, e)) {
+ return true;
+ }
+ }
+ return false;
+}
+
+template <class Extractor>
+bool AllExpression::evaluate(const Extractor& extractor) const {
+ Evaluator<Extractor> evaluator(extractor);
+ for (const auto& e: expressions) {
+ if (!mapbox::util::apply_visitor(evaluator, e)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+template <class Extractor>
+bool NoneExpression::evaluate(const Extractor& extractor) const {
+ Evaluator<Extractor> evaluator(extractor);
+ for (const auto& e: expressions) {
+ if (mapbox::util::apply_visitor(evaluator, e)) {
+ return false;
+ }
+ }
+ return true;
+}
+
+}
diff --git a/src/mbgl/style/function_properties.cpp b/src/mbgl/style/function_properties.cpp
new file mode 100644
index 0000000000..69466c1f64
--- /dev/null
+++ b/src/mbgl/style/function_properties.cpp
@@ -0,0 +1,68 @@
+#include <mbgl/style/function_properties.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/interpolate.hpp>
+
+#include <cmath>
+
+namespace mbgl {
+
+template <typename T>
+inline T defaultStopsValue();
+
+template <> inline bool defaultStopsValue() { return true; }
+template <> inline float defaultStopsValue() { return 1.0f; }
+template <> inline Color defaultStopsValue() { return {{ 0, 0, 0, 1 }}; }
+
+
+template <typename T>
+T StopsFunction<T>::evaluate(float z) const {
+ bool smaller = false;
+ float smaller_z = 0.0f;
+ T smaller_val = T();
+ bool larger = false;
+ float larger_z = 0.0f;
+ T larger_val = T();
+
+ for (uint32_t i = 0; i < values.size(); i++) {
+ float stop_z = values[i].first;
+ T stop_val = values[i].second;
+ if (stop_z <= z && (!smaller || smaller_z < stop_z)) {
+ smaller = true;
+ smaller_z = stop_z;
+ smaller_val = stop_val;
+ }
+ if (stop_z >= z && (!larger || larger_z > stop_z)) {
+ larger = true;
+ larger_z = stop_z;
+ larger_val = stop_val;
+ }
+ }
+
+ if (smaller && larger) {
+ if (larger_z == smaller_z || larger_val == smaller_val) {
+ return smaller_val;
+ }
+ const float zoomDiff = larger_z - smaller_z;
+ const float zoomProgress = z - smaller_z;
+ if (base == 1.0f) {
+ const float t = zoomProgress / zoomDiff;
+ return util::interpolate(smaller_val, larger_val, t);
+ } else {
+ const float t = (std::pow(base, zoomProgress) - 1) / (std::pow(base, zoomDiff) - 1);
+ return util::interpolate(smaller_val, larger_val, t);
+ }
+ } else if (larger) {
+ return larger_val;
+ } else if (smaller) {
+ return smaller_val;
+ } else {
+ // No stop defined.
+ return defaultStopsValue<T>();
+ }
+}
+
+template bool StopsFunction<bool>::evaluate(float z) const;
+template float StopsFunction<float>::evaluate(float z) const;
+template Color StopsFunction<Color>::evaluate(float z) const;
+
+}
diff --git a/src/mbgl/style/function_properties.hpp b/src/mbgl/style/function_properties.hpp
new file mode 100644
index 0000000000..924f192330
--- /dev/null
+++ b/src/mbgl/style/function_properties.hpp
@@ -0,0 +1,55 @@
+#ifndef MBGL_STYLE_FUNCTION_PROPERTIES
+#define MBGL_STYLE_FUNCTION_PROPERTIES
+
+#include <mbgl/util/variant.hpp>
+
+#include <vector>
+
+namespace mbgl {
+
+template <typename T>
+struct ConstantFunction {
+ inline ConstantFunction(const T &value_) : value(value_) {}
+ inline T evaluate(float) const { return value; }
+
+private:
+ const T value;
+};
+
+template <typename T>
+struct StopsFunction {
+ inline StopsFunction(const std::vector<std::pair<float, T>> &values_, float base_) : values(values_), base(base_) {}
+ T evaluate(float z) const;
+
+private:
+ const std::vector<std::pair<float, T>> values;
+ const float base;
+};
+
+template <typename T>
+using Function = mapbox::util::variant<
+ std::false_type,
+ ConstantFunction<T>,
+ StopsFunction<T>
+>;
+
+template <typename T>
+struct FunctionEvaluator {
+ typedef T result_type;
+ inline FunctionEvaluator(float z_) : z(z_) {}
+
+ inline result_type operator()(const std::false_type &) {
+ return result_type();
+ }
+
+ template <template <typename> class Fn>
+ inline result_type operator()(const Fn<T>& fn) {
+ return fn.evaluate(z);
+ }
+private:
+ float z;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp
new file mode 100644
index 0000000000..965baf6c4b
--- /dev/null
+++ b/src/mbgl/style/property_fallback.cpp
@@ -0,0 +1,61 @@
+#include <mbgl/style/property_fallback.hpp>
+#include <mbgl/style/style_properties.hpp>
+
+namespace mbgl {
+
+const std::map<PropertyKey, PropertyValue> PropertyFallbackValue::properties = {
+ { PropertyKey::FillAntialias, defaultStyleProperties<FillProperties>().antialias },
+ { PropertyKey::FillOpacity, defaultStyleProperties<FillProperties>().opacity },
+ { PropertyKey::FillColor, defaultStyleProperties<FillProperties>().fill_color },
+ // no FillOutlineColor on purpose.
+ { PropertyKey::FillTranslateX, defaultStyleProperties<FillProperties>().translate[0] },
+ { PropertyKey::FillTranslateY, defaultStyleProperties<FillProperties>().translate[1] },
+ { PropertyKey::FillTranslateAnchor, defaultStyleProperties<FillProperties>().translateAnchor },
+
+ { PropertyKey::LineOpacity, defaultStyleProperties<LineProperties>().opacity },
+ { PropertyKey::LineColor, defaultStyleProperties<LineProperties>().color },
+ { PropertyKey::LineTranslateX, defaultStyleProperties<LineProperties>().translate[0] },
+ { PropertyKey::LineTranslateY, defaultStyleProperties<LineProperties>().translate[1] },
+ { PropertyKey::LineTranslateAnchor, defaultStyleProperties<LineProperties>().translateAnchor },
+ { PropertyKey::LineWidth, defaultStyleProperties<LineProperties>().width },
+ { PropertyKey::LineGapWidth, defaultStyleProperties<LineProperties>().gap_width },
+ { PropertyKey::LineBlur, defaultStyleProperties<LineProperties>().blur },
+ { PropertyKey::LineDashLand, defaultStyleProperties<LineProperties>().dash_array[0] },
+ { PropertyKey::LineDashGap, defaultStyleProperties<LineProperties>().dash_array[1] },
+
+ { PropertyKey::IconOpacity, defaultStyleProperties<SymbolProperties>().icon.opacity },
+ { PropertyKey::IconRotate, defaultStyleProperties<SymbolProperties>().icon.rotate },
+ { PropertyKey::IconSize, defaultStyleProperties<SymbolProperties>().icon.size },
+ { PropertyKey::IconColor, defaultStyleProperties<SymbolProperties>().icon.color },
+ { PropertyKey::IconHaloColor, defaultStyleProperties<SymbolProperties>().icon.halo_color },
+ { PropertyKey::IconHaloWidth, defaultStyleProperties<SymbolProperties>().icon.halo_width },
+ { PropertyKey::IconHaloBlur, defaultStyleProperties<SymbolProperties>().icon.halo_blur },
+ { PropertyKey::IconTranslateX, defaultStyleProperties<SymbolProperties>().icon.translate[0] },
+ { PropertyKey::IconTranslateY, defaultStyleProperties<SymbolProperties>().icon.translate[1] },
+ { PropertyKey::IconTranslateAnchor, defaultStyleProperties<SymbolProperties>().icon.translate_anchor },
+
+ { PropertyKey::TextOpacity, defaultStyleProperties<SymbolProperties>().text.opacity },
+ { PropertyKey::TextSize, defaultStyleProperties<SymbolProperties>().text.size },
+ { PropertyKey::TextColor, defaultStyleProperties<SymbolProperties>().text.color },
+ { PropertyKey::TextHaloColor, defaultStyleProperties<SymbolProperties>().text.halo_color },
+ { PropertyKey::TextHaloWidth, defaultStyleProperties<SymbolProperties>().text.halo_width },
+ { PropertyKey::TextHaloBlur, defaultStyleProperties<SymbolProperties>().text.halo_blur },
+ { PropertyKey::TextTranslateX, defaultStyleProperties<SymbolProperties>().text.translate[0] },
+ { PropertyKey::TextTranslateY, defaultStyleProperties<SymbolProperties>().text.translate[1] },
+ { PropertyKey::TextTranslateAnchor, defaultStyleProperties<SymbolProperties>().text.translate_anchor },
+
+ { PropertyKey::RasterOpacity, defaultStyleProperties<RasterProperties>().opacity },
+ { PropertyKey::RasterHueRotate, defaultStyleProperties<RasterProperties>().hue_rotate },
+ { PropertyKey::RasterBrightnessLow, defaultStyleProperties<RasterProperties>().brightness[0] },
+ { PropertyKey::RasterBrightnessHigh, defaultStyleProperties<RasterProperties>().brightness[1] },
+ { PropertyKey::RasterSaturation, defaultStyleProperties<RasterProperties>().saturation },
+ { PropertyKey::RasterContrast, defaultStyleProperties<RasterProperties>().contrast },
+ { PropertyKey::RasterFade, defaultStyleProperties<RasterProperties>().fade },
+
+ { PropertyKey::BackgroundOpacity, defaultStyleProperties<BackgroundProperties>().opacity },
+ { PropertyKey::BackgroundColor, defaultStyleProperties<BackgroundProperties>().color },
+};
+
+const PropertyValue PropertyFallbackValue::defaultProperty = false;
+
+}
diff --git a/src/mbgl/style/property_fallback.hpp b/src/mbgl/style/property_fallback.hpp
new file mode 100644
index 0000000000..5c5eae0cd6
--- /dev/null
+++ b/src/mbgl/style/property_fallback.hpp
@@ -0,0 +1,29 @@
+#ifndef MBGL_STYLE_PROPERTY_FALLBACK
+#define MBGL_STYLE_PROPERTY_FALLBACK
+
+#include <mbgl/style/property_key.hpp>
+#include <mbgl/style/property_value.hpp>
+
+#include <map>
+
+namespace mbgl {
+
+class PropertyFallbackValue {
+public:
+ static const PropertyValue &Get(PropertyKey key) {
+ auto it = properties.find(key);
+ if (it != properties.end()) {
+ return it->second;
+ } else {
+ return defaultProperty;
+ }
+ }
+
+private:
+ static const std::map<PropertyKey, PropertyValue> properties;
+ static const PropertyValue defaultProperty;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/property_key.hpp b/src/mbgl/style/property_key.hpp
new file mode 100644
index 0000000000..efeebf0242
--- /dev/null
+++ b/src/mbgl/style/property_key.hpp
@@ -0,0 +1,70 @@
+#ifndef MBGL_STYLE_PROPERTY_KEY
+#define MBGL_STYLE_PROPERTY_KEY
+
+namespace mbgl {
+
+enum class PropertyKey {
+ FillAntialias,
+ FillOpacity,
+ FillColor,
+ FillOutlineColor,
+ FillTranslate, // for transitions only
+ FillTranslateX,
+ FillTranslateY,
+ FillTranslateAnchor,
+ FillImage,
+
+ LineOpacity,
+ LineColor,
+ LineTranslate, // for transitions only
+ LineTranslateX,
+ LineTranslateY,
+ LineTranslateAnchor,
+ LineWidth,
+ LineGapWidth,
+ LineBlur,
+ LineDashArray, // for transitions only
+ LineDashLand,
+ LineDashGap,
+ LineImage,
+
+ IconOpacity,
+ IconRotate,
+ IconSize,
+ IconColor,
+ IconHaloColor,
+ IconHaloWidth,
+ IconHaloBlur,
+ IconTranslate, // for transitions only
+ IconTranslateX,
+ IconTranslateY,
+ IconTranslateAnchor,
+
+ TextOpacity,
+ TextSize,
+ TextColor,
+ TextHaloColor,
+ TextHaloWidth,
+ TextHaloBlur,
+ TextTranslate, // for transitions only
+ TextTranslateX,
+ TextTranslateY,
+ TextTranslateAnchor,
+
+ RasterOpacity,
+ RasterHueRotate,
+ RasterBrightness, // for transitions only
+ RasterBrightnessLow,
+ RasterBrightnessHigh,
+ RasterSaturation,
+ RasterContrast,
+ RasterFade,
+
+ BackgroundOpacity,
+ BackgroundColor,
+ BackgroundImage
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/property_transition.hpp b/src/mbgl/style/property_transition.hpp
new file mode 100644
index 0000000000..07b7cfe288
--- /dev/null
+++ b/src/mbgl/style/property_transition.hpp
@@ -0,0 +1,15 @@
+#ifndef MBGL_STYLE_PROPERTY_TRANSITION
+#define MBGL_STYLE_PROPERTY_TRANSITION
+
+#include <cstdint>
+
+namespace mbgl {
+
+struct PropertyTransition {
+ uint16_t duration = 0;
+ uint16_t delay = 0;
+};
+
+}
+
+#endif \ No newline at end of file
diff --git a/src/mbgl/style/property_value.hpp b/src/mbgl/style/property_value.hpp
new file mode 100644
index 0000000000..1b22b31177
--- /dev/null
+++ b/src/mbgl/style/property_value.hpp
@@ -0,0 +1,21 @@
+#ifndef MBGL_STYLE_PROPERTY_VALUE
+#define MBGL_STYLE_PROPERTY_VALUE
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/style/function_properties.hpp>
+#include <mbgl/style/types.hpp>
+
+namespace mbgl {
+
+typedef mapbox::util::variant<
+ std::string,
+ TranslateAnchorType,
+ RotateAnchorType,
+ Function<bool>,
+ Function<float>,
+ Function<Color>
+> PropertyValue;
+
+}
+
+#endif
diff --git a/src/mbgl/style/style.cpp b/src/mbgl/style/style.cpp
new file mode 100644
index 0000000000..15ca4e14fb
--- /dev/null
+++ b/src/mbgl/style/style.cpp
@@ -0,0 +1,107 @@
+#include <mbgl/style/style.hpp>
+#include <mbgl/map/sprite.hpp>
+#include <mbgl/style/style_layer_group.hpp>
+#include <mbgl/style/style_parser.hpp>
+#include <mbgl/style/style_bucket.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/time.hpp>
+#include <mbgl/util/error.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/util/uv_detail.hpp>
+#include <csscolorparser/csscolorparser.hpp>
+
+#include <rapidjson/document.h>
+
+#include <algorithm>
+
+namespace mbgl {
+
+Style::Style()
+ : mtx(util::make_unique<uv::rwlock>()) {
+}
+
+// Note: This constructor is seemingly empty, but we need to declare it anyway
+// because this file includes uv_detail.hpp, which has the declarations necessary
+// for deleting the std::unique_ptr<uv::rwlock>.
+Style::~Style() {}
+
+void Style::updateProperties(float z, timestamp now) {
+ uv::writelock lock(mtx);
+
+ if (layers) {
+ layers->updateProperties(z, now);
+ }
+
+ // Apply transitions after the first time.
+ if (!initial_render_complete) {
+ initial_render_complete = true;
+ return;
+ }
+}
+
+const std::string &Style::getSpriteURL() const {
+ return sprite_url;
+}
+
+void Style::setDefaultTransitionDuration(uint16_t duration_milliseconds) {
+ defaultTransition.duration = duration_milliseconds;
+}
+
+const std::vector<std::string> &Style::getAppliedClasses() const {
+ return appliedClasses;
+}
+
+void Style::setAppliedClasses(const std::vector<std::string> &class_names) {
+ appliedClasses = class_names;
+ updateClasses();
+}
+
+void Style::toggleClass(const std::string &name) {
+ if (name.length()) {
+ auto it = std::find(appliedClasses.begin(), appliedClasses.end(), name);
+ if (it == appliedClasses.end()) {
+ appliedClasses.push_back(name);
+ } else {
+ appliedClasses.erase(it);
+ }
+ }
+
+ updateClasses();
+}
+
+void Style::updateClasses() {
+ if (layers) {
+ layers->setClasses(appliedClasses, util::now(), defaultTransition);
+ }
+}
+
+bool Style::hasTransitions() const {
+ if (layers) {
+ if (layers->hasTransitions()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void Style::loadJSON(const uint8_t *const data) {
+ uv::writelock lock(mtx);
+
+ rapidjson::Document doc;
+ doc.Parse<0>((const char *const)data);
+ if (doc.HasParseError()) {
+ throw error::style_parse(doc.GetErrorOffset(), doc.GetParseError());
+ }
+
+ StyleParser parser;
+ parser.parse(doc);
+
+ layers = parser.getLayers();
+ sprite_url = parser.getSprite();
+ glyph_url = parser.getGlyphURL();
+
+ updateClasses();
+}
+
+}
diff --git a/src/mbgl/style/style.hpp b/src/mbgl/style/style.hpp
new file mode 100644
index 0000000000..56f318ecbb
--- /dev/null
+++ b/src/mbgl/style/style.hpp
@@ -0,0 +1,68 @@
+#ifndef MBGL_STYLE_STYLE
+#define MBGL_STYLE_STYLE
+
+#include <mbgl/style/property_transition.hpp>
+#include <mbgl/style/style_source.hpp>
+
+#include <mbgl/util/time.hpp>
+#include <mbgl/util/uv.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <cstdint>
+#include <map>
+#include <string>
+#include <unordered_map>
+#include <vector>
+#include <set>
+
+namespace mbgl {
+
+class Sprite;
+class StyleLayer;
+class StyleLayerGroup;
+
+class Style {
+public:
+ struct exception : std::runtime_error { exception(const char *msg) : std::runtime_error(msg) {} };
+
+public:
+ Style();
+ ~Style();
+
+ void loadJSON(const uint8_t *const data);
+
+ size_t layerCount() const;
+ void updateProperties(float z, timestamp t);
+
+ void setDefaultTransitionDuration(uint16_t duration_milliseconds = 0);
+
+ void setAppliedClasses(const std::vector<std::string> &classes);
+ const std::vector<std::string> &getAppliedClasses() const;
+ void toggleClass(const std::string &name);
+
+ // Updates the styling information to reflect the current array
+ // of applied classes.
+ void updateClasses();
+
+ bool hasTransitions() const;
+
+ const std::string &getSpriteURL() const;
+
+public:
+ util::ptr<StyleLayerGroup> layers;
+ std::vector<std::string> appliedClasses;
+ std::string glyph_url;
+
+private:
+ std::string sprite_url;
+
+private:
+ PropertyTransition defaultTransition;
+ bool initial_render_complete = false;
+
+ std::unique_ptr<uv::rwlock> mtx;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/style_bucket.cpp b/src/mbgl/style/style_bucket.cpp
new file mode 100644
index 0000000000..9a40c2386b
--- /dev/null
+++ b/src/mbgl/style/style_bucket.cpp
@@ -0,0 +1,15 @@
+#include <mbgl/style/style_bucket.hpp>
+
+namespace mbgl {
+
+StyleBucket::StyleBucket(StyleLayerType type) {
+ switch (type) {
+ case StyleLayerType::Fill: render = StyleBucketFill{}; break;
+ case StyleLayerType::Line: render = StyleBucketLine{}; break;
+ case StyleLayerType::Symbol: render = StyleBucketSymbol{}; break;
+ case StyleLayerType::Raster: render = StyleBucketRaster{}; break;
+ default: break;
+ }
+}
+
+} \ No newline at end of file
diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp
new file mode 100644
index 0000000000..d84d35d5b2
--- /dev/null
+++ b/src/mbgl/style/style_bucket.hpp
@@ -0,0 +1,112 @@
+#ifndef MBGL_STYLE_STYLE_BUCKET
+#define MBGL_STYLE_STYLE_BUCKET
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/filter_expression.hpp>
+#include <mbgl/style/style_source.hpp>
+
+#include <mbgl/util/vec.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <mbgl/util/ptr.hpp>
+
+#include <forward_list>
+
+namespace mbgl {
+
+class Source;
+
+class StyleBucketFill {
+public:
+ WindingType winding = WindingType::NonZero;
+};
+
+class StyleBucketLine {
+public:
+ CapType cap = CapType::Butt;
+ JoinType join = JoinType::Miter;
+ float miter_limit = 2.0f;
+ float round_limit = 1.0f;
+};
+
+class StyleBucketSymbol {
+public:
+ // Make movable only.
+ inline StyleBucketSymbol() = default;
+ inline StyleBucketSymbol(StyleBucketSymbol &&) = default;
+ inline StyleBucketSymbol& operator=(StyleBucketSymbol &&) = default;
+ inline StyleBucketSymbol(const StyleBucketSymbol &) = delete;
+ inline StyleBucketSymbol& operator=(const StyleBucketSymbol &) = delete;
+
+ PlacementType placement = PlacementType::Point;
+ float min_distance = 250.0f;
+ bool avoid_edges = false;
+
+ struct {
+ bool allow_overlap = false;
+ bool ignore_placement = false;
+ bool optional = false;
+ RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
+ float max_size = 1.0f;
+ std::string image;
+ float rotate = 0.0f;
+ float padding = 2.0f;
+ bool keep_upright = false;
+ vec2<float> offset = {0, 0};
+ } icon;
+
+ struct {
+ RotationAlignmentType rotation_alignment = RotationAlignmentType::Viewport;
+ std::string field;
+ std::string font;
+ float max_size = 16.0f;
+ float max_width = 15.0f * 24 /* em */;
+ float line_height = 1.2f * 24 /* em */;
+ float letter_spacing = 0.0f * 24 /* em */;
+ TextJustifyType justify = TextJustifyType::Center;
+ TextAnchorType anchor = TextAnchorType::Center;
+ float max_angle = 45.0f /* degrees */;
+ float rotate = 0.0f;
+ float slant = 0.0f;
+ float padding = 2.0f;
+ bool keep_upright = true;
+ TextTransformType transform = TextTransformType::None;
+ vec2<float> offset = {0, 0};
+ bool allow_overlap = false;
+ bool ignore_placement = false;
+ bool optional = false;
+ } text;
+};
+
+class StyleBucketRaster {
+public:
+ bool prerendered = false;
+ uint16_t size = 256;
+ float blur = 0.0f;
+ float buffer = 0.03125f;
+};
+
+typedef mapbox::util::variant<StyleBucketFill, StyleBucketLine, StyleBucketSymbol,
+ StyleBucketRaster, std::false_type> StyleBucketRender;
+
+
+class StyleBucket {
+public:
+ typedef util::ptr<StyleBucket> Ptr;
+
+ StyleBucket(StyleLayerType type);
+
+ std::string name;
+ util::ptr<StyleSource> style_source;
+ std::string source_layer;
+ FilterExpression filter;
+ StyleBucketRender render = std::false_type();
+ float min_zoom = -std::numeric_limits<float>::infinity();
+ float max_zoom = std::numeric_limits<float>::infinity();
+};
+
+
+
+};
+
+#endif
diff --git a/src/mbgl/style/style_layer.cpp b/src/mbgl/style/style_layer.cpp
new file mode 100644
index 0000000000..e58756afa4
--- /dev/null
+++ b/src/mbgl/style/style_layer.cpp
@@ -0,0 +1,284 @@
+#include <mbgl/style/style_layer.hpp>
+#include <mbgl/style/style_bucket.hpp>
+#include <mbgl/style/style_layer_group.hpp>
+#include <mbgl/style/property_fallback.hpp>
+
+#include <mbgl/util/interpolate.hpp>
+
+namespace mbgl {
+
+StyleLayer::StyleLayer(const std::string &id_, std::map<ClassID, ClassProperties> &&styles_)
+ : id(id_), styles(std::move(styles_)) {}
+
+bool StyleLayer::isBackground() const {
+ return type == StyleLayerType::Background;
+}
+
+void StyleLayer::setClasses(const std::vector<std::string> &class_names, const timestamp now,
+ const PropertyTransition &defaultTransition) {
+ // Stores all keys that we have already added transitions for.
+ std::set<PropertyKey> already_applied;
+
+ // Reverse iterate through all class names and apply them last to first.
+ for (auto it = class_names.rbegin(); it != class_names.rend(); ++it) {
+ const std::string &class_name = *it;
+ // From here on, we're only dealing with IDs to avoid comparing strings all the time.
+ const ClassID class_id = ClassDictionary::Get().lookup(class_name);
+ applyClassProperties(class_id, already_applied, now, defaultTransition);
+ }
+
+ // As the last class, apply the default class.
+ applyClassProperties(ClassID::Default, already_applied, now, defaultTransition);
+
+ // Make sure that we also transition to the fallback value for keys that aren't changed by
+ // any applied classes.
+ for (std::pair<const PropertyKey, AppliedClassProperties> &property_pair : appliedStyle) {
+ const PropertyKey key = property_pair.first;
+ if (already_applied.find(key) != already_applied.end()) {
+ // This property has already been set by a previous class, so we don't need to
+ // transition to the fallback.
+ continue;
+ }
+
+ AppliedClassProperties &appliedProperties = property_pair.second;
+ // Make sure that we don't do double transitions to the fallback value.
+ if (appliedProperties.mostRecent() != ClassID::Fallback) {
+ // This property key hasn't been set by a previous class, so we need to add a transition
+ // to the fallback value for that key.
+ const timestamp begin = now + defaultTransition.delay * 1_millisecond;
+ const timestamp end = begin + defaultTransition.duration * 1_millisecond;
+ const PropertyValue &value = PropertyFallbackValue::Get(key);
+ appliedProperties.add(ClassID::Fallback, begin, end, value);
+ }
+ }
+
+ // Update all child layers as well.
+ if (layers) {
+ layers->setClasses(class_names, now, defaultTransition);
+ }
+}
+
+// Helper function for applying all properties of a a single class that haven't been applied yet.
+void StyleLayer::applyClassProperties(const ClassID class_id,
+ std::set<PropertyKey> &already_applied, timestamp now,
+ const PropertyTransition &defaultTransition) {
+ auto style_it = styles.find(class_id);
+ if (style_it == styles.end()) {
+ // There is no class in this layer with this class_name.
+ return;
+ }
+
+ // Loop through all the properties in this style, and add transitions to them, if they're
+ // not already the most recent transition.
+ const ClassProperties &class_properties = style_it->second;
+ for (const std::pair<PropertyKey, PropertyValue> &property_pair : class_properties) {
+ PropertyKey key = property_pair.first;
+ if (already_applied.find(key) != already_applied.end()) {
+ // This property has already been set by a previous class.
+ continue;
+ }
+
+ // Mark this property as written by a previous class, so that subsequent
+ // classes won't override this.
+ already_applied.insert(key);
+
+ // If the most recent transition is not the one with the highest priority, create
+ // a transition.
+ AppliedClassProperties &appliedProperties = appliedStyle[key];
+ if (appliedProperties.mostRecent() != class_id) {
+ const PropertyTransition &transition =
+ class_properties.getTransition(key, defaultTransition);
+ const timestamp begin = now + transition.delay * 1_millisecond;
+ const timestamp end = begin + transition.duration * 1_millisecond;
+ const PropertyValue &value = property_pair.second;
+ appliedProperties.add(class_id, begin, end, value);
+ }
+ }
+}
+
+template <typename T>
+struct PropertyEvaluator {
+ typedef T result_type;
+ PropertyEvaluator(float z_) : z(z_) {}
+
+ template <typename P, typename std::enable_if<std::is_convertible<P, T>::value, int>::type = 0>
+ T operator()(const P &value) const {
+ return value;
+ }
+
+ T operator()(const Function<T> &value) const {
+ return mapbox::util::apply_visitor(FunctionEvaluator<T>(z), value);
+ }
+
+ template <typename P, typename std::enable_if<!std::is_convertible<P, T>::value, int>::type = 0>
+ T operator()(const P &) const {
+ return T();
+ }
+
+private:
+ const float z;
+};
+
+template <typename T>
+void StyleLayer::applyStyleProperty(PropertyKey key, T &target, const float z, const timestamp now) {
+ auto it = appliedStyle.find(key);
+ if (it != appliedStyle.end()) {
+ AppliedClassProperties &applied = it->second;
+ // Iterate through all properties that we need to apply in order.
+ const PropertyEvaluator<T> evaluator(z);
+ for (AppliedClassProperty &property : applied.properties) {
+ if (now >= property.begin) {
+ // We overwrite the current property with the new value.
+ target = mapbox::util::apply_visitor(evaluator, property.value);
+ } else {
+ // Do not apply this property because its transition hasn't begun yet.
+ }
+ }
+ }
+}
+
+template <typename T>
+void StyleLayer::applyTransitionedStyleProperty(PropertyKey key, T &target, const float z, const timestamp now) {
+ auto it = appliedStyle.find(key);
+ if (it != appliedStyle.end()) {
+ AppliedClassProperties &applied = it->second;
+ // Iterate through all properties that we need to apply in order.
+ const PropertyEvaluator<T> evaluator(z);
+ for (AppliedClassProperty &property : applied.properties) {
+ if (now >= property.end) {
+ // We overwrite the current property with the new value.
+ target = mapbox::util::apply_visitor(evaluator, property.value);
+ } else if (now >= property.begin) {
+ // We overwrite the current property partially with the new value.
+ float progress = float(now - property.begin) / float(property.end - property.begin);
+ target = util::interpolate(target, mapbox::util::apply_visitor(evaluator, property.value), progress);
+ } else {
+ // Do not apply this property because its transition hasn't begun yet.
+ }
+ }
+ }
+}
+
+template <>
+void StyleLayer::applyStyleProperties<FillProperties>(const float z, const timestamp now) {
+ properties.set<FillProperties>();
+ FillProperties &fill = properties.get<FillProperties>();
+ applyStyleProperty(PropertyKey::FillAntialias, fill.antialias, z, now);
+ applyTransitionedStyleProperty(PropertyKey::FillOpacity, fill.opacity, z, now);
+ applyTransitionedStyleProperty(PropertyKey::FillColor, fill.fill_color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::FillOutlineColor, fill.stroke_color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::FillTranslateX, fill.translate[0], z, now);
+ applyTransitionedStyleProperty(PropertyKey::FillTranslateY, fill.translate[1], z, now);
+ applyStyleProperty(PropertyKey::FillTranslateAnchor, fill.translateAnchor, z, now);
+ applyStyleProperty(PropertyKey::FillImage, fill.image, z, now);
+}
+
+template <>
+void StyleLayer::applyStyleProperties<LineProperties>(const float z, const timestamp now) {
+ properties.set<LineProperties>();
+ LineProperties &line = properties.get<LineProperties>();
+ applyTransitionedStyleProperty(PropertyKey::LineOpacity, line.opacity, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineColor, line.color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineTranslateX, line.translate[0], z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineTranslateY, line.translate[1], z, now);
+ applyStyleProperty(PropertyKey::LineTranslateAnchor, line.translateAnchor, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineWidth, line.width, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineGapWidth, line.gap_width, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineBlur, line.blur, z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineDashLand, line.dash_array[0], z, now);
+ applyTransitionedStyleProperty(PropertyKey::LineDashGap, line.dash_array[1], z, now);
+ applyStyleProperty(PropertyKey::LineImage, line.image, z, now);
+}
+
+template <>
+void StyleLayer::applyStyleProperties<SymbolProperties>(const float z, const timestamp now) {
+ properties.set<SymbolProperties>();
+ SymbolProperties &symbol = properties.get<SymbolProperties>();
+ applyTransitionedStyleProperty(PropertyKey::IconOpacity, symbol.icon.opacity, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconRotate, symbol.icon.rotate, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconSize, symbol.icon.size, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconColor, symbol.icon.color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconHaloColor, symbol.icon.halo_color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconHaloWidth, symbol.icon.halo_width, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconHaloBlur, symbol.icon.halo_blur, z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconTranslateX, symbol.icon.translate[0], z, now);
+ applyTransitionedStyleProperty(PropertyKey::IconTranslateY, symbol.icon.translate[1], z, now);
+ applyStyleProperty(PropertyKey::IconTranslateAnchor, symbol.icon.translate_anchor, z, now);
+
+ applyTransitionedStyleProperty(PropertyKey::TextOpacity, symbol.text.opacity, z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextSize, symbol.text.size, z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextColor, symbol.text.color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextHaloColor, symbol.text.halo_color, z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextHaloWidth, symbol.text.halo_width, z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextHaloBlur, symbol.text.halo_blur, z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextTranslateX, symbol.text.translate[0], z, now);
+ applyTransitionedStyleProperty(PropertyKey::TextTranslateY, symbol.text.translate[1], z, now);
+ applyStyleProperty(PropertyKey::TextTranslateAnchor, symbol.text.translate_anchor, z, now);
+}
+
+template <>
+void StyleLayer::applyStyleProperties<RasterProperties>(const float z, const timestamp now) {
+ properties.set<RasterProperties>();
+ RasterProperties &raster = properties.get<RasterProperties>();
+ applyTransitionedStyleProperty(PropertyKey::RasterOpacity, raster.opacity, z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterHueRotate, raster.hue_rotate, z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterBrightnessLow, raster.brightness[0], z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterBrightnessHigh, raster.brightness[1], z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterSaturation, raster.saturation, z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterContrast, raster.contrast, z, now);
+ applyTransitionedStyleProperty(PropertyKey::RasterFade, raster.fade, z, now);
+}
+
+template <>
+void StyleLayer::applyStyleProperties<BackgroundProperties>(const float z, const timestamp now) {
+ properties.set<BackgroundProperties>();
+ BackgroundProperties &background = properties.get<BackgroundProperties>();
+ applyTransitionedStyleProperty(PropertyKey::BackgroundOpacity, background.opacity, z, now);
+ applyTransitionedStyleProperty(PropertyKey::BackgroundColor, background.color, z, now);
+ applyStyleProperty(PropertyKey::BackgroundImage, background.image, z, now);
+}
+
+void StyleLayer::updateProperties(float z, const timestamp now) {
+ if (layers) {
+ layers->updateProperties(z, now);
+ }
+
+ cleanupAppliedStyleProperties(now);
+
+ switch (type) {
+ case StyleLayerType::Fill: applyStyleProperties<FillProperties>(z, now); break;
+ case StyleLayerType::Line: applyStyleProperties<LineProperties>(z, now); break;
+ case StyleLayerType::Symbol: applyStyleProperties<SymbolProperties>(z, now); break;
+ case StyleLayerType::Raster: applyStyleProperties<RasterProperties>(z, now); break;
+ case StyleLayerType::Background: applyStyleProperties<BackgroundProperties>(z, now); break;
+ default: properties.set<std::false_type>(); break;
+ }
+}
+
+bool StyleLayer::hasTransitions() const {
+ for (const std::pair<PropertyKey, AppliedClassProperties> &pair : appliedStyle) {
+ if (pair.second.hasTransitions()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+
+void StyleLayer::cleanupAppliedStyleProperties(timestamp now) {
+ auto it = appliedStyle.begin();
+ const auto end = appliedStyle.end();
+ while (it != end) {
+ AppliedClassProperties &applied_properties = it->second;
+ applied_properties.cleanup(now);
+
+ // If the current properties object is empty, remove it from the map entirely.
+ if (applied_properties.empty()) {
+ appliedStyle.erase(it++);
+ } else {
+ ++it;
+ }
+ }
+}
+
+}
diff --git a/src/mbgl/style/style_layer.hpp b/src/mbgl/style/style_layer.hpp
new file mode 100644
index 0000000000..641dc1e71c
--- /dev/null
+++ b/src/mbgl/style/style_layer.hpp
@@ -0,0 +1,89 @@
+#ifndef MBGL_STYLE_STYLE_LAYER
+#define MBGL_STYLE_STYLE_LAYER
+
+#include <mbgl/style/class_dictionary.hpp>
+#include <mbgl/style/class_properties.hpp>
+#include <mbgl/style/style_properties.hpp>
+#include <mbgl/style/applied_class_properties.hpp>
+
+#include <mbgl/util/ptr.hpp>
+
+#include <vector>
+#include <string>
+#include <map>
+#include <set>
+
+namespace mbgl {
+
+class StyleBucket;
+class StyleLayerGroup;
+
+class StyleLayer {
+public:
+ StyleLayer(const std::string &id, std::map<ClassID, ClassProperties> &&styles);
+
+ template <typename T> const T &getProperties() {
+ if (properties.is<T>()) {
+ return properties.get<T>();
+ } else {
+ return defaultStyleProperties<T>();
+ }
+ }
+
+ // Determines whether this layer is the background layer.
+ bool isBackground() const;
+
+ // Updates the StyleProperties information in this layer by evaluating all
+ // pending transitions and applied classes in order.
+ void updateProperties(float z, timestamp now);
+
+ // Sets the list of classes and creates transitions to the currently applied values.
+ void setClasses(const std::vector<std::string> &class_names, timestamp now,
+ const PropertyTransition &defaultTransition);
+
+ bool hasTransitions() const;
+
+private:
+ // Applies all properties from a class, if they haven't been applied already.
+ void applyClassProperties(ClassID class_id, std::set<PropertyKey> &already_applied,
+ timestamp now, const PropertyTransition &defaultTransition);
+
+ // Sets the properties of this object by evaluating all pending transitions and
+ // aplied classes in order.
+ template <typename T> void applyStyleProperties(float z, timestamp now);
+ template <typename T> void applyStyleProperty(PropertyKey key, T &, float z, timestamp now);
+ template <typename T> void applyTransitionedStyleProperty(PropertyKey key, T &, float z, timestamp now);
+
+ // Removes all expired style transitions.
+ void cleanupAppliedStyleProperties(timestamp now);
+
+public:
+ // The name of this layer.
+ const std::string id;
+
+ StyleLayerType type = StyleLayerType::Unknown;
+
+ // Bucket information, telling the renderer how to generate the geometries
+ // for this layer (feature property filters, tessellation instructions, ...).
+ util::ptr<StyleBucket> bucket;
+
+ // Contains all style classes that can be applied to this layer.
+ const std::map<ClassID, ClassProperties> styles;
+
+private:
+ // For every property, stores a list of applied property values, with
+ // optional transition times.
+ std::map<PropertyKey, AppliedClassProperties> appliedStyle;
+
+public:
+ // Stores the evaluated, and cascaded styling information, specific to this
+ // layer's type.
+ StyleProperties properties;
+
+ // Child layer array (if this layer has child layers).
+ util::ptr<StyleLayerGroup> layers;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/style_layer_group.cpp b/src/mbgl/style/style_layer_group.cpp
new file mode 100644
index 0000000000..0ca0fa0cce
--- /dev/null
+++ b/src/mbgl/style/style_layer_group.cpp
@@ -0,0 +1,34 @@
+#include <mbgl/style/style_layer_group.hpp>
+
+namespace mbgl {
+
+void StyleLayerGroup::setClasses(const std::vector<std::string> &class_names, timestamp now,
+ const PropertyTransition &defaultTransition) {
+ for (const util::ptr<StyleLayer> &layer : layers) {
+ if (layer) {
+ layer->setClasses(class_names, now, defaultTransition);
+ }
+ }
+}
+
+void StyleLayerGroup::updateProperties(float z, timestamp t) {
+ for (const util::ptr<StyleLayer> &layer: layers) {
+ if (layer) {
+ layer->updateProperties(z, t);
+ }
+ }
+}
+
+bool StyleLayerGroup::hasTransitions() const {
+ for (const util::ptr<const StyleLayer> &layer: layers) {
+ if (layer) {
+ if (layer->hasTransitions()) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+
+}
diff --git a/src/mbgl/style/style_layer_group.hpp b/src/mbgl/style/style_layer_group.hpp
new file mode 100644
index 0000000000..1af6e23bd7
--- /dev/null
+++ b/src/mbgl/style/style_layer_group.hpp
@@ -0,0 +1,23 @@
+#ifndef MBGL_STYLE_STYLE_LAYER_GROUP
+#define MBGL_STYLE_STYLE_LAYER_GROUP
+
+#include <mbgl/style/style_layer.hpp>
+
+#include <vector>
+
+namespace mbgl {
+
+class StyleLayerGroup {
+public:
+ void setClasses(const std::vector<std::string> &class_names, timestamp now,
+ const PropertyTransition &defaultTransition);
+ void updateProperties(float z, timestamp t);
+
+ bool hasTransitions() const;
+public:
+ std::vector<util::ptr<StyleLayer>> layers;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp
new file mode 100644
index 0000000000..2dec648aff
--- /dev/null
+++ b/src/mbgl/style/style_parser.cpp
@@ -0,0 +1,845 @@
+#include <mbgl/style/style_source.hpp>
+#include <mbgl/style/style_parser.hpp>
+#include <mbgl/style/style_layer_group.hpp>
+#include <mbgl/util/constants.hpp>
+#include <mbgl/util/std.hpp>
+#include <mbgl/platform/log.hpp>
+#include <csscolorparser/csscolorparser.hpp>
+
+#include <algorithm>
+
+namespace mbgl {
+
+using JSVal = const rapidjson::Value&;
+
+StyleParser::StyleParser() {
+}
+
+void StyleParser::parse(JSVal document) {
+ if (document.HasMember("constants")) {
+ parseConstants(document["constants"]);
+ }
+
+ if (document.HasMember("sources")) {
+ parseSources(document["sources"]);
+ }
+
+ if (document.HasMember("layers")) {
+ root = createLayers(document["layers"]);
+ parseLayers();
+ }
+
+ if (document.HasMember("sprite")) {
+ parseSprite(document["sprite"]);
+ }
+
+ if (document.HasMember("glyphs")) {
+ parseGlyphURL(document["glyphs"]);
+ }
+}
+
+void StyleParser::parseConstants(JSVal value) {
+ if (value.IsObject()) {
+ rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
+ for (; itr != value.MemberEnd(); ++itr) {
+ std::string name { itr->name.GetString(), itr->name.GetStringLength() };
+ // Discard constants that don't start with an @ sign.
+ if (name.length() && name[0] == '@') {
+ constants.emplace(std::move(name), &itr->value);
+ }
+ }
+ } else {
+ Log::Warning(Event::ParseStyle, "constants must be an object");
+ }
+}
+
+JSVal StyleParser::replaceConstant(JSVal value) {
+ if (value.IsString()) {
+ auto it = constants.find({ value.GetString(), value.GetStringLength() });
+ if (it != constants.end()) {
+ return *it->second;
+ }
+ }
+
+ return value;
+}
+
+#pragma mark - Parse Render Properties
+
+template<> bool StyleParser::parseRenderProperty(JSVal value, bool &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsBool()) {
+ target = property.GetBool();
+ return true;
+ } else {
+ fprintf(stderr, "[WARNING] '%s' must be a boolean\n", name);
+ }
+ }
+ return false;
+}
+
+
+template<> bool StyleParser::parseRenderProperty(JSVal value, std::string &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsString()) {
+ target = { property.GetString(), property.GetStringLength() };
+ return true;
+ } else {
+ Log::Warning(Event::ParseStyle, "'%s' must be a string", name);
+ }
+ }
+ return false;
+}
+
+template<> bool StyleParser::parseRenderProperty(JSVal value, float &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsNumber()) {
+ target = property.GetDouble();
+ return true;
+ } else {
+ Log::Warning(Event::ParseStyle, "'%s' must be a number", name);
+ }
+ }
+ return false;
+}
+
+template<> bool StyleParser::parseRenderProperty(JSVal value, uint16_t &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsUint()) {
+ unsigned int int_value = property.GetUint();
+ if (int_value > std::numeric_limits<uint16_t>::max()) {
+ Log::Warning(Event::ParseStyle, "values for %s that are larger than %d are not supported", name, std::numeric_limits<uint16_t>::max());
+ return false;
+ }
+
+ target = int_value;
+ return true;
+ } else {
+ Log::Warning(Event::ParseStyle, "%s must be an unsigned integer", name);
+ }
+ }
+ return false;
+}
+
+template<> bool StyleParser::parseRenderProperty(JSVal value, int32_t &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsInt()) {
+ target = property.GetInt();
+ return true;
+ } else {
+ Log::Warning(Event::ParseStyle, "%s must be an integer", name);
+ }
+ }
+ return false;
+}
+
+template<> bool StyleParser::parseRenderProperty(JSVal value, vec2<float> &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsArray()) {
+ if (property.Size() >= 2) {
+ target.x = property[(rapidjson::SizeType)0].GetDouble();
+ target.y = property[(rapidjson::SizeType)1].GetDouble();
+ return true;
+ } else {
+ Log::Warning(Event::ParseStyle, "%s must have at least two members", name);
+ }
+ } else {
+ Log::Warning(Event::ParseStyle, "%s must be an array of numbers", name);
+ }
+ }
+ return false;
+}
+
+template<typename Parser, typename T>
+bool StyleParser::parseRenderProperty(JSVal value, T &target, const char *name) {
+ if (value.HasMember(name)) {
+ JSVal property = replaceConstant(value[name]);
+ if (property.IsString()) {
+ target = Parser({ property.GetString(), property.GetStringLength() });
+ return true;
+ } else {
+ Log::Warning(Event::ParseStyle, "%s must have one of the enum values", name);
+ }
+ }
+ return false;
+}
+
+
+#pragma mark - Parse Sources
+
+void StyleParser::parseSources(JSVal value) {
+ if (value.IsObject()) {
+ rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
+ for (; itr != value.MemberEnd(); ++itr) {
+ std::string name { itr->name.GetString(), itr->name.GetStringLength() };
+ SourceInfo& info = sources.emplace(name, std::make_shared<StyleSource>()).first->second->info;
+
+ parseRenderProperty<SourceTypeClass>(itr->value, info.type, "type");
+ parseRenderProperty(itr->value, info.url, "url");
+ parseRenderProperty(itr->value, info.tile_size, "tileSize");
+ info.parseTileJSONProperties(itr->value);
+ }
+ } else {
+ Log::Warning(Event::ParseStyle, "sources must be an object");
+ }
+}
+
+#pragma mark - Parse Style Properties
+
+Color parseColor(JSVal value) {
+ if (!value.IsString()) {
+ Log::Warning(Event::ParseStyle, "color value must be a string");
+ return Color{{ 0, 0, 0, 0 }};
+ }
+
+ CSSColorParser::Color css_color = CSSColorParser::parse({ value.GetString(), value.GetStringLength() });
+
+ // Premultiply the color.
+ const float factor = css_color.a / 255;
+
+ return Color{{(float)css_color.r * factor,
+ (float)css_color.g * factor,
+ (float)css_color.b * factor,
+ css_color.a}};
+}
+
+template <>
+bool StyleParser::parseFunctionArgument(JSVal value) {
+ JSVal rvalue = replaceConstant(value);
+ if (rvalue.IsBool()) {
+ return rvalue.GetBool();
+ } else if (rvalue.IsNumber()) {
+ return rvalue.GetDouble();
+ } else {
+ Log::Warning(Event::ParseStyle, "function argument must be a boolean or numeric value");
+ return false;
+ }
+}
+
+template <>
+float StyleParser::parseFunctionArgument(JSVal value) {
+ JSVal rvalue = replaceConstant(value);
+ if (rvalue.IsNumber()) {
+ return rvalue.GetDouble();
+ } else {
+ Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
+ return 0.0f;
+ }
+}
+
+template <>
+Color StyleParser::parseFunctionArgument(JSVal value) {
+ JSVal rvalue = replaceConstant(value);
+ return parseColor(rvalue);
+}
+
+template <typename T> inline float defaultBaseValue() { return 1.75; }
+template <> inline float defaultBaseValue<Color>() { return 1.0; }
+
+template <typename T>
+std::tuple<bool, Function<T>> StyleParser::parseFunction(JSVal value) {
+ if (!value.HasMember("stops")) {
+ Log::Warning(Event::ParseStyle, "function must specify a function type");
+ return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
+ }
+
+ float base = defaultBaseValue<T>();
+
+ if (value.HasMember("base")) {
+ JSVal value_base = value["base"];
+ if (value_base.IsNumber()) {
+ base = value_base.GetDouble();
+ } else {
+ Log::Warning(Event::ParseStyle, "base must be numeric");
+ }
+ }
+
+ JSVal value_stops = value["stops"];
+ if (!value_stops.IsArray()) {
+ Log::Warning(Event::ParseStyle, "stops function must specify a stops array");
+ return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
+ }
+
+ std::vector<std::pair<float, T>> stops;
+ for (rapidjson::SizeType i = 0; i < value_stops.Size(); ++i) {
+ JSVal stop = value_stops[i];
+ if (stop.IsArray()) {
+ if (stop.Size() != 2) {
+ Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification");
+ return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
+ }
+
+ JSVal z = stop[rapidjson::SizeType(0)];
+ if (!z.IsNumber()) {
+ Log::Warning(Event::ParseStyle, "zoom level in stop must be a number");
+ return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
+ }
+
+ stops.emplace_back(z.GetDouble(), parseFunctionArgument<T>(stop[rapidjson::SizeType(1)]));
+ } else {
+ Log::Warning(Event::ParseStyle, "function argument must be a numeric value");
+ return std::tuple<bool, Function<T>> { false, ConstantFunction<T>(T()) };
+ }
+ }
+
+ return std::tuple<bool, Function<T>> { true, StopsFunction<T>(stops, base) };
+}
+
+
+template <typename T>
+bool StyleParser::parseFunction(PropertyKey key, ClassProperties &klass, JSVal value) {
+ bool parsed;
+ Function<T> function;
+ std::tie(parsed, function) = parseFunction<T>(value);
+ if (parsed) {
+ klass.set(key, function);
+ }
+ return parsed;
+}
+
+template <typename T>
+bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass) {
+ bool parsed;
+ T result;
+ std::tie(parsed, result) = parseProperty<T>(value, property_name);
+ if (parsed) {
+ klass.set(key, result);
+ }
+ return parsed;
+}
+
+template <typename T>
+bool StyleParser::setProperty(JSVal value, const char *property_name, T &target) {
+ bool parsed;
+ T result;
+ std::tie(parsed, result) = parseProperty<T>(value, property_name);
+ if (parsed) {
+ target = std::move(result);
+ }
+ return parsed;
+}
+
+
+template<typename T>
+bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) {
+ if (!value.HasMember(property_name)) {
+ return false;
+ } else {
+ return setProperty<T>(replaceConstant(value[property_name]), property_name, key, klass);
+ }
+}
+
+template <typename T>
+bool StyleParser::parseOptionalProperty(const char *property_name, T &target, JSVal value) {
+ if (!value.HasMember(property_name)) {
+ return false;
+ } else {
+ return setProperty<T>(replaceConstant(value[property_name]), property_name, target);
+ }
+}
+
+template<> std::tuple<bool, std::string> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ if (!value.IsString()) {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
+ return std::tuple<bool, std::string> { false, std::string() };
+ }
+
+ return std::tuple<bool, std::string> { true, { value.GetString(), value.GetStringLength() } };
+}
+
+template<> std::tuple<bool, TranslateAnchorType> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ if (!value.IsString()) {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
+ return std::tuple<bool, TranslateAnchorType> { false, TranslateAnchorType::Map };
+ }
+
+ return std::tuple<bool, TranslateAnchorType> { true, TranslateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
+}
+
+template<> std::tuple<bool, RotateAnchorType> StyleParser::parseProperty<RotateAnchorType>(JSVal value, const char *property_name) {
+ if (!value.IsString()) {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name);
+ return std::tuple<bool, RotateAnchorType> { false, RotateAnchorType::Map };
+ }
+
+ return std::tuple<bool, RotateAnchorType> { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) };
+}
+
+template<> std::tuple<bool, PropertyTransition> StyleParser::parseProperty(JSVal value, const char */*property_name*/) {
+ PropertyTransition transition;
+ if (value.IsObject()) {
+ if (value.HasMember("duration") && value["duration"].IsNumber()) {
+ transition.duration = value["duration"].GetUint();
+ }
+ if (value.HasMember("delay") && value["delay"].IsNumber()) {
+ transition.delay = value["delay"].GetUint();
+ }
+ }
+
+ if (transition.duration == 0 && transition.delay == 0) {
+ return std::tuple<bool, PropertyTransition> { false, std::move(transition) };
+ }
+
+ return std::tuple<bool, PropertyTransition> { true, std::move(transition) };
+}
+
+template<> std::tuple<bool, Function<bool>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ if (value.IsObject()) {
+ return parseFunction<bool>(value);
+ } else if (value.IsNumber()) {
+ return std::tuple<bool, Function<bool>> { true, ConstantFunction<bool>(value.GetDouble()) };
+ } else if (value.IsBool()) {
+ return std::tuple<bool, Function<bool>> { true, ConstantFunction<bool>(value.GetBool()) };
+ } else {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be convertible to boolean, or a boolean function", property_name);
+ return std::tuple<bool, Function<bool>> { false, ConstantFunction<bool>(false) };
+ }
+}
+
+template<> std::tuple<bool, Function<float>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ if (value.IsObject()) {
+ return parseFunction<float>(value);
+ } else if (value.IsNumber()) {
+ return std::tuple<bool, Function<float>> { true, ConstantFunction<float>(value.GetDouble()) };
+ } else if (value.IsBool()) {
+ return std::tuple<bool, Function<float>> { true, ConstantFunction<float>(value.GetBool()) };
+ } else {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name);
+ return std::tuple<bool, Function<float>> { false, ConstantFunction<float>(0) };
+ }
+}
+
+template<> std::tuple<bool, Function<Color>> StyleParser::parseProperty(JSVal value, const char *property_name) {
+ if (value.IsObject()) {
+ return parseFunction<Color>(value);
+ } else if (value.IsString()) {
+ return std::tuple<bool, Function<Color>> { true, ConstantFunction<Color>(parseColor(value)) };
+ } else {
+ Log::Warning(Event::ParseStyle, "value of '%s' must be a color, or a color function", property_name);
+ return std::tuple<bool, Function<Color>> { false, ConstantFunction<Color>(Color {{ 0, 0, 0, 0 }}) };
+ }
+}
+
+template <typename T>
+bool StyleParser::parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value) {
+ if (value.HasMember(property_name)) {
+ JSVal rvalue = replaceConstant(value[property_name]);
+ if (!rvalue.IsArray()) {
+ Log::Warning(Event::ParseStyle, "array value must be an array");
+ }
+
+ if (rvalue.Size() != keys.size()) {
+ Log::Warning(Event::ParseStyle, "array value has unexpected number of elements");
+ }
+
+ for (uint16_t i = 0; i < keys.size(); i++) {
+ setProperty<T>(rvalue[(rapidjson::SizeType)i], property_name, keys[i], klass);
+ }
+ }
+ return true;
+}
+
+#pragma mark - Parse Layers
+
+std::unique_ptr<StyleLayerGroup> StyleParser::createLayers(JSVal value) {
+ if (value.IsArray()) {
+ std::unique_ptr<StyleLayerGroup> group = util::make_unique<StyleLayerGroup>();
+ for (rapidjson::SizeType i = 0; i < value.Size(); ++i) {
+ util::ptr<StyleLayer> layer = createLayer(value[i]);
+ if (layer) {
+ group->layers.emplace_back(layer);
+ }
+ }
+ return group;
+ } else {
+ Log::Warning(Event::ParseStyle, "layers must be an array");
+ return nullptr;
+ }
+}
+
+util::ptr<StyleLayer> StyleParser::createLayer(JSVal value) {
+ if (value.IsObject()) {
+ if (!value.HasMember("id")) {
+ Log::Warning(Event::ParseStyle, "layer must have an id");
+ return nullptr;
+ }
+
+ JSVal id = value["id"];
+ if (!id.IsString()) {
+ Log::Warning(Event::ParseStyle, "layer id must be a string");
+ return nullptr;
+ }
+
+ const std::string layer_id = { id.GetString(), id.GetStringLength() };
+
+ if (layers.find(layer_id) != layers.end()) {
+ Log::Warning(Event::ParseStyle, "duplicate layer id %s", layer_id.c_str());
+ return nullptr;
+ }
+
+ // Parse paints already, as they can't be inherited anyway.
+ std::map<ClassID, ClassProperties> paints;
+ parsePaints(value, paints);
+
+ util::ptr<StyleLayer> layer = std::make_shared<StyleLayer>(
+ layer_id, std::move(paints));
+
+ if (value.HasMember("layers")) {
+ layer->layers = createLayers(value["layers"]);
+ }
+
+ // Store the layer ID so we can reference it later.
+ layers.emplace(layer_id, std::pair<JSVal, util::ptr<StyleLayer>> { value, layer });
+
+ return layer;
+ } else {
+ Log::Warning(Event::ParseStyle, "layer must be an object");
+ return nullptr;
+ }
+}
+
+void StyleParser::parseLayers() {
+ for (std::pair<const std::string, std::pair<JSVal, util::ptr<StyleLayer>>> &pair : layers) {
+ parseLayer(pair.second);
+ }
+}
+
+void StyleParser::parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair) {
+ JSVal value = pair.first;
+ util::ptr<StyleLayer> &layer = pair.second;
+
+ if (value.HasMember("type")) {
+ JSVal type = value["type"];
+ if (!type.IsString()) {
+ Log::Warning(Event::ParseStyle, "layer type of '%s' must be a string", layer->id.c_str());
+ } else {
+ layer->type = StyleLayerTypeClass(std::string { type.GetString(), type.GetStringLength() });
+ }
+ }
+
+ if (layer->bucket || (layer->layers && layer->type != StyleLayerType::Raster)) {
+ // Skip parsing this again. We already have a valid layer definition.
+ return;
+ }
+
+ // Make sure we have not previously attempted to parse this layer.
+ if (std::find(stack.begin(), stack.end(), layer.get()) != stack.end()) {
+ Log::Warning(Event::ParseStyle, "layer reference of '%s' is circular", layer->id.c_str());
+ return;
+ }
+
+ if (value.HasMember("ref")) {
+ // This layer is referencing another layer. Inherit the bucket from that layer, if we
+ // already parsed it.
+ parseReference(replaceConstant(value["ref"]), layer);
+ } else {
+ // Otherwise, parse the source/source-layer/filter/render keys to form the bucket.
+ parseBucket(value, layer);
+ }
+}
+
+#pragma mark - Parse Styles
+
+void StyleParser::parsePaints(JSVal value, std::map<ClassID, ClassProperties> &paints) {
+ rapidjson::Value::ConstMemberIterator itr = value.MemberBegin();
+ for (; itr != value.MemberEnd(); ++itr) {
+ const std::string name { itr->name.GetString(), itr->name.GetStringLength() };
+
+ if (name == "paint") {
+ parsePaint(replaceConstant(itr->value), paints[ClassID::Default]);
+ } else if (name.compare(0, 6, "paint.") == 0 && name.length() > 6) {
+ const ClassID class_id = ClassDictionary::Get().lookup(name.substr(6));
+ parsePaint(replaceConstant(itr->value), paints[class_id]);
+ }
+ }
+}
+
+void StyleParser::parsePaint(JSVal value, ClassProperties &klass) {
+ using Key = PropertyKey;
+
+ parseOptionalProperty<Function<bool>>("fill-antialias", Key::FillAntialias, klass, value);
+ parseOptionalProperty<Function<float>>("fill-opacity", Key::FillOpacity, klass, value);
+ parseOptionalProperty<PropertyTransition>("fill-opacity-transition", Key::FillOpacity, klass, value);
+ parseOptionalProperty<Function<Color>>("fill-color", Key::FillColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("fill-color-transition", Key::FillColor, klass, value);
+ parseOptionalProperty<Function<Color>>("fill-outline-color", Key::FillOutlineColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("fill-outline-color-transition", Key::FillOutlineColor, klass, value);
+ parseOptionalProperty<Function<float>>("fill-translate", { Key::FillTranslateX, Key::FillTranslateY }, klass, value);
+ parseOptionalProperty<PropertyTransition>("fill-translate-transition", Key::FillTranslate, klass, value);
+ parseOptionalProperty<TranslateAnchorType>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value);
+ parseOptionalProperty<std::string>("fill-image", Key::FillImage, klass, value);
+
+ parseOptionalProperty<Function<float>>("line-opacity", Key::LineOpacity, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-opacity-transition", Key::LineOpacity, klass, value);
+ parseOptionalProperty<Function<Color>>("line-color", Key::LineColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-color-transition", Key::LineColor, klass, value);
+ parseOptionalProperty<Function<float>>("line-translate", { Key::LineTranslateX, Key::LineTranslateY }, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-translate-transition", Key::LineTranslate, klass, value);
+ parseOptionalProperty<TranslateAnchorType>("line-translate-anchor", Key::LineTranslateAnchor, klass, value);
+ parseOptionalProperty<Function<float>>("line-width", Key::LineWidth, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-width-transition", Key::LineWidth, klass, value);
+ parseOptionalProperty<Function<float>>("line-gap-width", Key::LineGapWidth, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-gap-width-transition", Key::LineGapWidth, klass, value);
+ parseOptionalProperty<Function<float>>("line-blur", Key::LineBlur, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-blur-transition", Key::LineBlur, klass, value);
+ parseOptionalProperty<Function<float>>("line-dasharray", { Key::LineDashLand, Key::LineDashGap }, klass, value);
+ parseOptionalProperty<PropertyTransition>("line-dasharray-transition", Key::LineDashArray, klass, value);
+ parseOptionalProperty<std::string>("line-image", Key::LineImage, klass, value);
+
+ parseOptionalProperty<Function<float>>("icon-opacity", Key::IconOpacity, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-opacity-transition", Key::IconOpacity, klass, value);
+ parseOptionalProperty<Function<float>>("icon-rotate", Key::IconRotate, klass, value);
+ parseOptionalProperty<Function<float>>("icon-size", Key::IconSize, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-size-transition", Key::IconSize, klass, value);
+ parseOptionalProperty<Function<Color>>("icon-color", Key::IconColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-color-transition", Key::IconColor, klass, value);
+ parseOptionalProperty<Function<Color>>("icon-halo-color", Key::IconHaloColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-halo-color-transition", Key::IconHaloColor, klass, value);
+ parseOptionalProperty<Function<float>>("icon-halo-width", Key::IconHaloWidth, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-halo-width-transition", Key::IconHaloWidth, klass, value);
+ parseOptionalProperty<Function<float>>("icon-halo-blur", Key::IconHaloBlur, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-halo-blur-transition", Key::IconHaloBlur, klass, value);
+ parseOptionalProperty<Function<float>>("icon-translate", { Key::IconTranslateX, Key::IconTranslateY }, klass, value);
+ parseOptionalProperty<PropertyTransition>("icon-translate-transition", Key::IconTranslate, klass, value);
+ parseOptionalProperty<TranslateAnchorType>("icon-translate-anchor", Key::IconTranslateAnchor, klass, value);
+
+ parseOptionalProperty<Function<float>>("text-opacity", Key::TextOpacity, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-opacity-transition", Key::TextOpacity, klass, value);
+ parseOptionalProperty<Function<float>>("text-size", Key::TextSize, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-size-transition", Key::TextSize, klass, value);
+ parseOptionalProperty<Function<Color>>("text-color", Key::TextColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-color-transition", Key::TextColor, klass, value);
+ parseOptionalProperty<Function<Color>>("text-halo-color", Key::TextHaloColor, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-halo-color-transition", Key::TextHaloColor, klass, value);
+ parseOptionalProperty<Function<float>>("text-halo-width", Key::TextHaloWidth, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-halo-width-transition", Key::TextHaloWidth, klass, value);
+ parseOptionalProperty<Function<float>>("text-halo-blur", Key::TextHaloBlur, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-halo-blur-transition", Key::TextHaloBlur, klass, value);
+ parseOptionalProperty<Function<float>>("text-translate", { Key::TextTranslateX, Key::TextTranslateY }, klass, value);
+ parseOptionalProperty<PropertyTransition>("text-translate-transition", Key::TextTranslate, klass, value);
+ parseOptionalProperty<TranslateAnchorType>("text-translate-anchor", Key::TextTranslateAnchor, klass, value);
+
+ parseOptionalProperty<Function<float>>("raster-opacity", Key::RasterOpacity, klass, value);
+ parseOptionalProperty<PropertyTransition>("raster-opacity-transition", Key::RasterOpacity, klass, value);
+ parseOptionalProperty<Function<float>>("raster-hue-rotate", Key::RasterHueRotate, klass, value);
+ parseOptionalProperty<PropertyTransition>("raster-hue-rotate-transition", Key::RasterHueRotate, klass, value);
+ parseOptionalProperty<Function<float>>("raster-brightness", { Key::RasterBrightnessLow, Key::RasterBrightnessHigh }, klass, value);
+ parseOptionalProperty<PropertyTransition>("raster-brightness-transition", Key::RasterBrightness, klass, value);
+ parseOptionalProperty<Function<float>>("raster-saturation", Key::RasterSaturation, klass, value);
+ parseOptionalProperty<PropertyTransition>("raster-saturation-transition", Key::RasterSaturation, klass, value);
+ parseOptionalProperty<Function<float>>("raster-contrast", Key::RasterContrast, klass, value);
+ parseOptionalProperty<PropertyTransition>("raster-contrast-transition", Key::RasterContrast, klass, value);
+ parseOptionalProperty<Function<float>>("raster-fade-duration", Key::RasterFade, klass, value);
+ parseOptionalProperty<PropertyTransition>("raster-fade-duration-transition", Key::RasterFade, klass, value);
+
+ parseOptionalProperty<Function<float>>("background-opacity", Key::BackgroundOpacity, klass, value);
+ parseOptionalProperty<Function<Color>>("background-color", Key::BackgroundColor, klass, value);
+ parseOptionalProperty<std::string>("background-image", Key::BackgroundImage, klass, value);
+}
+
+void StyleParser::parseReference(JSVal value, util::ptr<StyleLayer> &layer) {
+ if (!value.IsString()) {
+ Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str());
+ return;
+ }
+ const std::string ref { value.GetString(), value.GetStringLength() };
+ auto it = layers.find(ref);
+ if (it == layers.end()) {
+ Log::Warning(Event::ParseStyle, "layer '%s' references unknown layer %s", layer->id.c_str(), ref.c_str());
+ // We cannot parse this layer further.
+ return;
+ }
+
+ // Recursively parse the referenced layer.
+ stack.push_front(layer.get());
+ parseLayer(it->second);
+ stack.pop_front();
+
+
+ util::ptr<StyleLayer> reference = it->second.second;
+
+ layer->type = reference->type;
+
+ if (reference->layers) {
+ Log::Warning(Event::ParseStyle, "layer '%s' references composite layer", layer->id.c_str());
+ // We cannot parse this layer further.
+ return;
+ } else {
+ layer->bucket = reference->bucket;
+ }
+}
+
+#pragma mark - Parse Bucket
+
+void StyleParser::parseBucket(JSVal value, util::ptr<StyleLayer> &layer) {
+ layer->bucket = std::make_shared<StyleBucket>(layer->type);
+
+ // We name the buckets according to the layer that defined it.
+ layer->bucket->name = layer->id;
+
+ if (value.HasMember("source")) {
+ JSVal value_source = replaceConstant(value["source"]);
+ if (value_source.IsString()) {
+ const std::string source_name = { value_source.GetString(), value_source.GetStringLength() };
+ auto source_it = sources.find(source_name);
+ if (source_it != sources.end()) {
+ layer->bucket->style_source = source_it->second;
+ } else {
+ Log::Warning(Event::ParseStyle, "can't find source '%s' required for layer '%s'", source_name.c_str(), layer->id.c_str());
+ }
+ } else {
+ Log::Warning(Event::ParseStyle, "source of layer '%s' must be a string", layer->id.c_str());
+ }
+ }
+
+ if (value.HasMember("source-layer")) {
+ JSVal value_source_layer = replaceConstant(value["source-layer"]);
+ if (value_source_layer.IsString()) {
+ layer->bucket->source_layer = { value_source_layer.GetString(), value_source_layer.GetStringLength() };
+ } else {
+ Log::Warning(Event::ParseStyle, "source-layer of layer '%s' must be a string", layer->id.c_str());
+ }
+ }
+
+ if (value.HasMember("filter")) {
+ JSVal value_filter = replaceConstant(value["filter"]);
+ layer->bucket->filter = parseFilterExpression(value_filter);
+ }
+
+ if (value.HasMember("layout")) {
+ JSVal value_render = replaceConstant(value["layout"]);
+ parseLayout(value_render, layer);
+ }
+
+ if (value.HasMember("minzoom")) {
+ JSVal min_zoom = value["minzoom"];
+ if (min_zoom.IsNumber()) {
+ layer->bucket->min_zoom = min_zoom.GetDouble();
+ } else {
+ Log::Warning(Event::ParseStyle, "minzoom of layer %s must be numeric", layer->id.c_str());
+ }
+ }
+
+ if (value.HasMember("maxzoom")) {
+ JSVal max_zoom = value["maxzoom"];
+ if (max_zoom.IsNumber()) {
+ layer->bucket->min_zoom = max_zoom.GetDouble();
+ } else {
+ Log::Warning(Event::ParseStyle, "maxzoom of layer %s must be numeric", layer->id.c_str());
+ }
+ }
+}
+
+void StyleParser::parseLayout(JSVal value, util::ptr<StyleLayer> &layer) {
+ if (!value.IsObject()) {
+ Log::Warning(Event::ParseStyle, "layout property of layer '%s' must be an object", layer->id.c_str());
+ return;
+ }
+
+ StyleBucket &bucket = *layer->bucket;
+
+ switch (layer->type) {
+ case StyleLayerType::Fill: {
+ StyleBucketFill &render = bucket.render.get<StyleBucketFill>();
+
+ parseRenderProperty<WindingTypeClass>(value, render.winding, "fill-winding");
+ } break;
+
+ case StyleLayerType::Line: {
+ StyleBucketLine &render = bucket.render.get<StyleBucketLine>();
+
+ parseRenderProperty<CapTypeClass>(value, render.cap, "line-cap");
+ parseRenderProperty<JoinTypeClass>(value, render.join, "line-join");
+ parseRenderProperty(value, render.miter_limit, "line-miter-limit");
+ parseRenderProperty(value, render.round_limit, "line-round-limit");
+ } break;
+
+ case StyleLayerType::Symbol: {
+ StyleBucketSymbol &render = bucket.render.get<StyleBucketSymbol>();
+
+ parseRenderProperty<PlacementTypeClass>(value, render.placement, "symbol-placement");
+ if (render.placement == PlacementType::Line) {
+ // Change the default value in case of line placement.
+ render.text.rotation_alignment = RotationAlignmentType::Map;
+ render.icon.rotation_alignment = RotationAlignmentType::Map;
+ }
+
+ parseRenderProperty(value, render.min_distance, "symbol-min-distance");
+ parseRenderProperty(value, render.avoid_edges, "symbol-avoid-edges");
+
+ parseRenderProperty(value, render.icon.allow_overlap, "icon-allow-overlap");
+ parseRenderProperty(value, render.icon.ignore_placement, "icon-ignore-placement");
+ parseRenderProperty(value, render.icon.optional, "icon-optional");
+ parseRenderProperty<RotationAlignmentTypeClass>(value, render.icon.rotation_alignment, "icon-rotation-alignment");
+ parseRenderProperty(value, render.icon.max_size, "icon-max-size");
+ parseRenderProperty(value, render.icon.image, "icon-image");
+ parseRenderProperty(value, render.icon.rotate, "icon-rotate");
+ parseRenderProperty(value, render.icon.padding, "icon-padding");
+ parseRenderProperty(value, render.icon.keep_upright, "icon-keep-upright");
+ parseRenderProperty(value, render.icon.offset, "icon-offset");
+
+
+ parseRenderProperty<RotationAlignmentTypeClass>(value, render.text.rotation_alignment, "text-rotation-alignment");
+ parseRenderProperty(value, render.text.field, "text-field");
+ parseRenderProperty(value, render.text.font, "text-font");
+ parseRenderProperty(value, render.text.max_size, "text-max-size");
+ if (parseRenderProperty(value, render.text.max_width, "text-max-width")) {
+ render.text.max_width *= 24; // em
+ }
+ if (parseRenderProperty(value, render.text.line_height, "text-line-height")) {
+ render.text.line_height *= 24; // em
+ }
+ if (parseRenderProperty(value, render.text.letter_spacing, "text-letter-spacing")) {
+ render.text.letter_spacing *= 24; // em
+ }
+ parseRenderProperty<TextJustifyTypeClass>(value, render.text.justify, "text-justify");
+ parseRenderProperty<TextAnchorTypeClass>(value, render.text.anchor, "text-anchor");
+ parseRenderProperty(value, render.text.max_angle, "text-max-angle");
+ parseRenderProperty(value, render.text.rotate, "text-rotate");
+ parseRenderProperty(value, render.text.slant, "text-slant");
+ parseRenderProperty(value, render.text.padding, "text-padding");
+ parseRenderProperty(value, render.text.keep_upright, "text-keep-upright");
+ parseRenderProperty<TextTransformTypeClass>(value, render.text.transform, "text-transform");
+ parseRenderProperty(value, render.text.offset, "text-offset");
+ parseRenderProperty(value, render.text.allow_overlap, "text-allow-overlap");
+ parseRenderProperty(value, render.text.ignore_placement, "text-ignore-placement");
+ parseRenderProperty(value, render.text.optional, "text-optional");
+ } break;
+
+ case StyleLayerType::Raster: {
+ StyleBucketRaster &render = bucket.render.get<StyleBucketRaster>();
+
+ parseRenderProperty(value, render.size, "raster-size");
+ parseRenderProperty(value, render.blur, "raster-blur");
+ parseRenderProperty(value, render.buffer, "raster-buffer");
+ if (layer->layers) {
+ render.prerendered = true;
+ }
+ } break;
+
+ default:
+ // There are no render properties for these layer types.
+ break;
+ }
+}
+
+void StyleParser::parseSprite(JSVal value) {
+ if (value.IsString()) {
+ sprite = { value.GetString(), value.GetStringLength() };
+ }
+}
+
+void StyleParser::parseGlyphURL(JSVal value) {
+ if (value.IsString()) {
+ glyph_url = { value.GetString(), value.GetStringLength() };
+ }
+}
+
+
+}
diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp
new file mode 100644
index 0000000000..c37e856034
--- /dev/null
+++ b/src/mbgl/style/style_parser.hpp
@@ -0,0 +1,113 @@
+#ifndef MBGL_STYLE_STYLE_PARSER
+#define MBGL_STYLE_STYLE_PARSER
+
+#include <rapidjson/document.h>
+#include <mbgl/style/style.hpp>
+#include <mbgl/style/style_source.hpp>
+#include <mbgl/style/filter_expression.hpp>
+#include <mbgl/style/class_properties.hpp>
+#include <mbgl/style/style_bucket.hpp>
+
+#include <unordered_map>
+#include <forward_list>
+#include <tuple>
+
+namespace mbgl {
+
+enum class ClassID : uint32_t;
+
+class StyleLayer;
+class StyleLayerGroup;
+
+class StyleParser {
+public:
+ using JSVal = const rapidjson::Value&;
+
+ StyleParser();
+
+ void parse(JSVal document);
+
+ util::ptr<StyleLayerGroup> getLayers() {
+ return root;
+ }
+
+ std::string getSprite() const {
+ return sprite;
+ }
+
+ std::string getGlyphURL() const {
+ return glyph_url;
+ }
+
+private:
+ void parseConstants(JSVal value);
+ JSVal replaceConstant(JSVal value);
+
+ void parseSources(JSVal value);
+
+ std::unique_ptr<StyleLayerGroup> createLayers(JSVal value);
+ util::ptr<StyleLayer> createLayer(JSVal value);
+ void parseLayers();
+ void parseLayer(std::pair<JSVal, util::ptr<StyleLayer>> &pair);
+ void parsePaints(JSVal value, std::map<ClassID, ClassProperties> &paints);
+ void parsePaint(JSVal, ClassProperties &properties);
+ void parseReference(JSVal value, util::ptr<StyleLayer> &layer);
+ void parseBucket(JSVal value, util::ptr<StyleLayer> &layer);
+ void parseLayout(JSVal value, util::ptr<StyleLayer> &layer);
+ void parseSprite(JSVal value);
+ void parseGlyphURL(JSVal value);
+
+ // Parses optional properties into a render bucket.
+ template<typename T>
+ bool parseRenderProperty(JSVal value, T &target, const char *name);
+ template <typename Parser, typename T>
+ bool parseRenderProperty(JSVal value, T &target, const char *name);
+
+ // Parses optional properties into style class properties.
+ template <typename T>
+ bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value);
+ template <typename T>
+ bool parseOptionalProperty(const char *property_name, const std::vector<PropertyKey> &keys, ClassProperties &klass, JSVal value);
+ template <typename T>
+ bool parseOptionalProperty(const char *property_name, T &target, JSVal value);
+ template <typename T>
+ bool setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass);
+ template <typename T>
+ bool setProperty(JSVal value, const char *property_name, T &target);
+
+ template <typename T>
+ std::tuple<bool, T> parseProperty(JSVal value, const char *property_name);
+
+ template <typename T>
+ bool parseFunction(PropertyKey key, ClassProperties &klass, JSVal value);
+ template <typename T>
+ std::tuple<bool, Function<T>> parseFunction(JSVal value);
+ template <typename T>
+ T parseFunctionArgument(JSVal value);
+
+ FilterExpression parseFilter(JSVal);
+
+private:
+ std::unordered_map<std::string, const rapidjson::Value *> constants;
+
+ std::unordered_map<std::string, const util::ptr<StyleSource>> sources;
+
+ // This stores the root layer.
+ util::ptr<StyleLayerGroup> root;
+
+ // This maps ids to Layer objects, with all items being at the root level.
+ std::unordered_map<std::string, std::pair<JSVal, util::ptr<StyleLayer>>> layers;
+
+ // Store a stack of layers we're parsing right now. This is to prevent reference cycles.
+ std::forward_list<StyleLayer *> stack;
+
+ // Base URL of the sprite image.
+ std::string sprite;
+
+ // URL template for glyph PBFs.
+ std::string glyph_url;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/style_properties.cpp b/src/mbgl/style/style_properties.cpp
new file mode 100644
index 0000000000..29730fb85b
--- /dev/null
+++ b/src/mbgl/style/style_properties.cpp
@@ -0,0 +1,11 @@
+#include <mbgl/style/style_properties.hpp>
+
+namespace mbgl {
+
+template<> const FillProperties &defaultStyleProperties() { static const FillProperties p; return p; }
+template<> const LineProperties &defaultStyleProperties() { static const LineProperties p; return p; }
+template<> const SymbolProperties &defaultStyleProperties() { static const SymbolProperties p; return p; }
+template<> const RasterProperties &defaultStyleProperties() { static const RasterProperties p; return p; }
+template<> const BackgroundProperties &defaultStyleProperties() { static const BackgroundProperties p; return p; }
+
+}
diff --git a/src/mbgl/style/style_properties.hpp b/src/mbgl/style/style_properties.hpp
new file mode 100644
index 0000000000..c44b7c34c8
--- /dev/null
+++ b/src/mbgl/style/style_properties.hpp
@@ -0,0 +1,114 @@
+#ifndef MBGL_STYLE_STYLE_PROPERTIES
+#define MBGL_STYLE_STYLE_PROPERTIES
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/function_properties.hpp>
+
+#include <array>
+#include <string>
+#include <type_traits>
+#include <memory>
+
+namespace mbgl {
+
+struct FillProperties {
+ FillProperties() {}
+ bool antialias = true;
+ float opacity = 1.0f;
+ Color fill_color = {{ 0, 0, 0, 1 }};
+ Color stroke_color = {{ 0, 0, 0, -1 }};
+ std::array<float, 2> translate = {{ 0, 0 }};
+ TranslateAnchorType translateAnchor = TranslateAnchorType::Map;
+ std::string image;
+
+ inline bool isVisible() const {
+ return opacity > 0 && (fill_color[3] > 0 || stroke_color[3] > 0);
+ }
+};
+
+struct LineProperties {
+ inline LineProperties() {}
+ float opacity = 1.0f;
+ Color color = {{ 0, 0, 0, 1 }};
+ std::array<float, 2> translate = {{ 0, 0 }};
+ TranslateAnchorType translateAnchor = TranslateAnchorType::Map;
+ float width = 1;
+ float gap_width = 0;
+ float blur = 0;
+ std::array<float, 2> dash_array = {{ 1, -1 }};
+ std::string image;
+
+ inline bool isVisible() const {
+ return opacity > 0 && color[3] > 0 && width > 0;
+ }
+};
+
+struct SymbolProperties {
+ inline SymbolProperties() {}
+
+ struct {
+ float opacity = 1.0f;
+ float rotate = 0.0f;
+ float size = 1.0f;
+ Color color = {{ 0, 0, 0, 1 }};
+ Color halo_color = {{ 0, 0, 0, 0 }};
+ float halo_width = 0.0f;
+ float halo_blur = 0.0f;
+ std::array<float, 2> translate = {{ 0, 0 }};
+ TranslateAnchorType translate_anchor = TranslateAnchorType::Map;
+ } icon;
+
+ struct {
+ float opacity = 1.0f;
+ float size = 16.0f;
+ Color color = {{ 0, 0, 0, 1 }};
+ Color halo_color = {{ 0, 0, 0, 0 }};
+ float halo_width = 0.0f;
+ float halo_blur = 0.0f;
+ std::array<float, 2> translate = {{ 0, 0 }};
+ TranslateAnchorType translate_anchor = TranslateAnchorType::Map;
+ } text;
+
+ inline bool isVisible() const {
+ return (icon.opacity > 0 && (icon.color[3] > 0 || icon.halo_color[3] > 0) && icon.size > 0) ||
+ (text.opacity > 0 && (text.color[3] > 0 || text.halo_color[3] > 0) && text.size > 0);
+ }
+};
+
+struct RasterProperties {
+ inline RasterProperties() {}
+ float opacity = 1.0f;
+ float hue_rotate = 0.0f;
+ std::array<float, 2> brightness = {{ 0, 1 }};
+ float saturation = 0.0f;
+ float contrast = 0.0f;
+ float fade = 0.0f;
+
+ inline bool isVisible() const {
+ return opacity > 0;
+ }
+};
+
+struct BackgroundProperties {
+ inline BackgroundProperties() {}
+ float opacity = 1.0f;
+ Color color = {{ 0, 0, 0, 1 }};
+ std::string image;
+};
+
+typedef mapbox::util::variant<
+ FillProperties,
+ LineProperties,
+ SymbolProperties,
+ RasterProperties,
+ BackgroundProperties,
+ std::false_type
+> StyleProperties;
+
+template <typename T>
+const T &defaultStyleProperties();
+
+}
+
+#endif
diff --git a/src/mbgl/style/style_source.cpp b/src/mbgl/style/style_source.cpp
new file mode 100644
index 0000000000..f46a6fb09b
--- /dev/null
+++ b/src/mbgl/style/style_source.cpp
@@ -0,0 +1,77 @@
+#include <mbgl/style/style_source.hpp>
+#include <mbgl/platform/platform.hpp>
+#include <mbgl/platform/log.hpp>
+
+#include <limits>
+
+namespace mbgl {
+
+void parse(const rapidjson::Value& value, std::vector<std::string>& target, const char *name) {
+ if (!value.HasMember(name))
+ return;
+
+ const rapidjson::Value& property = value[name];
+ if (!property.IsArray())
+ return;
+
+ for (rapidjson::SizeType i = 0; i < property.Size(); i++)
+ if (!property[i].IsString())
+ return;
+
+ for (rapidjson::SizeType i = 0; i < property.Size(); i++)
+ target.emplace_back(std::string(property[i].GetString(), property[i].GetStringLength()));
+}
+
+void parse(const rapidjson::Value& value, std::string& target, const char* name) {
+ if (!value.HasMember(name))
+ return;
+
+ const rapidjson::Value& property = value[name];
+ if (!property.IsString())
+ return;
+
+ target = { property.GetString(), property.GetStringLength() };
+}
+
+void parse(const rapidjson::Value& value, uint16_t& target, const char* name) {
+ if (!value.HasMember(name))
+ return;
+
+ const rapidjson::Value& property = value[name];
+ if (!property.IsUint())
+ return;
+
+ unsigned int uint = property.GetUint();
+ if (uint > std::numeric_limits<uint16_t>::max())
+ return;
+
+ target = uint;
+}
+
+template <size_t N>
+void parse(const rapidjson::Value& value, std::array<float, N>& target, const char* name) {
+ if (!value.HasMember(name))
+ return;
+
+ const rapidjson::Value& property = value[name];
+ if (!property.IsArray() || property.Size() != N)
+ return;
+
+ for (rapidjson::SizeType i = 0; i < property.Size(); i++)
+ if (!property[i].IsNumber())
+ return;
+
+ for (rapidjson::SizeType i = 0; i < property.Size(); i++)
+ target[i] = property[i].GetDouble();
+}
+
+void SourceInfo::parseTileJSONProperties(const rapidjson::Value& value) {
+ parse(value, tiles, "tiles");
+ parse(value, min_zoom, "minzoom");
+ parse(value, max_zoom, "maxzoom");
+ parse(value, attribution, "attribution");
+ parse(value, center, "center");
+ parse(value, bounds, "bounds");
+}
+
+}
diff --git a/src/mbgl/style/style_source.hpp b/src/mbgl/style/style_source.hpp
new file mode 100644
index 0000000000..8c7d028880
--- /dev/null
+++ b/src/mbgl/style/style_source.hpp
@@ -0,0 +1,41 @@
+#ifndef MBGL_STYLE_STYLE_SOURCE
+#define MBGL_STYLE_STYLE_SOURCE
+
+#include <mbgl/style/types.hpp>
+#include <mbgl/util/ptr.hpp>
+#include <mbgl/util/noncopyable.hpp>
+#include <rapidjson/document.h>
+
+#include <vector>
+#include <string>
+
+namespace mbgl {
+
+class Source;
+
+class SourceInfo : private util::noncopyable {
+public:
+ SourceType type = SourceType::Vector;
+ std::string url;
+ std::vector<std::string> tiles;
+ uint16_t tile_size = 512;
+ uint16_t min_zoom = 0;
+ uint16_t max_zoom = 22;
+ std::string attribution;
+ std::array<float, 3> center = {{0, 0, 0}};
+ std::array<float, 4> bounds = {{-180, -90, 180, 90}};
+
+ void parseTileJSONProperties(const rapidjson::Value&);
+};
+
+
+class StyleSource : private util::noncopyable {
+public:
+ SourceInfo info;
+ bool enabled = false;
+ util::ptr<Source> source;
+};
+
+}
+
+#endif
diff --git a/src/mbgl/style/types.cpp b/src/mbgl/style/types.cpp
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/src/mbgl/style/types.cpp
diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp
new file mode 100644
index 0000000000..2f7ca7683d
--- /dev/null
+++ b/src/mbgl/style/types.hpp
@@ -0,0 +1,196 @@
+#ifndef MBGL_STYLE_TYPES
+#define MBGL_STYLE_TYPES
+
+#include <mbgl/util/enum.hpp>
+
+#include <string>
+#include <array>
+
+namespace mbgl {
+
+// Stores a premultiplied color, with all four channels ranging from 0..1
+typedef std::array<float, 4> Color;
+
+// -------------------------------------------------------------------------------------------------
+
+enum class StyleLayerType : uint8_t {
+ Unknown,
+ Fill,
+ Line,
+ Symbol,
+ Raster,
+ Background
+};
+
+MBGL_DEFINE_ENUM_CLASS(StyleLayerTypeClass, StyleLayerType, {
+ { StyleLayerType::Unknown, "unknown" },
+ { StyleLayerType::Fill, "fill" },
+ { StyleLayerType::Line, "line" },
+ { StyleLayerType::Symbol, "symbol" },
+ { StyleLayerType::Raster, "raster" },
+ { StyleLayerType::Background, "background" },
+ { StyleLayerType(-1), "unknown" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class SourceType : uint8_t {
+ Vector,
+ Raster,
+ GeoJSON,
+ Video
+};
+
+MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, {
+ { SourceType::Vector, "vector" },
+ { SourceType::Raster, "raster" },
+ { SourceType::GeoJSON, "geojson" },
+ { SourceType::Video, "video" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class WindingType : bool {
+ EvenOdd,
+ NonZero,
+};
+
+MBGL_DEFINE_ENUM_CLASS(WindingTypeClass, WindingType, {
+ { WindingType::EvenOdd, "even-odd" },
+ { WindingType::NonZero, "non-zero" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class CapType : uint8_t {
+ Round,
+ Butt,
+ Square,
+};
+
+MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, {
+ { CapType::Round, "round" },
+ { CapType::Butt, "butt" },
+ { CapType::Square, "square" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class JoinType : uint8_t {
+ Miter,
+ Bevel,
+ Round
+};
+
+MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, {
+ { JoinType::Miter, "miter" },
+ { JoinType::Bevel, "bevel" },
+ { JoinType::Round, "round" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class TranslateAnchorType : bool {
+ Map,
+ Viewport
+};
+
+MBGL_DEFINE_ENUM_CLASS(TranslateAnchorTypeClass, TranslateAnchorType, {
+ { TranslateAnchorType::Map, "map" },
+ { TranslateAnchorType::Viewport, "viewport" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class RotateAnchorType : bool {
+ Map,
+ Viewport,
+};
+
+MBGL_DEFINE_ENUM_CLASS(RotateAnchorTypeClass, RotateAnchorType, {
+ { RotateAnchorType::Map, "map" },
+ { RotateAnchorType::Viewport, "viewport" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class PlacementType : bool {
+ Point,
+ Line,
+};
+
+MBGL_DEFINE_ENUM_CLASS(PlacementTypeClass, PlacementType, {
+ { PlacementType::Point, "point" },
+ { PlacementType::Line, "line" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class RotationAlignmentType : bool {
+ Map,
+ Viewport,
+};
+
+MBGL_DEFINE_ENUM_CLASS(RotationAlignmentTypeClass, RotationAlignmentType, {
+ { RotationAlignmentType::Map, "map" },
+ { RotationAlignmentType::Viewport, "viewport" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class TextJustifyType : uint8_t {
+ Center,
+ Left,
+ Right
+};
+
+MBGL_DEFINE_ENUM_CLASS(TextJustifyTypeClass, TextJustifyType, {
+ { TextJustifyType::Center, "center" },
+ { TextJustifyType::Left, "left" },
+ { TextJustifyType::Right, "right" },
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class TextAnchorType : uint8_t {
+ Center,
+ Left,
+ Right,
+ Top,
+ Bottom,
+ TopLeft,
+ TopRight,
+ BottomLeft,
+ BottomRight
+};
+
+MBGL_DEFINE_ENUM_CLASS(TextAnchorTypeClass, TextAnchorType, {
+ { TextAnchorType::Center, "center" },
+ { TextAnchorType::Left, "left" },
+ { TextAnchorType::Right, "right" },
+ { TextAnchorType::Top, "top" },
+ { TextAnchorType::Bottom, "bottom" },
+ { TextAnchorType::TopLeft, "top-left" },
+ { TextAnchorType::TopRight, "top-right" },
+ { TextAnchorType::BottomLeft, "bottom-left" },
+ { TextAnchorType::BottomRight, "bottom-right" }
+});
+
+// -------------------------------------------------------------------------------------------------
+
+enum class TextTransformType : uint8_t {
+ None,
+ Uppercase,
+ Lowercase,
+};
+
+MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, {
+ { TextTransformType::None, "none" },
+ { TextTransformType::Uppercase, "uppercase" },
+ { TextTransformType::Lowercase, "lowercase" },
+});
+
+}
+
+#endif
+
diff --git a/src/mbgl/style/value.cpp b/src/mbgl/style/value.cpp
new file mode 100644
index 0000000000..ae51ce3783
--- /dev/null
+++ b/src/mbgl/style/value.cpp
@@ -0,0 +1,60 @@
+#include <mbgl/style/value.hpp>
+#include <mbgl/util/string.hpp>
+
+mbgl::Value mbgl::parseValue(pbf data) {
+ while (data.next())
+ {
+ switch (data.tag)
+ {
+ case 1: // string_value
+ return data.string();
+ case 2: // float_value
+ return static_cast<double>(data.float32());
+ case 3: // double_value
+ return data.float64();
+ case 4: // int_value
+ return data.varint<int64_t>();
+ case 5: // uint_value
+ return data.varint<uint64_t>();
+ case 6: // sint_value
+ return data.svarint<int64_t>();
+ case 7: // bool_value
+ return data.boolean();
+ default:
+ data.skip();
+ break;
+ }
+ }
+ return false;
+}
+
+std::string mbgl::toString(const mbgl::Value& value) {
+ if (value.is<std::string>()) return value.get<std::string>();
+ else if (value.is<bool>()) return value.get<bool>() ? "true" : "false";
+ else if (value.is<int64_t>()) return util::toString(value.get<int64_t>());
+ else if (value.is<uint64_t>()) return util::toString(value.get<uint64_t>());
+ else if (value.is<double>()) return util::toString(value.get<double>());
+ else return "null";
+}
+
+mbgl::Value mbgl::parseValue(const rapidjson::Value& 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;
+ }
+}
diff --git a/src/mbgl/style/value.hpp b/src/mbgl/style/value.hpp
new file mode 100644
index 0000000000..87d6f4cda3
--- /dev/null
+++ b/src/mbgl/style/value.hpp
@@ -0,0 +1,45 @@
+#ifndef MBGL_STYLE_VALUE
+#define MBGL_STYLE_VALUE
+
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/pbf.hpp>
+#include <rapidjson/document.h>
+
+#include <cstdlib>
+#include <cerrno>
+
+namespace mbgl {
+
+typedef mapbox::util::variant<bool, int64_t, uint64_t, double, std::string> Value;
+
+std::string toString(const Value &value);
+
+Value parseValue(pbf data);
+Value parseValue(const rapidjson::Value&);
+
+namespace util {
+inline bool parseNumericString(const std::string &str, double &result) {
+ char *end = nullptr;
+ const char *begin = str.c_str();
+ result = std::strtod(begin, &end);
+ while (*end != '\0' && isspace(*end)) end++; // eat whitespace after the end
+ return errno == 0 && end - begin == long(str.size());
+}
+}
+
+template <typename T>
+T toNumber(const Value &value) {
+ if (value.is<std::string>()) {
+ double val;
+ return util::parseNumericString(value.get<std::string>(), val) ? val : 0;
+ }
+ else if (value.is<bool>()) return value.get<bool>();
+ else if (value.is<int64_t>()) return value.get<int64_t>();
+ else if (value.is<uint64_t>()) return value.get<uint64_t>();
+ else if (value.is<double>()) return value.get<double>();
+ else return 0;
+}
+
+}
+
+#endif
diff --git a/src/mbgl/style/value_comparison.hpp b/src/mbgl/style/value_comparison.hpp
new file mode 100644
index 0000000000..895c8a0869
--- /dev/null
+++ b/src/mbgl/style/value_comparison.hpp
@@ -0,0 +1,109 @@
+#ifndef MBGL_STYLE_VALUE_COMPARISON
+#define MBGL_STYLE_VALUE_COMPARISON
+
+#include <mbgl/style/value.hpp>
+#include <cstdlib>
+#include <cerrno>
+
+namespace mbgl {
+
+namespace util {
+
+namespace detail {
+
+template <typename Operator>
+struct relaxed_operator_visitor {
+ typedef bool result_type;
+
+ template <typename T0, typename T1>
+ inline bool operator()(T0, T1) const { return false; }
+
+ template <typename T>
+ inline bool operator()(T lhs, T rhs) const { return Operator()(lhs, rhs); }
+
+ inline bool operator()(int64_t lhs, uint64_t rhs) const {
+ return Operator()(double(lhs), double(rhs));
+ }
+
+ inline bool operator()(int64_t lhs, double rhs) const {
+ return Operator()(double(lhs), rhs);
+ }
+
+ inline bool operator()(uint64_t lhs, int64_t rhs) const {
+ return Operator()(double(lhs), double(rhs));
+ }
+
+ inline bool operator()(uint64_t lhs, double rhs) const {
+ return Operator()(double(lhs), rhs);
+ }
+
+ inline bool operator()(double lhs, uint64_t rhs) const {
+ return Operator()(lhs, double(rhs));
+ }
+
+ inline bool operator()(double lhs, int64_t rhs) const {
+ return Operator()(lhs, double(rhs));
+ }
+};
+
+struct relaxed_equal_operator {
+ template <typename T0, typename T1>
+ inline bool operator()(T0 lhs, T1 rhs) const { return lhs == rhs; }
+};
+
+struct relaxed_not_equal_operator {
+ template <typename T0, typename T1>
+ inline bool operator()(T0 lhs, T1 rhs) const { return lhs != rhs; }
+};
+
+struct relaxed_greater_operator {
+ template <typename T0, typename T1>
+ inline bool operator()(T0 lhs, T1 rhs) const { return lhs > rhs; }
+};
+
+struct relaxed_greater_equal_operator {
+ template <typename T0, typename T1>
+ inline bool operator()(T0 lhs, T1 rhs) const { return lhs >= rhs; }
+};
+
+struct relaxed_less_operator {
+ template <typename T0, typename T1>
+ inline bool operator()(T0 lhs, T1 rhs) const { return lhs < rhs; }
+};
+
+struct relaxed_less_equal_operator {
+ template <typename T0, typename T1>
+ inline bool operator()(T0 lhs, T1 rhs) const { return lhs <= rhs; }
+};
+
+} // end namespace detail
+
+inline bool relaxed_equal(Value const &lhs, Value const &rhs) {
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_equal_operator>(), lhs, rhs);
+}
+
+inline bool relaxed_not_equal(Value const &lhs, Value const &rhs) {
+ return apply_visitor(detail::relaxed_operator_visitor<detail::relaxed_not_equal_operator>(), lhs, rhs);
+}
+
+inline bool relaxed_greater(Value const &lhs, Value const &rhs) {
+ 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(detail::relaxed_operator_visitor<detail::relaxed_greater_equal_operator>(), lhs, rhs);
+}
+
+inline bool relaxed_less(Value const &lhs, Value const &rhs) {
+ 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(detail::relaxed_operator_visitor<detail::relaxed_less_equal_operator>(), lhs, rhs);
+}
+
+}
+
+}
+
+#endif