optional<Function<T>> parseFunction(const char* name, const JSVal& value) { if (!value.IsObject()) { auto constant = parseProperty<T>(name, value); if (!constant) { return {}; } return { Function<T>(*constant) }; } if (!value.HasMember("stops")) { Log::Warning(Event::ParseStyle, "function must specify a function type"); return {}; } float base = 1.0f; if (value.HasMember("base")) { const JSVal& value_base = value["base"]; if (!value_base.IsNumber()) { Log::Warning(Event::ParseStyle, "base must be numeric"); return {}; } base = value_base.GetDouble(); } auto stops = parseStops<T>(name, value["stops"]); if (!stops) { return {}; } return { Function<T>(*stops, base) }; }
optional<float> parseProperty(const char* name, const JSVal& value) { if (!value.IsNumber()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a number, or a number function", name); return {}; } return value.GetDouble(); }
optional<RotateAnchorType> parseProperty<RotateAnchorType>(const char* name, const JSVal& value) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); return {}; } return { RotateAnchorTypeClass({ value.GetString(), value.GetStringLength() }) }; }
optional<bool> parseProperty(const char* name, const JSVal& value) { if (!value.IsBool()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a boolean", name); return {}; } return value.GetBool(); }
optional<std::array<float, 2>> parseProperty(const char* name, const JSVal& value) { 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 { {{ first, second }} }; } else { Log::Warning(Event::ParseStyle, "value of '%s' must be an array of two numbers", name); return {}; } }
void StyleParser::parseLayers(const JSVal& value) { std::vector<std::string> ids; if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "layers must be an array"); return; } for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { const 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; } const 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; } layersMap.emplace(layerID, std::pair<const JSVal&, util::ptr<StyleLayer>> { layerValue, nullptr }); ids.push_back(layerID); } for (const auto& id : ids) { auto it = layersMap.find(id); parseLayer(it->first, it->second.first, it->second.second); if (it->second.second) { layers.emplace_back(it->second.second); } } }
optional<Color> parseProperty(const char* name, const JSVal& value) { if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); return {}; } 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}}; }
optional<PropertyTransition> parseProperty(const char *, const JSVal& value) { PropertyTransition transition; if (value.IsObject()) { bool parsed = false; if (value.HasMember("duration") && value["duration"].IsNumber()) { transition.duration = std::chrono::milliseconds(value["duration"].GetUint()); parsed = true; } if (value.HasMember("delay") && value["delay"].IsNumber()) { transition.delay = std::chrono::milliseconds(value["delay"].GetUint()); parsed = true; } if (!parsed) { return {}; } } return transition; }
void StyleParser::parseVisibility(StyleLayer& layer, const JSVal& value) { if (!value.HasMember("visibility")) { return; } else if (!value["visibility"].IsString()) { Log::Warning(Event::ParseStyle, "value of 'visibility' must be a string"); layer.visibility = VisibilityType::Visible; return; } layer.visibility = VisibilityTypeClass({ value["visibility"].GetString(), value["visibility"].GetStringLength() }); }
optional<Function<Faded<std::string>>> parseProperty(const char* name, const JSVal& value) { if (value.IsObject()) { return parseFadedFunction<std::string>(value); } auto constant = parseProperty<std::string>(name, value); if (!constant) { return {}; } return Function<Faded<std::string>>(*constant); }
optional<std::vector<float>> parseProperty(const char* name, const JSVal& value) { if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers", name); return {}; } std::vector<float> result; for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { const JSVal& part = value[i]; if (!part.IsNumber()) { Log::Warning(Event::ParseStyle, "value of '%s' must be an array of numbers", name); return {}; } result.push_back(part.GetDouble()); } return result; }
optional<std::string> parseProperty(const char* name, const JSVal& value) { if (std::string { "text-font" } == name) { if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "value of '%s' must be an array of strings", name); return {}; } std::string result = ""; for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { const JSVal& stop = value[i]; if (stop.IsString()) { result += stop.GetString(); if (i < value.Size()-1) { result += ","; } } else { Log::Warning(Event::ParseStyle, "text-font members must be strings"); return {}; } } return result; } if (!value.IsString()) { Log::Warning(Event::ParseStyle, "value of '%s' must be a string", name); return {}; } return std::string { value.GetString(), value.GetStringLength() }; }
optional<Function<Faded<T>>> parseFadedFunction(const JSVal& value) { if (!value.HasMember("stops")) { Log::Warning(Event::ParseStyle, "function must specify a function type"); return {}; } auto stops = parseStops<T>("", value["stops"]); if (!stops) { return {}; } return Function<Faded<T>>(*stops); }
optional<std::vector<std::pair<float, T>>> parseStops(const char* name, const JSVal& value) { if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "stops function must specify a stops array"); return {}; } std::vector<std::pair<float, T>> stops; for (rapidjson::SizeType i = 0; i < value.Size(); ++i) { const JSVal& stop = value[i]; if (!stop.IsArray()) { Log::Warning(Event::ParseStyle, "function argument must be a numeric value"); return {}; } if (stop.Size() != 2) { Log::Warning(Event::ParseStyle, "stop must have zoom level and value specification"); return {}; } const JSVal& z = stop[rapidjson::SizeType(0)]; if (!z.IsNumber()) { Log::Warning(Event::ParseStyle, "zoom level in stop must be a number"); return {}; } optional<T> v = parseProperty<T>(name, stop[rapidjson::SizeType(1)]); if (!v) { return {}; } stops.emplace_back(z.GetDouble(), *v); } return stops; }
void StyleParser::parse(const JSVal& document) { if (document.HasMember("version")) { version = document["version"].GetInt(); if (version != 8) { Log::Warning(Event::ParseStyle, "current renderer implementation only supports style spec version 8; using an outdated style will cause rendering errors"); } } if (document.HasMember("sources")) { parseSources(document["sources"]); } if (document.HasMember("layers")) { parseLayers(document["layers"]); } if (document.HasMember("sprite")) { parseSprite(document["sprite"]); } if (document.HasMember("glyphs")) { parseGlyphURL(document["glyphs"]); } }
void StyleParser::parseSources(const JSVal& value) { if (!value.IsObject()) { Log::Warning(Event::ParseStyle, "sources must be an object"); return; } rapidjson::Value::ConstMemberIterator itr = value.MemberBegin(); for (; itr != value.MemberEnd(); ++itr) { const JSVal& nameVal = itr->name; const JSVal& sourceVal = itr->value; std::unique_ptr<Source> source = std::make_unique<Source>(); source->info.source_id = { nameVal.GetString(), nameVal.GetStringLength() }; if (!sourceVal.HasMember("type")) { Log::Warning(Event::ParseStyle, "source must have a type"); continue; } const JSVal& typeVal = sourceVal["type"]; if (!typeVal.IsString()) { Log::Warning(Event::ParseStyle, "source type must have one of the enum values"); continue; } source->info.type = SourceTypeClass({ typeVal.GetString(), typeVal.GetStringLength() }); if (sourceVal.HasMember("url")) { const JSVal& urlVal = sourceVal["url"]; if (!urlVal.IsString()) { Log::Warning(Event::ParseStyle, "source url must be a string"); continue; } source->info.url = { urlVal.GetString(), urlVal.GetStringLength() }; } if (sourceVal.HasMember("tileSize")) { const JSVal& tileSizeVal = sourceVal["tileSize"]; if (!tileSizeVal.IsUint()) { Log::Warning(Event::ParseStyle, "source tileSize must be an unsigned integer"); continue; } unsigned int intValue = tileSizeVal.GetUint(); if (intValue > std::numeric_limits<uint16_t>::max()) { Log::Warning(Event::ParseStyle, "values for tileSize that are larger than %d are not supported", std::numeric_limits<uint16_t>::max()); continue; } source->info.tile_size = intValue; } source->info.parseTileJSONProperties(sourceVal); sourcesMap.emplace(source->info.source_id, source.get()); sources.emplace_back(std::move(source)); } }
void StyleParser::parseGlyphURL(const JSVal& value) { if (value.IsString()) { glyph_url = { value.GetString(), value.GetStringLength() }; } }
void StyleParser::parseSprite(const JSVal& value) { if (value.IsString()) { sprite = { value.GetString(), value.GetStringLength() }; } }
void StyleParser::parseLayer(const std::string& id, const JSVal& value, util::ptr<StyleLayer>& layer) { if (layer) { // 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(), id) != stack.end()) { Log::Warning(Event::ParseStyle, "layer reference of '%s' is circular", id.c_str()); return; } if (value.HasMember("ref")) { // This layer is referencing another layer. Recursively parse that layer. const JSVal& refVal = value["ref"]; if (!refVal.IsString()) { Log::Warning(Event::ParseStyle, "layer ref of '%s' must be a string", id.c_str()); return; } const std::string ref { refVal.GetString(), refVal.GetStringLength() }; auto it = layersMap.find(ref); if (it == layersMap.end()) { Log::Warning(Event::ParseStyle, "layer '%s' references unknown layer %s", id.c_str(), ref.c_str()); return; } // Recursively parse the referenced layer. stack.push_front(id); parseLayer(it->first, it->second.first, it->second.second); stack.pop_front(); util::ptr<StyleLayer> reference = it->second.second; if (!reference) { return; } layer = reference->clone(); layer->id = id; layer->ref = ref; } else { // Otherwise, parse the source/source-layer/filter/render keys to form the bucket. if (!value.HasMember("type")) { Log::Warning(Event::ParseStyle, "layer '%s' is missing a type", id.c_str()); return; } const JSVal& typeVal = value["type"]; if (!typeVal.IsString()) { Log::Warning(Event::ParseStyle, "layer '%s' has an invalid type", id.c_str()); return; } std::string type { typeVal.GetString(), typeVal.GetStringLength() }; StyleLayerType typeClass = StyleLayerTypeClass(type); layer = StyleLayer::create(typeClass); if (!layer) { Log::Warning(Event::ParseStyle, "unknown type '%s' for layer '%s'", type.c_str(), id.c_str()); return; } layer->id = id; layer->type = typeClass; if (value.HasMember("source")) { const JSVal& value_source = value["source"]; if (value_source.IsString()) { layer->source = { value_source.GetString(), value_source.GetStringLength() }; auto source_it = sourcesMap.find(layer->source); if (source_it == sourcesMap.end()) { Log::Warning(Event::ParseStyle, "can't find source '%s' required for layer '%s'", layer->source.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")) { const JSVal& value_source_layer = value["source-layer"]; if (value_source_layer.IsString()) { layer->sourceLayer = { 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")) { layer->filter = parseFilterExpression(value["filter"]); } if (value.HasMember("minzoom")) { const JSVal& min_zoom = value["minzoom"]; if (min_zoom.IsNumber()) { layer->minZoom = min_zoom.GetDouble(); } else { Log::Warning(Event::ParseStyle, "minzoom of layer %s must be numeric", layer->id.c_str()); } } if (value.HasMember("maxzoom")) { const JSVal& max_zoom = value["maxzoom"]; if (max_zoom.IsNumber()) { layer->maxZoom = max_zoom.GetDouble(); } else { Log::Warning(Event::ParseStyle, "maxzoom of layer %s must be numeric", layer->id.c_str()); } } if (value.HasMember("layout")) { parseVisibility(*layer, value["layout"]); layer->parseLayout(value["layout"]); } } layer->parsePaints(value); }