#include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { std::unique_ptr GeoJSONSource::parseGeoJSON(const JSValue& value) { using namespace mapbox::geojsonvt; Options options; options.buffer = util::EXTENT / util::tileSize * 128; options.extent = util::EXTENT; try { return std::make_unique(Convert::convert(value, 0), options); } catch (const std::exception& ex) { Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", ex.what()); // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for // tiles to load. return std::make_unique(std::vector{}, options); } } std::unique_ptr GeoJSONSource::parse(const std::string& id, const JSValue& value) { // We should probably split this up to have URLs in the url property, and actual data // in the data property. Until then, we're going to detect the content based on the // object type. if (!value.HasMember("data")) { Log::Error(Event::ParseStyle, "GeoJSON source must have a data value"); return nullptr; } const JSValue& dataVal = value["data"]; if (dataVal.IsString()) { return std::make_unique(id, std::string(dataVal.GetString(), dataVal.GetStringLength())); } else if (dataVal.IsObject()) { return std::make_unique(id, parseGeoJSON(dataVal)); } else { Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object"); return nullptr; } } GeoJSONSource::GeoJSONSource(std::string id_, variant urlOrGeoJSON_) : Source(SourceType::GeoJSON, std::move(id_)), urlOrGeoJSON(std::move(urlOrGeoJSON_)) { } GeoJSONSource::~GeoJSONSource() = default; void GeoJSONSource::load(FileSource& fileSource) { if (urlOrGeoJSON.is()) { loaded = true; return; } if (req) { return; } const std::string& url = urlOrGeoJSON.get(); req = fileSource.request(Resource::source(url), [this](Response res) { if (res.error) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(res.error->message))); } else if (res.notModified) { return; } else if (res.noContent) { observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error("unexpectedly empty GeoJSON"))); } else { rapidjson::GenericDocument, rapidjson::CrtAllocator> d; d.Parse<0>(res.data->c_str()); if (d.HasParseError()) { std::stringstream message; message << d.GetErrorOffset() << " - " << rapidjson::GetParseError_En(d.GetParseError()); observer->onSourceError(*this, std::make_exception_ptr(std::runtime_error(message.str()))); return; } invalidateTiles(); urlOrGeoJSON = parseGeoJSON(d); loaded = true; observer->onSourceLoaded(*this); } }); } Range GeoJSONSource::getZoomRange() { assert(loaded); return { 0, urlOrGeoJSON.get()->options.maxZoom }; } std::unique_ptr GeoJSONSource::createTile(const OverscaledTileID& tileID, const UpdateParameters& parameters) { assert(loaded); return std::make_unique(tileID, id, parameters, *urlOrGeoJSON.get()); } } // namespace style } // namespace mbgl