#include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace conversion { namespace { // A tile source can either specify a URL to TileJSON, or inline TileJSON. std::optional> convertURLOrTileset(const Convertible& value, Error& error) { auto urlVal = objectMember(value, "url"); if (!urlVal) { std::optional tileset = convert(value, error); if (!tileset) { return std::nullopt; } return {std::move(*tileset)}; } std::optional url = toString(*urlVal); if (!url) { error.message = "source url must be a string"; return std::nullopt; } return {std::move(*url)}; } std::optional> convertRasterSource(const std::string& id, const Convertible& value, Error& error) { std::optional> urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { return std::nullopt; } uint16_t tileSize = util::tileSize_I; auto tileSizeValue = objectMember(value, "tileSize"); if (tileSizeValue) { std::optional size = toNumber(*tileSizeValue); if (!size || *size < 0 || *size > std::numeric_limits::max()) { error.message = "invalid tileSize"; return std::nullopt; } tileSize = static_cast(*size); } return {std::make_unique(id, std::move(*urlOrTileset), tileSize)}; } std::optional> convertRasterDEMSource(const std::string& id, const Convertible& value, Error& error) { std::optional> urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { return std::nullopt; } uint16_t tileSize = util::tileSize_I; auto tileSizeValue = objectMember(value, "tileSize"); if (tileSizeValue) { std::optional size = toNumber(*tileSizeValue); if (!size || *size < 0 || *size > std::numeric_limits::max()) { error.message = "invalid tileSize"; return std::nullopt; } tileSize = static_cast(*size); } /* TODO: Refactor TileJSON parsing responsibilities. Currently `Tileset` handles full TileJSON parsing with optional parameters. Since style.json can contain TileJSON parameter overrides or non-TileJSON spec parameters, a separate mechanism is needed. Ideally these two pathways share parsing logic. */ auto options = convert(value, error); return {std::make_unique(id, std::move(*urlOrTileset), tileSize, options)}; } std::optional> convertVectorSource(const std::string& id, const Convertible& value, Error& error) { auto urlOrTileset = convertURLOrTileset(value, error); if (!urlOrTileset) { return std::nullopt; } const auto maxzoomValue = objectMember(value, "maxzoom"); std::optional maxzoom; if (maxzoomValue) { maxzoom = toNumber(*maxzoomValue); if (!maxzoom || *maxzoom < 0 || *maxzoom > std::numeric_limits::max()) { error.message = "invalid maxzoom"; return std::nullopt; } } const auto minzoomValue = objectMember(value, "minzoom"); std::optional minzoom; if (minzoomValue) { minzoom = toNumber(*minzoomValue); if (!minzoom || *minzoom < 0 || *minzoom > std::numeric_limits::max()) { error.message = "invalid minzoom"; return std::nullopt; } } const auto encodingValue = objectMember(value, "encoding"); auto encoding = Tileset::VectorEncoding::Mapbox; if (encodingValue) { const auto encodingStr = toString(*encodingValue); if (encodingStr && encodingStr == "mvt") { encoding = Tileset::VectorEncoding::Mapbox; } else if (encodingStr && encodingStr == "mlt") { encoding = Tileset::VectorEncoding::MLT; } else { error.message = "invalid encoding"; return std::nullopt; } } return {std::make_unique(id, std::move(*urlOrTileset), maxzoom, minzoom, encoding)}; } std::optional> convertGeoJSONSource(const std::string& id, const Convertible& value, Error& error) { auto dataValue = objectMember(value, "data"); if (!dataValue) { error.message = "GeoJSON source must have a data value"; return std::nullopt; } Immutable options = GeoJSONOptions::defaultOptions(); if (std::optional converted = convert(value, error)) { options = makeMutable(std::move(*converted)); } auto result = std::make_unique(id, std::move(options)); if (isObject(*dataValue)) { auto geoJSON = convert(*dataValue, error); if (!geoJSON) { return std::nullopt; } result->setGeoJSON(*geoJSON); } else if (toString(*dataValue)) { result->setURL(*toString(*dataValue)); } else { error.message = "GeoJSON data must be a URL or an object"; return std::nullopt; } return {std::move(result)}; } std::optional> convertImageSource(const std::string& id, const Convertible& value, Error& error) { auto urlValue = objectMember(value, "url"); if (!urlValue) { error.message = "Image source must have a url value"; return std::nullopt; } auto urlString = toString(*urlValue); if (!urlString) { error.message = "Image url must be a URL string"; return std::nullopt; } auto coordinatesValue = objectMember(value, "coordinates"); if (!coordinatesValue) { error.message = "Image source must have a coordinates values"; return std::nullopt; } if (!isArray(*coordinatesValue) || arrayLength(*coordinatesValue) != 4) { error.message = "Image coordinates must be an array of four longitude latitude " "pairs"; return std::nullopt; } std::array coordinates; for (std::size_t i = 0; i < 4; i++) { auto latLng = conversion::convert(arrayMember(*coordinatesValue, i), error); if (!latLng) { return std::nullopt; } coordinates[i] = *latLng; } auto result = std::make_unique(id, coordinates); result->setURL(*urlString); return {std::move(result)}; } } // namespace std::optional> Converter>::operator()(const Convertible& value, Error& error, const std::string& id) const { if (!isObject(value)) { error.message = "source must be an object"; return std::nullopt; } auto typeValue = objectMember(value, "type"); if (!typeValue) { error.message = "source must have a type"; return std::nullopt; } std::optional type = toString(*typeValue); if (!type) { error.message = "source type must be a string"; return std::nullopt; } const std::string& tname = type.value(); if (tname == "raster") { return convertRasterSource(id, value, error); } else if (tname == "raster-dem") { return convertRasterDEMSource(id, value, error); } else if (tname == "vector") { return convertVectorSource(id, value, error); } else if (tname == "geojson") { return convertGeoJSONSource(id, value, error); } else if (tname == "image") { return convertImageSource(id, value, error); } else { error.message = "invalid source type"; return std::nullopt; } } } // namespace conversion } // namespace style } // namespace mbgl