#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_); //Signal that the source description needs a reload if (loaded || req) { loaded = false; req.reset(); observer->onSourceDescriptionChanged(base); } } optional GeoJSONSource::Impl::getURL() { return url; } void GeoJSONSource::Impl::setGeoJSON(const GeoJSON& geoJSON) { req.reset(); _setGeoJSON(geoJSON); } //Private implementation void GeoJSONSource::Impl::_setGeoJSON(const GeoJSON& geoJSON) { double scale = util::EXTENT / util::tileSize; cache.clear(); 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); } for (auto const &item : tiles) { GeoJSONTile* geoJSONTile = static_cast(item.second.get()); setTileData(*geoJSONTile, geoJSONTile->id); } } void GeoJSONSource::Impl::setTileData(GeoJSONTile& tile, const OverscaledTileID& tileID) { if (geoJSONOrSupercluster.is()) { tile.updateData(geoJSONOrSupercluster.get()->getTile(tileID.canonical.z, tileID.canonical.x, tileID.canonical.y).features); } else { assert(geoJSONOrSupercluster.is()); tile.updateData(geoJSONOrSupercluster.get()->getTile(tileID.canonical.z, tileID.canonical.x, tileID.canonical.y)); } } 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); auto tilePointer = std::make_unique(tileID, base.getID(), parameters); setTileData(*tilePointer.get(), tileID); return std::move(tilePointer); } } // namespace style } // namespace mbgl