#include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { Parser::~Parser() = default; StyleParseResult Parser::parse(const std::string& json) { rapidjson::GenericDocument, rapidjson::CrtAllocator> document; document.Parse<0>(json.c_str()); if (document.HasParseError()) { std::stringstream message; message << document.GetErrorOffset() << " - " << rapidjson::GetParseError_En(document.GetParseError()); return std::make_exception_ptr(std::runtime_error(message.str())); } if (!document.IsObject()) { return std::make_exception_ptr(std::runtime_error("style must be an object")); } if (document.HasMember("version")) { const JSValue& versionValue = document["version"]; const int version = versionValue.IsNumber() ? versionValue.GetInt() : 0; 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("name")) { const JSValue& value = document["name"]; if (value.IsString()) { name = { value.GetString(), value.GetStringLength() }; } } if (document.HasMember("center")) { const JSValue& value = document["center"]; if (value.IsArray() && value.Size() >= 2) { // Style spec uses lon/lat order latLng = LatLng(value[1].IsNumber() ? value[1].GetDouble() : 0, value[0].IsNumber() ? value[0].GetDouble() : 0); } } if (document.HasMember("zoom")) { const JSValue& value = document["zoom"]; if (value.IsNumber()) { zoom = value.GetDouble(); } } if (document.HasMember("bearing")) { const JSValue& value = document["bearing"]; if (value.IsNumber()) { bearing = value.GetDouble(); } } if (document.HasMember("pitch")) { const JSValue& value = document["pitch"]; if (value.IsNumber()) { pitch = value.GetDouble(); } } if (document.HasMember("sources")) { parseSources(document["sources"]); } if (document.HasMember("layers")) { parseLayers(document["layers"]); } if (document.HasMember("sprite")) { const JSValue& sprite = document["sprite"]; if (sprite.IsString()) { spriteURL = { sprite.GetString(), sprite.GetStringLength() }; } } if (document.HasMember("glyphs")) { const JSValue& glyphs = document["glyphs"]; if (glyphs.IsString()) { glyphURL = { glyphs.GetString(), glyphs.GetStringLength() }; } } return nullptr; } void Parser::parseSources(const JSValue& value) { if (!value.IsObject()) { Log::Warning(Event::ParseStyle, "sources must be an object"); return; } for (const auto& property : value.GetObject()) { std::string id = *conversion::toString(property.name); conversion::Error error; optional> source = conversion::convert>(property.value, error, id); if (!source) { Log::Warning(Event::ParseStyle, error.message); continue; } sourcesMap.emplace(id, (*source).get()); sources.emplace_back(std::move(*source)); } } void Parser::parseLayers(const JSValue& value) { std::vector ids; if (!value.IsArray()) { Log::Warning(Event::ParseStyle, "layers must be an array"); return; } for (auto& layerValue : value.GetArray()) { 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 JSValue& 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> { 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); } for (const auto& id : ids) { auto it = layersMap.find(id); if (it->second.second) { layers.emplace_back(std::move(it->second.second)); } } } void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique_ptr& 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 JSValue& 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(); Layer* reference = it->second.second.get(); if (!reference) { return; } layer = reference->baseImpl->cloneRef(id); conversion::setPaintProperties(*layer, value); } else { conversion::Error error; optional> converted = conversion::convert>(value, error); if (!converted) { Log::Warning(Event::ParseStyle, error.message); return; } layer = std::move(*converted); } } std::vector Parser::fontStacks() const { std::set optional; for (const auto& layer : layers) { if (layer->is()) { PropertyValue textFont = layer->as()->getTextFont(); if (textFont.isUndefined()) { optional.insert({"Open Sans Regular", "Arial Unicode MS Regular"}); } else if (textFont.isConstant()) { optional.insert(textFont.asConstant()); } else if (textFont.isCameraFunction()) { textFont.asCameraFunction().stops.match( [&] (const auto& stops) { for (const auto& stop : stops.stops) { optional.insert(stop.second); } } ); } } } return std::vector(optional.begin(), optional.end()); } } // namespace style } // namespace mbgl