diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2014-12-04 18:29:42 +0100 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2014-12-04 20:02:50 +0100 |
commit | abafb52f37beb5659efc2105ccd1568e1f754898 (patch) | |
tree | 6a60636d3497560ca61e5aae5f6d7061c4f18553 /src/mbgl/style | |
parent | bff6aeb4da41dee1f5f1cfa0be81b6c257257253 (diff) | |
download | qtlocation-mapboxgl-abafb52f37beb5659efc2105ccd1568e1f754898.tar.gz |
make most headers private
Diffstat (limited to 'src/mbgl/style')
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 |