#include #include #include #include #include #include #include #include #include #include #pragma GCC diagnostic push #ifndef __clang__ #pragma GCC diagnostic ignored "-Wunused-local-typedefs" #endif #include #pragma GCC diagnostic pop #include 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")) { parseLayers(document["layers"]); // create point annotations layer // const std::string& id = AnnotationManager::layerID; std::map paints; util::ptr annotations = std::make_shared(id, std::move(paints)); annotations->type = StyleLayerType::Symbol; layersMap.emplace(id, std::pair> { JSVal(id), annotations }); layers.emplace_back(annotations); util::ptr pointBucket = std::make_shared(annotations->type); pointBucket->name = annotations->id; pointBucket->source_layer = annotations->id; rapidjson::Document d; rapidjson::Value iconImage(rapidjson::kObjectType); iconImage.AddMember("icon-image", "{sprite}", d.GetAllocator()); parseLayout(iconImage, pointBucket); rapidjson::Value iconOverlap(rapidjson::kObjectType); iconOverlap.AddMember("icon-allow-overlap", true, d.GetAllocator()); parseLayout(iconOverlap, pointBucket); SourceInfo& info = sources.emplace(id, std::make_shared()).first->second->info; info.type = SourceType::Annotations; auto source_it = sources.find(id); pointBucket->style_source = source_it->second; annotations->bucket = pointBucket; // // end point annotations } 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 { Log::Warning(Event::ParseStyle, "'%s' must be a boolean", 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::max()) { Log::Warning(Event::ParseStyle, "values for %s that are larger than %d are not supported", name, std::numeric_limits::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 &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 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()).first->second->info; parseRenderProperty(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}}; } std::tuple> StyleParser::parseFloatArray(JSVal value) { if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers"); return std::tuple> { false, std::vector() }; } std::vector vec; for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { JSVal part = replaceConstant(value[i]); if (!part.IsNumber()) { Log::Warning(Event::ParseStyle, "dasharray value must be an array of numbers"); return std::tuple> { false, std::vector() }; } vec.push_back(part.GetDouble()); } return std::tuple> { true, vec }; } template <> std::tuple> StyleParser::parseProperty(JSVal value, const char*) { if (value.IsArray() && value.Size() == 2 && value[rapidjson::SizeType(0)].IsNumber() && value[rapidjson::SizeType(1)].IsNumber()) { float first = value[rapidjson::SizeType(0)].GetDouble(); float second = value[rapidjson::SizeType(1)].GetDouble(); return std::tuple> { false, {{ first, second }} }; } else { Log::Warning(Event::ParseStyle, "value must be array of two numbers"); return std::tuple> { false, {{ 0.0f, 0.0f }} }; } } template <> std::tuple StyleParser::parseProperty(JSVal value, const char* property_name) { JSVal rvalue = replaceConstant(value); if (rvalue.IsNumber()) { return std::tuple { true, rvalue.GetDouble() }; } else { Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", property_name); return std::tuple { false, 0.0f }; } } template <> std::tuple StyleParser::parseProperty(JSVal value, const char*) { JSVal rvalue = replaceConstant(value); return std::tuple { true, parseColor(rvalue) }; } template <> std::tuple>> StyleParser::parseProperty(JSVal value, const char*) { Faded> parsed; JSVal rvalue = replaceConstant(value); parsed.to = std::get<1>(parseFloatArray(rvalue)); return std::tuple>> { true, parsed }; } template <> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { JSVal rvalue = replaceConstant(value); Faded parsed; if (rvalue.IsString()) { parsed.to = { value.GetString(), value.GetStringLength() }; return std::tuple> { true, parsed }; } else { Log::Warning(Event::ParseStyle, "value of '%s' must be a string, or a string function", property_name); return std::tuple> { false, parsed }; } } template std::tuple>> StyleParser::parseStops(JSVal value_stops, const char *property_name) { if (!value_stops.IsArray()) { Log::Warning(Event::ParseStyle, "stops function must specify a stops array"); return std::tuple>> { false, {}}; } std::vector> 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>> { false, {}}; } JSVal z = stop[rapidjson::SizeType(0)]; if (!z.IsNumber()) { Log::Warning(Event::ParseStyle, "zoom level in stop must be a number"); return std::tuple>> { false, {}}; } stops.emplace_back(z.GetDouble(), std::get<1>(parseProperty(replaceConstant(stop[rapidjson::SizeType(1)]), property_name))); } else { Log::Warning(Event::ParseStyle, "function argument must be a numeric value"); return std::tuple>> { false, {}}; } } return std::tuple>>(true, stops); } template std::tuple> StyleParser::parseFunction(JSVal value, const char *property_name) { if (!value.IsObject()) { return std::tuple> { true, ConstantFunction(std::get<1>(parseProperty(value, property_name))) }; } if (!value.HasMember("stops")) { Log::Warning(Event::ParseStyle, "function must specify a function type"); return std::tuple> { false, ConstantFunction(T()) }; } float base = 1.0f; 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"); } } auto stops = parseStops(value["stops"], property_name); if (!std::get<0>(stops)) { return std::tuple> { false, ConstantFunction(T()) }; } return std::tuple> { true, StopsFunction(std::get<1>(stops), base) }; } template std::tuple> StyleParser::parsePiecewiseConstantFunction(JSVal value, Duration duration) { if (!value.HasMember("stops")) { Log::Warning(Event::ParseStyle, "function must specify a function type"); return std::tuple> { false, {} }; } auto stops = parseStops(value["stops"], ""); if (!std::get<0>(stops)) { return std::tuple> { false, {} }; } return std::tuple> { true, { std::get<1>(stops), duration } }; } template bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass) { auto res = parseProperty(value, property_name); if (std::get<0>(res)) { klass.set(key, std::get<1>(res)); } return std::get<0>(res); } template bool StyleParser::setProperty(JSVal value, const char *property_name, PropertyKey key, ClassProperties &klass, JSVal transition) { auto res = parseProperty(value, property_name, transition); if (std::get<0>(res)) { klass.set(key, std::get<1>(res)); } return std::get<0>(res); } 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)) { return false; } else { return setProperty(replaceConstant(value[property_name]), property_name, key, klass); } } template bool StyleParser::parseOptionalProperty(const char *property_name, PropertyKey key, ClassProperties &klass, JSVal value, const char *transition_name) { if (!value.HasMember(property_name)) { return false; } else { if (value.HasMember(transition_name)) { return setProperty(replaceConstant(value[property_name]), property_name, key, klass, value[transition_name]); } else { JSVal val = JSVal(rapidjson::kObjectType); return setProperty(replaceConstant(value[property_name]), property_name, key, klass, val); } } } std::string normalizeFontStack(const std::string &name) { namespace algo = boost::algorithm; std::vector parts; algo::split(parts, name, algo::is_any_of(","), algo::token_compress_on); std::for_each(parts.begin(), parts.end(), [](std::string& str) { algo::trim(str); }); return algo::join(parts, ", "); } 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, std::string() }; } if (std::string { "text-font" } == property_name) { return std::tuple { true, normalizeFontStack({ value.GetString(), value.GetStringLength() }) }; } else { 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); return std::tuple { false, TranslateAnchorType::Map }; } return std::tuple { true, TranslateAnchorTypeClass({ 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, RotateAnchorType::Map }; } return std::tuple { 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()) { if (value.HasMember("duration") && value["duration"].IsNumber()) { transition.duration = std::chrono::milliseconds(value["duration"].GetUint()); } if (value.HasMember("delay") && value["delay"].IsNumber()) { transition.delay = std::chrono::milliseconds(value["delay"].GetUint()); } } if (transition.duration == Duration::zero() && transition.delay == Duration::zero()) { return std::tuple { false, std::move(transition) }; } return std::tuple { true, std::move(transition) }; } template<> std::tuple>> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction>(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple> StyleParser::parseProperty(JSVal value, const char *property_name) { return parseFunction(value, property_name); } template<> std::tuple>>> StyleParser::parseProperty(JSVal value, const char *property_name, JSVal transition) { Duration duration = std::chrono::milliseconds(300); if (transition.HasMember("duration")) { duration = std::chrono::milliseconds(transition["duration"].GetUint()); } if (value.IsObject()) { return parsePiecewiseConstantFunction>>(value, duration); } else if (value.IsArray()) { Faded> parsed; std::tuple> floatarray = parseFloatArray(value); parsed.to = std::get<1>(floatarray); return std::tuple>>> { std::get<0>(floatarray), { parsed, duration } }; } else { Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers, or a number array function", property_name); return std::tuple>>> { false, {} }; } } template<> std::tuple>> StyleParser::parseProperty(JSVal value, const char *property_name, JSVal transition) { Duration duration = std::chrono::milliseconds(300); if (transition.HasMember("duration")) { duration = std::chrono::milliseconds(transition["duration"].GetUint()); } if (value.IsObject()) { return parsePiecewiseConstantFunction>(value, duration); } else if (value.IsString()) { Faded parsed; parsed.to = { value.GetString(), value.GetStringLength() }; return std::tuple>> { true, { parsed, duration } }; } else { Log::Warning(Event::ParseStyle, "value of '%s' must be string or a string function", property_name); return std::tuple>> { false, {} }; } } #pragma mark - Parse Layers void StyleParser::parseLayers(JSVal value) { if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "layers must be an array"); return; } for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { JSVal layerValue = value[i]; if (!layerValue.IsObject()) { Log::Warning(Event::ParseStyle, "layer must be an object"); continue; } if (!layerValue.HasMember("id")) { Log::Warning(Event::ParseStyle, "layer must have an id"); continue; } JSVal id = layerValue["id"]; if (!id.IsString()) { Log::Warning(Event::ParseStyle, "layer id must be a string"); continue; } const std::string layerID = { id.GetString(), id.GetStringLength() }; if (layersMap.find(layerID) != layersMap.end()) { Log::Warning(Event::ParseStyle, "duplicate layer id %s", layerID.c_str()); continue; } // Parse paints already, as they can't be inherited anyway. std::map paints; parsePaints(layerValue, paints); util::ptr layer = std::make_shared(layerID, std::move(paints)); layers.emplace_back(layer); layersMap.emplace(layerID, std::pair> { layerValue, layer }); } for (auto& pair : layersMap) { parseLayer(pair.second); } } void StyleParser::parseLayer(std::pair> &pair) { JSVal value = pair.first; util::ptr &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) { // 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 &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>("fill-antialias", Key::FillAntialias, klass, value); parseOptionalProperty>("fill-opacity", Key::FillOpacity, klass, value); parseOptionalProperty("fill-opacity-transition", Key::FillOpacity, klass, value); parseOptionalProperty>("fill-color", Key::FillColor, klass, value); parseOptionalProperty("fill-color-transition", Key::FillColor, klass, value); parseOptionalProperty>("fill-outline-color", Key::FillOutlineColor, klass, value); parseOptionalProperty("fill-outline-color-transition", Key::FillOutlineColor, klass, value); parseOptionalProperty>>("fill-translate", Key::FillTranslate, klass, value); parseOptionalProperty("fill-translate-transition", Key::FillTranslate, klass, value); parseOptionalProperty>("fill-translate-anchor", Key::FillTranslateAnchor, klass, value); parseOptionalProperty>>("fill-image", Key::FillImage, klass, value, "fill-image-transition"); parseOptionalProperty>("line-opacity", Key::LineOpacity, klass, value); parseOptionalProperty("line-opacity-transition", Key::LineOpacity, klass, value); parseOptionalProperty>("line-color", Key::LineColor, klass, value); parseOptionalProperty("line-color-transition", Key::LineColor, klass, value); parseOptionalProperty>>("line-translate", Key::LineTranslate, klass, value); parseOptionalProperty("line-translate-transition", Key::LineTranslate, klass, value); parseOptionalProperty>("line-translate-anchor", Key::LineTranslateAnchor, klass, value); parseOptionalProperty>("line-width", Key::LineWidth, klass, value); parseOptionalProperty("line-width-transition", Key::LineWidth, klass, value); parseOptionalProperty>("line-gap-width", Key::LineGapWidth, klass, value); parseOptionalProperty("line-gap-width-transition", Key::LineGapWidth, klass, value); parseOptionalProperty>("line-blur", Key::LineBlur, klass, value); parseOptionalProperty("line-blur-transition", Key::LineBlur, klass, value); parseOptionalProperty>>>("line-dasharray", Key::LineDashArray, klass, value, "line-dasharray-transition"); parseOptionalProperty>>("line-image", Key::LineImage, klass, value, "line-image-transition"); parseOptionalProperty>("icon-opacity", Key::IconOpacity, klass, value); parseOptionalProperty("icon-opacity-transition", Key::IconOpacity, klass, value); parseOptionalProperty>("icon-rotate", Key::IconRotate, klass, value); parseOptionalProperty>("icon-size", Key::IconSize, klass, value); parseOptionalProperty("icon-size-transition", Key::IconSize, klass, value); parseOptionalProperty>("icon-color", Key::IconColor, klass, value); parseOptionalProperty("icon-color-transition", Key::IconColor, klass, value); parseOptionalProperty>("icon-halo-color", Key::IconHaloColor, klass, value); parseOptionalProperty("icon-halo-color-transition", Key::IconHaloColor, klass, value); parseOptionalProperty>("icon-halo-width", Key::IconHaloWidth, klass, value); parseOptionalProperty("icon-halo-width-transition", Key::IconHaloWidth, klass, value); parseOptionalProperty>("icon-halo-blur", Key::IconHaloBlur, klass, value); parseOptionalProperty("icon-halo-blur-transition", Key::IconHaloBlur, klass, value); parseOptionalProperty>>("icon-translate", Key::IconTranslate, klass, value); parseOptionalProperty("icon-translate-transition", Key::IconTranslate, klass, value); parseOptionalProperty>("icon-translate-anchor", Key::IconTranslateAnchor, klass, value); parseOptionalProperty>("text-opacity", Key::TextOpacity, klass, value); parseOptionalProperty("text-opacity-transition", Key::TextOpacity, klass, value); parseOptionalProperty>("text-size", Key::TextSize, klass, value); parseOptionalProperty("text-size-transition", Key::TextSize, klass, value); parseOptionalProperty>("text-color", Key::TextColor, klass, value); parseOptionalProperty("text-color-transition", Key::TextColor, klass, value); parseOptionalProperty>("text-halo-color", Key::TextHaloColor, klass, value); parseOptionalProperty("text-halo-color-transition", Key::TextHaloColor, klass, value); parseOptionalProperty>("text-halo-width", Key::TextHaloWidth, klass, value); parseOptionalProperty("text-halo-width-transition", Key::TextHaloWidth, klass, value); parseOptionalProperty>("text-halo-blur", Key::TextHaloBlur, klass, value); parseOptionalProperty("text-halo-blur-transition", Key::TextHaloBlur, klass, value); parseOptionalProperty>>("text-translate", Key::TextTranslate, klass, value); parseOptionalProperty("text-translate-transition", Key::TextTranslate, klass, value); parseOptionalProperty>("text-translate-anchor", Key::TextTranslateAnchor, klass, value); parseOptionalProperty>("raster-opacity", Key::RasterOpacity, klass, value); parseOptionalProperty("raster-opacity-transition", Key::RasterOpacity, klass, value); parseOptionalProperty>("raster-hue-rotate", Key::RasterHueRotate, klass, value); parseOptionalProperty("raster-hue-rotate-transition", Key::RasterHueRotate, klass, value); parseOptionalProperty>("raster-brightness-min", Key::RasterBrightnessLow, klass, value); parseOptionalProperty>("raster-brightness-max", Key::RasterBrightnessHigh, klass, value); parseOptionalProperty("raster-brightness-transition", Key::RasterBrightness, klass, value); parseOptionalProperty>("raster-saturation", Key::RasterSaturation, klass, value); parseOptionalProperty("raster-saturation-transition", Key::RasterSaturation, klass, value); parseOptionalProperty>("raster-contrast", Key::RasterContrast, klass, value); parseOptionalProperty("raster-contrast-transition", Key::RasterContrast, klass, value); parseOptionalProperty>("raster-fade-duration", Key::RasterFade, klass, value); parseOptionalProperty("raster-fade-duration-transition", Key::RasterFade, klass, value); parseOptionalProperty>("background-opacity", Key::BackgroundOpacity, klass, value); parseOptionalProperty>("background-color", Key::BackgroundColor, klass, value); parseOptionalProperty>>("background-image", Key::BackgroundImage, klass, value, "background-image-transition"); } 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::IconOffset, 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::TextOffset, 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()); return; } const std::string ref { value.GetString(), value.GetStringLength() }; auto it = layersMap.find(ref); if (it == layersMap.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 reference = it->second.second; layer->type = reference->type; layer->bucket = reference->bucket; } #pragma mark - Parse Bucket void StyleParser::parseBucket(JSVal value, util::ptr &layer) { util::ptr bucket = std::make_shared(layer->type); // We name the buckets according to the layer that defined it. 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()) { 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()) { 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"]); bucket->filter = parseFilterExpression(value_filter); } if (value.HasMember("layout")) { JSVal value_render = replaceConstant(value["layout"]); parseLayout(value_render, bucket); } if (value.HasMember("minzoom")) { JSVal min_zoom = value["minzoom"]; if (min_zoom.IsNumber()) { 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()) { bucket->max_zoom = max_zoom.GetDouble(); } else { Log::Warning(Event::ParseStyle, "maxzoom of layer %s must be numeric", layer->id.c_str()); } } layer->bucket = bucket; } 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() }; } } }