#include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { std::unique_ptr GeoJSONSource::Impl::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::Impl::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([&] (Source& base) { return std::make_unique(id, base, std::string(dataVal.GetString(), dataVal.GetStringLength())); }); } else if (dataVal.IsObject()) { return std::make_unique([&] (Source& base) { return std::make_unique(id, base, parseGeoJSON(dataVal)); }); } else { Log::Error(Event::ParseStyle, "GeoJSON data must be a URL or an object"); return nullptr; } } GeoJSONSource::Impl::Impl(std::string id_, Source& base_, variant urlOrGeoJSON_) : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), urlOrGeoJSON(std::move(urlOrGeoJSON_)) { } GeoJSONSource::Impl::~Impl() = default; void GeoJSONSource::Impl::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(base, std::make_exception_ptr(std::runtime_error(res.error->message))); } else if (res.notModified) { return; } else if (res.noContent) { observer->onSourceError(base, 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(base, std::make_exception_ptr(std::runtime_error(message.str()))); return; } invalidateTiles(); urlOrGeoJSON = parseGeoJSON(d); loaded = true; observer->onSourceLoaded(base); } }); } Range GeoJSONSource::Impl::getZoomRange() { assert(loaded); return { 0, urlOrGeoJSON.get()->options.maxZoom }; } std::unique_ptr GeoJSONSource::Impl::createTile(const OverscaledTileID& tileID, const UpdateParameters& parameters) { assert(loaded); return std::make_unique(tileID, base.getID(), parameters, *urlOrGeoJSON.get()); } } // namespace style } // namespace mbgl