diff options
author | Konstantin Käfer <mail@kkaefer.com> | 2015-12-09 16:24:51 -0800 |
---|---|---|
committer | Konstantin Käfer <mail@kkaefer.com> | 2015-12-11 16:23:42 -0800 |
commit | 0f9922cff59772375d81202a43284b732b6aacb3 (patch) | |
tree | c2fb557962ddbaddf7269ac39e55b02a96c3e63d | |
parent | 2f973d0a7e29bdaaa0fc8f2b13d040ef0c189544 (diff) | |
download | qtlocation-mapboxgl-0f9922cff59772375d81202a43284b732b6aacb3.tar.gz |
[core] create GeoJSON tiles from inline GeoJSON in sources
-rw-r--r-- | src/mbgl/map/source.cpp | 3 | ||||
-rw-r--r-- | src/mbgl/map/source_info.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/map/source_info.hpp | 9 | ||||
-rw-r--r-- | src/mbgl/style/style_parser.cpp | 5 | ||||
-rw-r--r-- | src/mbgl/tile/geojson_tile.cpp | 134 | ||||
-rw-r--r-- | src/mbgl/tile/geojson_tile.hpp | 78 |
6 files changed, 231 insertions, 3 deletions
diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index a761d28ab5..28ea69f007 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -4,6 +4,7 @@ #include <mbgl/map/tile.hpp> #include <mbgl/map/vector_tile.hpp> #include <mbgl/annotation/annotation_tile.hpp> +#include <mbgl/tile/geojson_tile.hpp> #include <mbgl/renderer/painter.hpp> #include <mbgl/util/exception.hpp> #include <mbgl/util/constants.hpp> @@ -207,6 +208,8 @@ TileData::State Source::addTile(const TileID& id, const StyleUpdateParameters& p monitor = std::make_unique<VectorTileMonitor>(info, normalized_id, parameters.pixelRatio); } else if (info.type == SourceType::Annotations) { monitor = std::make_unique<AnnotationTileMonitor>(normalized_id, parameters.data); + } else if (info.type == SourceType::GeoJSON) { + monitor = std::make_unique<GeoJSONTileMonitor>(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 b8ab84b550..3a79cb9dff 100644 --- a/src/mbgl/map/source_info.cpp +++ b/src/mbgl/map/source_info.cpp @@ -3,6 +3,8 @@ #include <mbgl/util/string.hpp> #include <mbgl/util/token.hpp> +#include <mapbox/geojsonvt.hpp> + namespace mbgl { namespace { @@ -83,6 +85,9 @@ void parse(const rapidjson::Value& value, std::array<float, N>& 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 725e9f4249..5b4e4e2e15 100644 --- a/src/mbgl/map/source_info.hpp +++ b/src/mbgl/map/source_info.hpp @@ -14,10 +14,18 @@ #include <vector> #include <cstdint> +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<std::string> tiles; @@ -28,6 +36,7 @@ public: std::array<float, 3> center = { { 0, 0, 0 } }; std::array<float, 4> bounds = { { -180, -90, 180, 90 } }; std::string source_id = ""; + std::unique_ptr<mapbox::geojsonvt::GeoJSONVT> 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 483d096a7f..259517b0a5 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::GeoJSONVT>(mapbox::geojsonvt::Convert::convert(dataVal, 0)); - // TODO + using namespace mapbox::geojsonvt; + source.info.geojsonvt = std::make_unique<GeoJSONVT>(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 0000000000..9f14780ca4 --- /dev/null +++ b/src/mbgl/tile/geojson_tile.cpp @@ -0,0 +1,134 @@ +#include <mbgl/tile/geojson_tile.hpp> +#include <mbgl/storage/file_source.hpp> +#include <mapbox/geojsonvt.hpp> + +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<Value> GeoJSONTileFeature::getValue(const std::string& key) const { + auto it = tags.find(key); + if (it != tags.end()) { + return mapbox::util::optional<Value>(it->second); + } + return mapbox::util::optional<Value>(); +} + +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<const GeometryTileFeature> GeoJSONTileLayer::getFeature(std::size_t i) const { + return features[i]; +} + +GeoJSONTile::GeoJSONTile(std::shared_ptr<GeoJSONTileLayer> layer_) : layer(std::move(layer_)) { +} + +util::ptr<GeometryTileLayer> 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<GeoJSONTile> convertTile(const mapbox::geojsonvt::Tile& tile) { + std::shared_ptr<GeoJSONTileLayer> layer; + + if (tile) { + std::vector<std::shared_ptr<const GeoJSONTileFeature>> features; + std::vector<Coordinate> 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<mapbox::geojsonvt::TilePoints>()) { + line.clear(); + for (auto& point : feature.tileGeometry.get<mapbox::geojsonvt::TilePoints>()) { + line.emplace_back(point.x, point.y); + } + geometry.emplace_back(std::move(line)); + } else if (feature.tileGeometry.is<mapbox::geojsonvt::TileRings>()) { + for (auto& ring : feature.tileGeometry.get<mapbox::geojsonvt::TileRings>()) { + 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<GeoJSONTileFeature>( + featureType, std::move(geometry), std::move(tags))); + } + + layer = std::make_unique<GeoJSONTileLayer>(std::move(features)); + } + + return std::make_unique<GeoJSONTile>(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<FileRequest> +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 0000000000..e5e9766a17 --- /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 <mbgl/map/geometry_tile.hpp> +#include <mbgl/map/tile_id.hpp> + +#include <unordered_map> + +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<std::string, std::string>; + + GeoJSONTileFeature(FeatureType, GeometryCollection&&, Tags&& = Tags{}); + FeatureType getType() const override; + mapbox::util::optional<Value> 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<std::shared_ptr<const GeoJSONTileFeature>>; + + GeoJSONTileLayer(Features&&); + std::size_t featureCount() const override; + util::ptr<const GeometryTileFeature> getFeature(std::size_t) const override; + +private: + const Features features; +}; + +class GeoJSONTile : public GeometryTile { +public: + GeoJSONTile(std::shared_ptr<GeoJSONTileLayer>); + util::ptr<GeometryTileLayer> getLayer(const std::string&) const override; + +private: + const std::shared_ptr<GeoJSONTileLayer> layer; +}; + +class GeoJSONTileMonitor : public GeometryTileMonitor { +public: + GeoJSONTileMonitor(mapbox::geojsonvt::GeoJSONVT*, const TileID&); + virtual ~GeoJSONTileMonitor(); + + std::unique_ptr<FileRequest> 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 |