#include #include #include #include #include #include #include #include #include #include #include #include #include #include namespace mbgl { namespace style { namespace conversion { template <> Result convertGeoJSON(const JSValue& value) { try { return mapbox::geojson::convert(value); } catch (const std::exception& ex) { return Error{ ex.what() }; } } } // namespace conversion GeoJSONSource::Impl::Impl(std::string id_, Source& base_, const GeoJSONOptions options_) : Source::Impl(SourceType::GeoJSON, std::move(id_), base_), options(options_) { } GeoJSONSource::Impl::~Impl() = default; void GeoJSONSource::Impl::setURL(std::string url_) { url = std::move(url_); } optional GeoJSONSource::Impl::getURL() { return url; } void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { double scale = util::EXTENT / util::tileSize; if (!options.cluster) { mapbox::geojsonvt::Options vtOptions; vtOptions.maxZoom = options.maxzoom; vtOptions.extent = util::EXTENT; vtOptions.buffer = std::round(scale * options.buffer); vtOptions.tolerance = scale * options.tolerance; geoJSONOrSupercluster = std::make_unique(geoJSON, vtOptions); } else { mapbox::supercluster::Options clusterOptions; clusterOptions.maxZoom = options.clusterMaxZoom; clusterOptions.extent = util::EXTENT; clusterOptions.radius = std::round(scale * options.clusterRadius); const auto& features = geoJSON.get>(); geoJSONOrSupercluster = std::make_unique(features, clusterOptions); } } void GeoJSONSource::Impl::loadDescription(FileSource& fileSource) { if (!url) { loaded = true; return; } if (req) { return; } 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(); conversion::Result geoJSON = conversion::convertGeoJSON(d); if (!geoJSON) { Log::Error(Event::ParseStyle, "Failed to parse GeoJSON data: %s", geoJSON.error().message.c_str()); // Create an empty GeoJSON VT object to make sure we're not infinitely waiting for // tiles to load. setGeoJSON(GeoJSON{ FeatureCollection{} }); } else { setGeoJSON(*geoJSON); } loaded = true; observer->onSourceLoaded(base); } }); } Range GeoJSONSource::Impl::getZoomRange() { assert(loaded); return { 0, options.maxzoom }; } std::unique_ptr GeoJSONSource::Impl::createTile(const OverscaledTileID& tileID, const UpdateParameters& parameters) { assert(loaded); if (geoJSONOrSupercluster.is()) { return std::make_unique(tileID, base.getID(), parameters, geoJSONOrSupercluster.get()->getTile( tileID.canonical.z, tileID.canonical.x, tileID.canonical.y).features); } else { assert(geoJSONOrSupercluster.is()); return std::make_unique(tileID, base.getID(), parameters, geoJSONOrSupercluster.get()->getTile( tileID.canonical.z, tileID.canonical.x, tileID.canonical.y)); } } } // namespace style } // namespace mbgl