diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index a761d28ab55..28ea69f0072 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include #include #include @@ -207,6 +208,8 @@ TileData::State Source::addTile(const TileID& id, const StyleUpdateParameters& p monitor = std::make_unique(info, normalized_id, parameters.pixelRatio); } else if (info.type == SourceType::Annotations) { monitor = std::make_unique(normalized_id, parameters.data); + } else if (info.type == SourceType::GeoJSON) { + monitor = std::make_unique(info.geojsonvt.get(), normalized_id); } else { Log::Warning(Event::Style, "Source type '%s' is not implemented", SourceTypeClass(info.type).c_str()); return TileData::State::invalid; diff --git a/src/mbgl/map/source_info.cpp b/src/mbgl/map/source_info.cpp index b8ab84b5500..3a79cb9dffb 100644 --- a/src/mbgl/map/source_info.cpp +++ b/src/mbgl/map/source_info.cpp @@ -3,6 +3,8 @@ #include #include +#include + namespace mbgl { namespace { @@ -83,6 +85,9 @@ void parse(const rapidjson::Value& value, std::array& target, const ch } // end namespace +// Destructor in implementation file because header only contains forward declarations. +SourceInfo::~SourceInfo() = default; + void SourceInfo::parseTileJSONProperties(const rapidjson::Value& value) { parse(value, tiles, "tiles"); parse(value, min_zoom, "minzoom"); diff --git a/src/mbgl/map/source_info.hpp b/src/mbgl/map/source_info.hpp index 725e9f42492..5b4e4e2e152 100644 --- a/src/mbgl/map/source_info.hpp +++ b/src/mbgl/map/source_info.hpp @@ -14,10 +14,18 @@ #include #include +namespace mapbox { +namespace geojsonvt { +class GeoJSONVT; +} // namespace geojsonvt +} // namespace mapbox + namespace mbgl { class SourceInfo : private util::noncopyable { public: + ~SourceInfo(); + SourceType type = SourceType::Vector; std::string url; std::vector tiles; @@ -28,6 +36,7 @@ class SourceInfo : private util::noncopyable { std::array center = { { 0, 0, 0 } }; std::array bounds = { { -180, -90, 180, 90 } }; std::string source_id = ""; + std::unique_ptr geojsonvt; void parseTileJSONProperties(const rapidjson::Value&); std::string tileURL(const TileID& id, float pixelRatio) const; diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 483d096a7f1..259517b0a55 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -168,11 +168,10 @@ bool StyleParser::parseGeoJSONSource(Source& source, const JSVal& sourceVal) { if (dataVal.IsString()) { // We need to load an external GeoJSON file source.info.url = { dataVal.GetString(), dataVal.GetStringLength() }; - } else if (dataVal.IsObject()) { // We need to parse dataVal as a GeoJSON object - auto geojsonvt = std::make_unique(mapbox::geojsonvt::Convert::convert(dataVal, 0)); - // TODO + using namespace mapbox::geojsonvt; + source.info.geojsonvt = std::make_unique(Convert::convert(dataVal, 0)); } else { Log::Warning(Event::ParseStyle, "GeoJSON data must be a URL or an object"); return false; diff --git a/src/mbgl/tile/geojson_tile.cpp b/src/mbgl/tile/geojson_tile.cpp new file mode 100644 index 00000000000..9f14780ca40 --- /dev/null +++ b/src/mbgl/tile/geojson_tile.cpp @@ -0,0 +1,134 @@ +#include +#include +#include + +namespace mbgl { + +GeoJSONTileFeature::GeoJSONTileFeature(FeatureType type_, + GeometryCollection&& geometries_, + GeoJSONTileFeature::Tags&& tags_) + : type(type_), geometries(std::move(geometries_)), tags(std::move(tags_)) { +} + +FeatureType GeoJSONTileFeature::getType() const { + return type; +} + +mapbox::util::optional GeoJSONTileFeature::getValue(const std::string& key) const { + auto it = tags.find(key); + if (it != tags.end()) { + return mapbox::util::optional(it->second); + } + return mapbox::util::optional(); +} + +GeometryCollection GeoJSONTileFeature::getGeometries() const { + return geometries; +} + +GeoJSONTileLayer::GeoJSONTileLayer(Features&& features_) : features(std::move(features_)) { +} + +std::size_t GeoJSONTileLayer::featureCount() const { + return features.size(); +} + +util::ptr GeoJSONTileLayer::getFeature(std::size_t i) const { + return features[i]; +} + +GeoJSONTile::GeoJSONTile(std::shared_ptr layer_) : layer(std::move(layer_)) { +} + +util::ptr GeoJSONTile::getLayer(const std::string&) const { + // We're ignoring the layer name because GeoJSON tiles only have one layer. + return layer; +} + +// Converts the geojsonvt::Tile to a a GeoJSONTile. They have a differing internal structure. +std::unique_ptr convertTile(const mapbox::geojsonvt::Tile& tile) { + std::shared_ptr layer; + + if (tile) { + std::vector> features; + std::vector line; + + for (auto& feature : tile.features) { + const FeatureType featureType = + (feature.type == mapbox::geojsonvt::TileFeatureType::Point + ? FeatureType::Point + : (feature.type == mapbox::geojsonvt::TileFeatureType::LineString + ? FeatureType::LineString + : (feature.type == mapbox::geojsonvt::TileFeatureType::Polygon + ? FeatureType::Polygon + : FeatureType::Unknown))); + if (featureType == FeatureType::Unknown) { + continue; + } + + GeometryCollection geometry; + + // Flatten the geometry; GeoJSONVT distinguishes between a Points array and Rings array + // (Points = GeoJSON types Point, MultiPoint, LineString) + // (Rings = GeoJSON types MultiLineString, Polygon, MultiPolygon) + // However, in Mapbox GL, we use one structure for both types, and just have one outer + // element for Points. + if (feature.tileGeometry.is()) { + line.clear(); + for (auto& point : feature.tileGeometry.get()) { + line.emplace_back(point.x, point.y); + } + geometry.emplace_back(std::move(line)); + } else if (feature.tileGeometry.is()) { + for (auto& ring : feature.tileGeometry.get()) { + line.clear(); + for (auto& point : ring) { + line.emplace_back(point.x, point.y); + } + geometry.emplace_back(std::move(line)); + } + } + + GeoJSONTileFeature::Tags tags{ feature.tags.begin(), feature.tags.end() }; + + features.emplace_back(std::make_shared( + featureType, std::move(geometry), std::move(tags))); + } + + layer = std::make_unique(std::move(features)); + } + + return std::make_unique(layer); +} + +GeoJSONTileMonitor::GeoJSONTileMonitor(mapbox::geojsonvt::GeoJSONVT* geojsonvt_, const TileID& id) + : tileID(id), geojsonvt(geojsonvt_) { +} + +GeoJSONTileMonitor::~GeoJSONTileMonitor() = default; + +// A monitor can have its GeoJSONVT object swapped out (e.g. when loading a new GeoJSON file). +// In that case, we're sending new notifications to all observers. +void GeoJSONTileMonitor::setGeoJSONVT(mapbox::geojsonvt::GeoJSONVT* vt) { + // Don't duplicate notifications in case of nil changes. + if (geojsonvt != vt) { + geojsonvt = vt; + update(); + } +} + +void GeoJSONTileMonitor::update() { + if (geojsonvt) { + auto tile = convertTile(geojsonvt->getTile(tileID.z, tileID.x, tileID.y)); + callback(nullptr, std::move(tile), Seconds::zero(), Seconds::zero()); + } +} + +std::unique_ptr +GeoJSONTileMonitor::monitorTile(const GeometryTileMonitor::Callback& cb) { + callback = cb; + update(); + return nullptr; +} + +} // namespace mbgl diff --git a/src/mbgl/tile/geojson_tile.hpp b/src/mbgl/tile/geojson_tile.hpp new file mode 100644 index 00000000000..03551333dc5 --- /dev/null +++ b/src/mbgl/tile/geojson_tile.hpp @@ -0,0 +1,78 @@ +#ifndef MBGL_ANNOTATION_GEOJSON_VT_TILE +#define MBGL_ANNOTATION_GEOJSON_VT_TILE + +#include +#include + +#include + +namespace mapbox { +namespace geojsonvt { +class GeoJSONVT; +} // namespace geojsonvt +} // namespace mapbox + +namespace mbgl { + +// Implements a simple in-memory Tile type that holds GeoJSON values. A GeoJSON tile can only have +// one layer, and it is always returned regardless of which layer is requested. + +class GeoJSONTileFeature : public GeometryTileFeature { +public: + using Tags = std::unordered_map; + + GeoJSONTileFeature(FeatureType, GeometryCollection&&, Tags&& = {}); + FeatureType getType() const override; + mapbox::util::optional getValue(const std::string&) const override; + GeometryCollection getGeometries() const override; + +private: + const FeatureType type; + const GeometryCollection geometries; + const Tags tags; +}; + +class GeoJSONTileLayer : public GeometryTileLayer { +public: + using Features = std::vector>; + + GeoJSONTileLayer(Features&&); + std::size_t featureCount() const override; + util::ptr getFeature(std::size_t) const override; + +private: + const Features features; +}; + +class GeoJSONTile : public GeometryTile { +public: + GeoJSONTile(std::shared_ptr); + util::ptr getLayer(const std::string&) const override; + +private: + const std::shared_ptr layer; +}; + +class GeoJSONTileMonitor : public GeometryTileMonitor { +public: + GeoJSONTileMonitor(mapbox::geojsonvt::GeoJSONVT*, const TileID&); + virtual ~GeoJSONTileMonitor(); + + std::unique_ptr monitorTile(const GeometryTileMonitor::Callback&) override; + + void setGeoJSONVT(mapbox::geojsonvt::GeoJSONVT*); + +private: + void update(); + +public: + const TileID tileID; + +private: + mapbox::geojsonvt::GeoJSONVT* geojsonvt = nullptr; + GeometryTileMonitor::Callback callback; +}; + +} // namespace mbgl + +#endif