From 895d90409b66738f88d3cd7fd75c533fb92f922a Mon Sep 17 00:00:00 2001 From: Lauren Budorick Date: Fri, 30 Jan 2015 16:38:32 -0800 Subject: [PATCH] Add functions for layout properties. --- src/mbgl/map/tile_parser.cpp | 105 +++++++++- src/mbgl/map/tile_parser.hpp | 6 + src/mbgl/renderer/symbol_bucket.cpp | 8 +- src/mbgl/style/applied_class_properties.cpp | 2 +- src/mbgl/style/applied_class_properties.hpp | 2 +- src/mbgl/style/property_fallback.cpp | 43 ++++ src/mbgl/style/property_key.hpp | 46 ++++- src/mbgl/style/property_value.hpp | 9 + src/mbgl/style/style_bucket.cpp | 16 +- src/mbgl/style/style_bucket.hpp | 23 ++- src/mbgl/style/style_parser.cpp | 213 ++++++++++++-------- src/mbgl/style/style_parser.hpp | 4 +- src/mbgl/text/placement.cpp | 4 +- 13 files changed, 365 insertions(+), 116 deletions(-) diff --git a/src/mbgl/map/tile_parser.cpp b/src/mbgl/map/tile_parser.cpp index e8c5a4320..c3e7a8244 100644 --- a/src/mbgl/map/tile_parser.cpp +++ b/src/mbgl/map/tile_parser.cpp @@ -91,6 +91,100 @@ void TileParser::parseStyleLayers(util::ptr group) { } } +template +struct PropertyEvaluator { + typedef T result_type; + PropertyEvaluator(float z_) : z(z_) {} + + template ::value, int>::type = 0> + T operator()(const P &value) const { + return value; + } + + T operator()(const Function &value) const { + return mapbox::util::apply_visitor(FunctionEvaluator(z), value); + } + + template ::value, int>::type = 0> + T operator()(const P &) const { + return T(); + } + +private: + const float z; +}; + +template +void TileParser::applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &target, const float z) { + auto it = classProperties.properties.find(key); + if (it != classProperties.properties.end()) { + const PropertyEvaluator evaluator(z); + target = mapbox::util::apply_visitor(evaluator, it->second); + + } +} + +template <> +void TileParser::applyLayoutProperties(StyleBucket &bucket_desc, const float) { + bucket_desc.render.set(); + // no-op; Fill buckets don't currently have any applicable layout properties +} + +template<> +void TileParser::applyLayoutProperties(StyleBucket &bucket_desc, const float z) { + bucket_desc.render.set(); + StyleBucketLine &line = bucket_desc.render.get(); + applyLayoutProperty(PropertyKey::LineCap, bucket_desc.layout, line.cap, z); + applyLayoutProperty(PropertyKey::LineJoin, bucket_desc.layout, line.join, z); + applyLayoutProperty(PropertyKey::LineMiterLimit, bucket_desc.layout, line.miter_limit, z); + applyLayoutProperty(PropertyKey::LineRoundLimit, bucket_desc.layout, line.round_limit, z); +} + +template<> +void TileParser::applyLayoutProperties(StyleBucket &bucket_desc, const float z) { + bucket_desc.render.set(); + StyleBucketSymbol &symbol = bucket_desc.render.get(); + applyLayoutProperty(PropertyKey::SymbolPlacement, bucket_desc.layout, symbol.placement, z); + if (symbol.placement == PlacementType::Line) { + symbol.icon.rotation_alignment = RotationAlignmentType::Map; + symbol.text.rotation_alignment = RotationAlignmentType::Map; + }; + applyLayoutProperty(PropertyKey::SymbolMinDistance, bucket_desc.layout, symbol.min_distance, z); + applyLayoutProperty(PropertyKey::SymbolAvoidEdges, bucket_desc.layout, symbol.avoid_edges, z); + + applyLayoutProperty(PropertyKey::IconAllowOverlap, bucket_desc.layout, symbol.icon.allow_overlap, z); + applyLayoutProperty(PropertyKey::IconIgnorePlacement, bucket_desc.layout, symbol.icon.ignore_placement, z); + applyLayoutProperty(PropertyKey::IconOptional, bucket_desc.layout, symbol.icon.optional, z); + applyLayoutProperty(PropertyKey::IconRotationAlignment, bucket_desc.layout, symbol.icon.rotation_alignment, z); + applyLayoutProperty(PropertyKey::IconMaxSize, bucket_desc.layout, symbol.icon.max_size, z); + applyLayoutProperty(PropertyKey::IconImage, bucket_desc.layout, symbol.icon.image, z); + applyLayoutProperty(PropertyKey::IconPadding, bucket_desc.layout, symbol.icon.padding, z); + applyLayoutProperty(PropertyKey::IconRotate, bucket_desc.layout, symbol.icon.rotate, z); + applyLayoutProperty(PropertyKey::IconKeepUpright, bucket_desc.layout, symbol.icon.keep_upright, z); + applyLayoutProperty(PropertyKey::IconOffsetX, bucket_desc.layout, symbol.icon.offset[0], z); + applyLayoutProperty(PropertyKey::IconOffsetY, bucket_desc.layout, symbol.icon.offset[1], z); + + applyLayoutProperty(PropertyKey::TextRotationAlignment, bucket_desc.layout, symbol.text.rotation_alignment, z); + applyLayoutProperty(PropertyKey::TextField, bucket_desc.layout, symbol.text.field, z); + applyLayoutProperty(PropertyKey::TextFont, bucket_desc.layout, symbol.text.font, z); + applyLayoutProperty(PropertyKey::TextMaxSize, bucket_desc.layout, symbol.text.max_size, z); + applyLayoutProperty(PropertyKey::TextMaxWidth, bucket_desc.layout, symbol.text.max_width, z); + applyLayoutProperty(PropertyKey::TextLineHeight, bucket_desc.layout, symbol.text.line_height, z); + applyLayoutProperty(PropertyKey::TextLetterSpacing, bucket_desc.layout, symbol.text.letter_spacing, z); + applyLayoutProperty(PropertyKey::TextMaxAngle, bucket_desc.layout, symbol.text.max_angle, z); + applyLayoutProperty(PropertyKey::TextRotate, bucket_desc.layout, symbol.text.rotate, z); + applyLayoutProperty(PropertyKey::TextPadding, bucket_desc.layout, symbol.text.padding, z); + applyLayoutProperty(PropertyKey::TextIgnorePlacement, bucket_desc.layout, symbol.text.ignore_placement, z); + applyLayoutProperty(PropertyKey::TextOptional, bucket_desc.layout, symbol.text.optional, z); + applyLayoutProperty(PropertyKey::TextJustify, bucket_desc.layout, symbol.text.justify, z); + applyLayoutProperty(PropertyKey::TextAnchor, bucket_desc.layout, symbol.text.anchor, z); + applyLayoutProperty(PropertyKey::TextKeepUpright, bucket_desc.layout, symbol.text.keep_upright, z); + applyLayoutProperty(PropertyKey::TextTransform, bucket_desc.layout, symbol.text.transform, z); + applyLayoutProperty(PropertyKey::TextOffsetX, bucket_desc.layout, symbol.text.offset[0], z); + applyLayoutProperty(PropertyKey::TextOffsetY, bucket_desc.layout, symbol.text.offset[1], z); + applyLayoutProperty(PropertyKey::TextAllowOverlap, bucket_desc.layout, symbol.text.allow_overlap, z); +} + std::unique_ptr TileParser::createBucket(util::ptr bucket_desc) { if (!bucket_desc) { fprintf(stderr, "missing bucket desc\n"); @@ -105,13 +199,16 @@ std::unique_ptr TileParser::createBucket(util::ptr bucket_d auto layer_it = vector_data.layers.find(bucket_desc->source_layer); if (layer_it != vector_data.layers.end()) { const VectorTileLayer &layer = layer_it->second; - if (bucket_desc->render.is()) { + if (bucket_desc->type == StyleLayerType::Fill) { + applyLayoutProperties(*bucket_desc, tile.id.z); return createFillBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { + } else if (bucket_desc->type == StyleLayerType::Line) { + applyLayoutProperties(*bucket_desc, tile.id.z); return createLineBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { + } else if (bucket_desc->type == StyleLayerType::Symbol) { + applyLayoutProperties(*bucket_desc, tile.id.z); return createSymbolBucket(layer, bucket_desc->filter, bucket_desc->render.get()); - } else if (bucket_desc->render.is()) { + } else if (bucket_desc->type == StyleLayerType::Raster) { return nullptr; } else { fprintf(stderr, "[WARNING] unknown bucket render type for layer '%s' (source layer '%s')\n", bucket_desc->name.c_str(), bucket_desc->source_layer.c_str()); diff --git a/src/mbgl/map/tile_parser.hpp b/src/mbgl/map/tile_parser.hpp index beae3af83..d10f9aa26 100644 --- a/src/mbgl/map/tile_parser.hpp +++ b/src/mbgl/map/tile_parser.hpp @@ -3,6 +3,8 @@ #include #include +#include +#include #include #include #include @@ -48,6 +50,10 @@ public: private: bool obsolete() const; void parseStyleLayers(util::ptr group); + + template void applyLayoutProperties(StyleBucket &bucket, float z); + template void applyLayoutProperty(PropertyKey key, ClassProperties &classProperties, T &, float z); + std::unique_ptr createBucket(util::ptr bucket_desc); std::unique_ptr createFillBucket(const VectorTileLayer& layer, const FilterExpression &filter, const StyleBucketFill &fill); diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index a5043a373..00ff4e4a3 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -177,13 +177,13 @@ void SymbolBucket::addFeatures(const VectorTileLayer &layer, const FilterExpress if (feature.label.length()) { shaping = fontStack.getShaping( /* string */ feature.label, - /* maxWidth */ properties.text.max_width, - /* lineHeight */ properties.text.line_height, + /* maxWidth: ems */ properties.text.max_width * 24, + /* lineHeight: ems */ properties.text.line_height * 24, /* horizontalAlign */ horizontalAlign, /* verticalAlign */ verticalAlign, /* justify */ justify, - /* spacing */ properties.text.letter_spacing, - /* translate */ properties.text.offset); + /* spacing: ems */ properties.text.letter_spacing * 24, + /* translate */ vec2(properties.text.offset[0], properties.text.offset[1])); // Add the glyphs we need for this label to the glyph atlas. if (shaping.size()) { diff --git a/src/mbgl/style/applied_class_properties.cpp b/src/mbgl/style/applied_class_properties.cpp index 4e5647ce4..6e304e69d 100644 --- a/src/mbgl/style/applied_class_properties.cpp +++ b/src/mbgl/style/applied_class_properties.cpp @@ -8,7 +8,7 @@ AppliedClassProperty::AppliedClassProperty(ClassID class_id, std::chrono::steady end(end_), value(value_) {} -// Returns thie ID of the most recent +// Returns the ID of the most recent ClassID AppliedClassProperties::mostRecent() const { return properties.size() ? properties.back().name : ClassID::Fallback; } diff --git a/src/mbgl/style/applied_class_properties.hpp b/src/mbgl/style/applied_class_properties.hpp index ac086a224..bd8409559 100644 --- a/src/mbgl/style/applied_class_properties.hpp +++ b/src/mbgl/style/applied_class_properties.hpp @@ -26,7 +26,7 @@ public: std::list properties; public: - // Returns thie ID of the most recent + // Returns the ID of the most recent ClassID mostRecent() const; void add(ClassID class_id, std::chrono::steady_clock::time_point begin, std::chrono::steady_clock::time_point end, const PropertyValue &value); bool hasTransitions() const; diff --git a/src/mbgl/style/property_fallback.cpp b/src/mbgl/style/property_fallback.cpp index dc747b37e..5fc3ce1f0 100644 --- a/src/mbgl/style/property_fallback.cpp +++ b/src/mbgl/style/property_fallback.cpp @@ -1,5 +1,6 @@ #include #include +#include namespace mbgl { @@ -52,6 +53,48 @@ const std::map PropertyFallbackValue::properties = { { PropertyKey::BackgroundOpacity, defaultStyleProperties().opacity }, { PropertyKey::BackgroundColor, defaultStyleProperties().color }, + + { PropertyKey::LineCap, defaultLayoutProperties().cap }, + { PropertyKey::LineJoin, defaultLayoutProperties().join }, + { PropertyKey::LineMiterLimit, defaultLayoutProperties().miter_limit }, + { PropertyKey::LineRoundLimit, defaultLayoutProperties().round_limit }, + + { PropertyKey::SymbolPlacement, defaultLayoutProperties().placement }, + { PropertyKey::SymbolMinDistance, defaultLayoutProperties().min_distance }, + { PropertyKey::SymbolAvoidEdges, defaultLayoutProperties().avoid_edges }, + + { PropertyKey::IconAllowOverlap, defaultLayoutProperties().icon.allow_overlap }, + { PropertyKey::IconIgnorePlacement, defaultLayoutProperties().icon.ignore_placement }, + { PropertyKey::IconOptional, defaultLayoutProperties().icon.optional }, + { PropertyKey::IconRotationAlignment, defaultLayoutProperties().icon.rotation_alignment }, + { PropertyKey::IconMaxSize, defaultLayoutProperties().icon.max_size }, + { PropertyKey::IconImage, defaultLayoutProperties().icon.image }, + { PropertyKey::IconRotate, defaultLayoutProperties().icon.rotate }, + { PropertyKey::IconPadding, defaultLayoutProperties().icon.padding }, + { PropertyKey::IconKeepUpright, defaultLayoutProperties().icon.keep_upright }, + { PropertyKey::IconOffsetX, defaultLayoutProperties().icon.offset[0] }, + { PropertyKey::IconOffsetY, defaultLayoutProperties().icon.offset[1] }, + + { PropertyKey::TextRotationAlignment, defaultLayoutProperties().text.rotation_alignment }, + { PropertyKey::TextField, defaultLayoutProperties().text.field }, + { PropertyKey::TextFont, defaultLayoutProperties().text.font }, + { PropertyKey::TextMaxSize, defaultLayoutProperties().text.max_size }, + { PropertyKey::TextMaxWidth, defaultLayoutProperties().text.max_width }, + { PropertyKey::TextLineHeight, defaultLayoutProperties().text.line_height }, + { PropertyKey::TextLetterSpacing, defaultLayoutProperties().text.letter_spacing }, + { PropertyKey::TextJustify, defaultLayoutProperties().text.justify }, + { PropertyKey::TextAnchor, defaultLayoutProperties().text.anchor }, + { PropertyKey::TextMaxAngle, defaultLayoutProperties().text.max_angle }, + { PropertyKey::TextRotate, defaultLayoutProperties().text.rotate }, + { PropertyKey::TextPadding, defaultLayoutProperties().text.padding }, + { PropertyKey::TextKeepUpright, defaultLayoutProperties().text.keep_upright }, + { PropertyKey::TextTransform, defaultLayoutProperties().text.transform }, + { PropertyKey::TextOffsetX, defaultLayoutProperties().text.offset[0] }, + { PropertyKey::TextOffsetY, defaultLayoutProperties().text.offset[1] }, + { PropertyKey::TextAllowOverlap, defaultLayoutProperties().text.allow_overlap }, + { PropertyKey::TextIgnorePlacement, defaultLayoutProperties().text.ignore_placement }, + { PropertyKey::TextOptional, defaultLayoutProperties().text.optional }, + }; const PropertyValue PropertyFallbackValue::defaultProperty = false; diff --git a/src/mbgl/style/property_key.hpp b/src/mbgl/style/property_key.hpp index e283f1157..f10607a7a 100644 --- a/src/mbgl/style/property_key.hpp +++ b/src/mbgl/style/property_key.hpp @@ -26,8 +26,16 @@ enum class PropertyKey { LineDashArray, // for transitions only LineImage, + LineCap, + LineJoin, + LineMiterLimit, + LineRoundLimit, + + SymbolPlacement, + SymbolMinDistance, + SymbolAvoidEdges, + IconOpacity, - IconRotate, IconSize, IconColor, IconHaloColor, @@ -38,6 +46,18 @@ enum class PropertyKey { IconTranslateY, IconTranslateAnchor, + IconAllowOverlap, + IconIgnorePlacement, + IconOptional, + IconRotationAlignment, + IconMaxSize, + IconImage, + IconPadding, + IconRotate, + IconKeepUpright, + IconOffsetX, + IconOffsetY, + TextOpacity, TextSize, TextColor, @@ -49,6 +69,26 @@ enum class PropertyKey { TextTranslateY, TextTranslateAnchor, + TextRotationAlignment, + TextField, + TextFont, + TextMaxSize, + TextMaxWidth, + TextLineHeight, + TextLetterSpacing, + TextMaxAngle, + TextRotate, + TextPadding, + TextIgnorePlacement, + TextOptional, + TextJustify, + TextAnchor, + TextKeepUpright, + TextTransform, + TextOffsetX, + TextOffsetY, + TextAllowOverlap, + RasterOpacity, RasterHueRotate, RasterBrightness, // for transitions only @@ -60,7 +100,9 @@ enum class PropertyKey { BackgroundOpacity, BackgroundColor, - BackgroundImage + BackgroundImage, + + Visibilty }; } diff --git a/src/mbgl/style/property_value.hpp b/src/mbgl/style/property_value.hpp index e017981de..ff5a2f868 100644 --- a/src/mbgl/style/property_value.hpp +++ b/src/mbgl/style/property_value.hpp @@ -13,6 +13,15 @@ typedef mapbox::util::variant< std::string, TranslateAnchorType, RotateAnchorType, + bool, + CapType, + JoinType, + VisibilityType, + PlacementType, + RotationAlignmentType, + TextTransformType, + TextJustifyType, + TextAnchorType, Function, Function, Function, diff --git a/src/mbgl/style/style_bucket.cpp b/src/mbgl/style/style_bucket.cpp index 9a40c2386..20d5d1147 100644 --- a/src/mbgl/style/style_bucket.cpp +++ b/src/mbgl/style/style_bucket.cpp @@ -2,14 +2,12 @@ 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; - } -} +StyleBucket::StyleBucket(StyleLayerType type_) :type(type_) {} + +template<> const StyleBucketFill &defaultLayoutProperties() { static StyleBucketFill p; return p; } +template<> const StyleBucketLine &defaultLayoutProperties() { static StyleBucketLine p; return p; } +template<> const StyleBucketSymbol &defaultLayoutProperties() { static StyleBucketSymbol p; return p; } +template<> const StyleBucketRaster &defaultLayoutProperties() { static StyleBucketRaster p; return p; } +template<> const StyleBucketBackground &defaultLayoutProperties() { static StyleBucketBackground p; return p; } } \ No newline at end of file diff --git a/src/mbgl/style/style_bucket.hpp b/src/mbgl/style/style_bucket.hpp index 7a70e32c3..702d0286a 100644 --- a/src/mbgl/style/style_bucket.hpp +++ b/src/mbgl/style/style_bucket.hpp @@ -9,6 +9,7 @@ #include #include #include +#include #include @@ -52,7 +53,7 @@ public: float rotate = 0.0f; float padding = 2.0f; bool keep_upright = false; - vec2 offset = {0, 0}; + std::array offset = {{ 0, 0 }}; } icon; struct { @@ -60,9 +61,9 @@ public: 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 */; + float max_width = 15.0f /* em */; + float line_height = 1.2f /* em */; + float letter_spacing = 0.0f /* em */; TextJustifyType justify = TextJustifyType::Center; TextAnchorType anchor = TextAnchorType::Center; float max_angle = 45.0f /* degrees */; @@ -71,7 +72,7 @@ public: float padding = 2.0f; bool keep_upright = true; TextTransformType transform = TextTransformType::None; - vec2 offset = {0, 0}; + std::array offset = {{ 0, 0 }}; bool allow_overlap = false; bool ignore_placement = false; bool optional = false; @@ -82,8 +83,12 @@ class StyleBucketRaster { public: }; +class StyleBucketBackground { +public: +}; + typedef mapbox::util::variant StyleBucketRender; + StyleBucketRaster, StyleBucketBackground, std::false_type> StyleBucketRender; class StyleBucket { @@ -91,18 +96,20 @@ public: typedef util::ptr Ptr; StyleBucket(StyleLayerType type); - + StyleLayerType type; std::string name; util::ptr style_source; std::string source_layer; FilterExpression filter; + ClassProperties layout; StyleBucketRender render = std::false_type(); float min_zoom = -std::numeric_limits::infinity(); float max_zoom = std::numeric_limits::infinity(); VisibilityType visibility = VisibilityType::Visible; }; - +template +const T &defaultLayoutProperties(); }; diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index e69388e3f..15aedd8ea 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -327,6 +327,18 @@ bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKe return parsed; } +template +void StyleParser::parseVisibility(StyleBucket &bucket, JSVal value) { + if (!value.HasMember("visibility")) { + return; + } else if (!value["visibility"].IsString()) { + Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string"); + bucket.visibility = VisibilityType::Visible; + return; + } + bucket.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() }); +} + template bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value) { if (!value.HasMember(property_name)) { @@ -345,6 +357,15 @@ template<> std::tuple StyleParser::parseProperty(JSVal value, return std::tuple { true, { value.GetString(), value.GetStringLength() } }; } +template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { + if (!value.IsBool()) { + Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", property_name); + return std::tuple { false, true }; + } + + return std::tuple { true, value.GetBool() }; +} + template<> std::tuple StyleParser::parseProperty(JSVal value, const char *property_name) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", property_name); @@ -363,6 +384,69 @@ template<> std::tuple StyleParser::parseProperty { true, RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; } +template<> std::tuple 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 { false, CapType::Butt }; + } + + return std::tuple { true, CapTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple 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 { false, JoinType::Miter }; + } + + return std::tuple { true, JoinTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple 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 { false, PlacementType::Point }; + } + + return std::tuple { true, PlacementTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple 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 { false, TextAnchorType::Center }; + } + + return std::tuple { true, TextAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple 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 { false, TextJustifyType::Center }; + } + + return std::tuple { true, TextJustifyTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple 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 { false, TextTransformType::None }; + } + + return std::tuple { true, TextTransformTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + +template<> std::tuple 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 { false, RotationAlignmentType::Map }; + } + + return std::tuple { true, RotationAlignmentTypeClass({ value.GetString(), value.GetStringLength() }) }; +} + template<> std::tuple StyleParser::parseProperty(JSVal value, const char */*property_name*/) { PropertyTransition transition; if (value.IsObject()) { @@ -642,6 +726,50 @@ void StyleParser::parsePaint(JSVal value, ClassProperties &klass) { parseOptionalProperty("background-image", Key::BackgroundImage, klass, value); } +void StyleParser::parseLayout(JSVal value, util::ptr &bucket) { + using Key = PropertyKey; + + parseVisibility(*bucket, value); + + parseOptionalProperty("line-cap", Key::LineCap, bucket->layout, value); + parseOptionalProperty("line-join", Key::LineJoin, bucket->layout, value); + parseOptionalProperty>("line-miter-limit", Key::LineMiterLimit, bucket->layout, value); + parseOptionalProperty>("line-round-limit", Key::LineRoundLimit, bucket->layout, value); + + parseOptionalProperty("symbol-placement", Key::SymbolPlacement, bucket->layout, value); + parseOptionalProperty>("symbol-min-distance", Key::SymbolMinDistance, bucket->layout, value); + parseOptionalProperty("symbol-avoid-edges", Key::SymbolAvoidEdges, bucket->layout, value); + parseOptionalProperty("icon-allow-overlap", Key::IconAllowOverlap, bucket->layout, value); + parseOptionalProperty("icon-ignore-placement", Key::IconIgnorePlacement, bucket->layout, value); + parseOptionalProperty("icon-optional", Key::IconOptional, bucket->layout, value); + parseOptionalProperty("icon-rotation-alignment", Key::IconRotationAlignment, bucket->layout, value); + parseOptionalProperty>("icon-max-size", Key::IconMaxSize, bucket->layout, value); + parseOptionalProperty("icon-image", Key::IconImage, bucket->layout, value); + parseOptionalProperty>("icon-rotate", Key::IconRotate, bucket->layout, value); + parseOptionalProperty>("icon-padding", Key::IconPadding, bucket->layout, value); + parseOptionalProperty("icon-keep-upright", Key::IconKeepUpright, bucket->layout, value); + parseOptionalProperty>("icon-offset", { Key::IconOffsetX, Key::IconOffsetY }, bucket->layout, value); + parseOptionalProperty("text-rotation-alignment", Key::TextRotationAlignment, bucket->layout, value); + parseOptionalProperty("text-field", Key::TextField, bucket->layout, value); + parseOptionalProperty("text-font", Key::TextFont, bucket->layout, value); + parseOptionalProperty>("text-max-size", Key::TextMaxSize, bucket->layout, value); + parseOptionalProperty>("text-max-width", Key::TextMaxWidth, bucket->layout, value); + parseOptionalProperty>("text-line-height", Key::TextLineHeight, bucket->layout, value); + parseOptionalProperty>("text-letter-spacing", Key::TextLetterSpacing, bucket->layout, value); + parseOptionalProperty("text-justify", Key::TextJustify, bucket->layout, value); + parseOptionalProperty("text-anchor", Key::TextAnchor, bucket->layout, value); + parseOptionalProperty>("text-max-angle", Key::TextMaxAngle, bucket->layout, value); + parseOptionalProperty>("text-rotate", Key::TextRotate, bucket->layout, value); + parseOptionalProperty>("text-padding", Key::TextPadding, bucket->layout, value); + parseOptionalProperty("text-keep-upright", Key::TextKeepUpright, bucket->layout, value); + parseOptionalProperty("text-transform", Key::TextTransform, bucket->layout, value); + parseOptionalProperty>("text-offset", { Key::TextOffsetX, Key::TextOffsetY }, bucket->layout, value); + parseOptionalProperty("text-allow-overlap", Key::TextAllowOverlap, bucket->layout, value); + parseOptionalProperty("text-ignore-placement", Key::TextIgnorePlacement, bucket->layout, value); + parseOptionalProperty("text-optional", Key::TextOptional, bucket->layout, value); + +} + void StyleParser::parseReference(JSVal value, util::ptr &layer) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", layer->id.c_str()); @@ -704,7 +832,7 @@ void StyleParser::parseBucket(JSVal value, util::ptr &layer) { if (value.HasMember("layout")) { JSVal value_render = replaceConstant(value["layout"]); - parseLayout(value_render, layer); + parseLayout(value_render, layer->bucket); } if (value.HasMember("minzoom")) { @@ -726,89 +854,6 @@ void StyleParser::parseBucket(JSVal value, util::ptr &layer) { } } -void StyleParser::parseLayout(JSVal value, util::ptr &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; - parseRenderProperty(value, bucket.visibility, "visibility"); - - switch (layer->type) { - case StyleLayerType::Fill: { - StyleBucketFill &render = bucket.render.get(); - - parseRenderProperty(value, render.winding, "fill-winding"); - } break; - - case StyleLayerType::Line: { - StyleBucketLine &render = bucket.render.get(); - - parseRenderProperty(value, render.cap, "line-cap"); - parseRenderProperty(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(); - - parseRenderProperty(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(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(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(value, render.text.justify, "text-justify"); - parseRenderProperty(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(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; - - default: - // There are no render properties for these layer types. - break; - } -} - void StyleParser::parseSprite(JSVal value) { if (value.IsString()) { sprite = { value.GetString(), value.GetStringLength() }; diff --git a/src/mbgl/style/style_parser.hpp b/src/mbgl/style/style_parser.hpp index 42e22f945..24fd4292a 100644 --- a/src/mbgl/style/style_parser.hpp +++ b/src/mbgl/style/style_parser.hpp @@ -53,7 +53,7 @@ private: void parsePaint(JSVal, ClassProperties &properties); void parseReference(JSVal value, util::ptr &layer); void parseBucket(JSVal value, util::ptr &layer); - void parseLayout(JSVal value, util::ptr &layer); + void parseLayout(JSVal value, util::ptr &bucket); void parseSprite(JSVal value); void parseGlyphURL(JSVal value); @@ -65,6 +65,8 @@ private: // Parses optional properties into style class properties. template + void parseVisibility(StyleBucket &bucket, JSVal value); + template bool parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value); template bool parseOptionalProperty(const char *property_name, const std::vector &keys, ClassProperties &klass, JSVal value); diff --git a/src/mbgl/text/placement.cpp b/src/mbgl/text/placement.cpp index bc0f642c4..27e2b7010 100644 --- a/src/mbgl/text/placement.cpp +++ b/src/mbgl/text/placement.cpp @@ -131,8 +131,8 @@ GlyphBox getMergedBoxes(const GlyphBoxes &glyphs, const Anchor &anchor) { Placement Placement::getIcon(Anchor &anchor, const Rect &image, float boxScale, const std::vector &line, const StyleBucketSymbol &props) { - const float dx = props.icon.offset.x; - const float dy = props.icon.offset.y; + const float dx = props.icon.offset[0]; + const float dy = props.icon.offset[1]; float x1 = dx - image.originalW / 2.0f; float x2 = x1 + image.w; float y1 = dy - image.originalH / 2.0f;