diff options
author | Justin R. Miller <incanus@codesorcery.net> | 2015-06-15 18:30:39 -0700 |
---|---|---|
committer | Justin R. Miller <incanus@codesorcery.net> | 2015-06-15 18:30:39 -0700 |
commit | bd0bf294bf1e350b184e54a824430c911dd66b9b (patch) | |
tree | d7c643cf197a8263fb1880ce73d034da253efe4c /src | |
parent | e75e538ddbb1dad0baad02576df86f6b8eb1510f (diff) | |
download | qtlocation-mapboxgl-bd0bf294bf1e350b184e54a824430c911dd66b9b.tar.gz |
squash of #1655: shape annotations support for core & iOS
Diffstat (limited to 'src')
-rw-r--r-- | src/mbgl/map/annotation.cpp | 582 | ||||
-rw-r--r-- | src/mbgl/map/annotation.hpp | 69 | ||||
-rw-r--r-- | src/mbgl/map/live_tile.cpp | 7 | ||||
-rw-r--r-- | src/mbgl/map/live_tile.hpp | 7 | ||||
-rw-r--r-- | src/mbgl/map/map.cpp | 24 | ||||
-rw-r--r-- | src/mbgl/map/map_context.cpp | 114 | ||||
-rw-r--r-- | src/mbgl/map/map_context.hpp | 6 | ||||
-rw-r--r-- | src/mbgl/map/source.cpp | 13 | ||||
-rw-r--r-- | src/mbgl/map/source.hpp | 4 | ||||
-rw-r--r-- | src/mbgl/style/style_parser.cpp | 46 | ||||
-rw-r--r-- | src/mbgl/style/style_properties.cpp | 1 | ||||
-rw-r--r-- | src/mbgl/style/style_properties.hpp | 115 | ||||
-rw-r--r-- | src/mbgl/style/types.hpp | 210 | ||||
m--------- | src/mbgl/util/geojsonvt | 0 |
14 files changed, 696 insertions, 502 deletions
diff --git a/src/mbgl/map/annotation.cpp b/src/mbgl/map/annotation.cpp index 6df49ec647..c946264593 100644 --- a/src/mbgl/map/annotation.cpp +++ b/src/mbgl/map/annotation.cpp @@ -4,6 +4,7 @@ #include <mbgl/map/live_tile.hpp> #include <mbgl/map/map_data.hpp> #include <mbgl/util/constants.hpp> +#include <mbgl/util/geojsonvt/geojsonvt_convert.hpp> #include <mbgl/util/ptr.hpp> #include <algorithm> @@ -11,35 +12,15 @@ namespace mbgl { -enum class AnnotationType : uint8_t { - Point, - Shape -}; - -using AnnotationSegment = std::vector<LatLng>; -using AnnotationSegments = std::vector<AnnotationSegment>; - -class Annotation : private util::noncopyable { - friend class AnnotationManager; -public: - Annotation(AnnotationType, const AnnotationSegments&); - -private: - LatLng getPoint() const; - LatLngBounds getBounds() const { return bounds; } - -private: - const AnnotationType type = AnnotationType::Point; - const AnnotationSegments geometry; - std::unordered_map<TileID, std::weak_ptr<const LiveTileFeature>, TileID::Hash> tileFeatures; - const LatLngBounds bounds; -}; - -Annotation::Annotation(AnnotationType type_, const AnnotationSegments& geometry_) - : type(type_), +Annotation::Annotation(AnnotationType type_, + const AnnotationSegments& geometry_, + const StyleProperties& styleProperties_) + : styleProperties(styleProperties_), + type(type_), geometry(geometry_), bounds([this] { LatLngBounds bounds_; + assert(type != AnnotationType::Any); if (type == AnnotationType::Point) { bounds_ = { getPoint(), getPoint() }; } else { @@ -67,6 +48,16 @@ AnnotationManager::~AnnotationManager() { // Annotation so we can't destruct the object with just the header file. } +void AnnotationManager::markStaleTiles(std::unordered_set<TileID, TileID::Hash> ids) { + std::lock_guard<std::mutex> lock(mtx); + std::copy(ids.begin(), ids.end(), std::inserter(staleTiles, staleTiles.begin())); +} + +std::unordered_set<TileID, TileID::Hash> AnnotationManager::resetStaleTiles() { + std::lock_guard<std::mutex> lock(mtx); + return std::move(staleTiles); +} + void AnnotationManager::setDefaultPointAnnotationSymbol(const std::string& symbol) { std::lock_guard<std::mutex> lock(mtx); defaultPointAnnotationSymbol = symbol; @@ -77,118 +68,314 @@ uint32_t AnnotationManager::nextID() { } vec2<double> AnnotationManager::projectPoint(const LatLng& point) { + // Clamp to the latitude limits of Mercator. + const double constrainedLatitude = std::fmin(std::fmax(point.latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + // Project a coordinate into unit space in a square map. - const double sine = std::sin(point.latitude * M_PI / 180.0); + const double sine = std::sin(constrainedLatitude * M_PI / 180.0); const double x = point.longitude / 360.0 + 0.5; const double y = 0.5 - 0.25 * std::log((1.0 + sine) / (1.0 - sine)) / M_PI; return { x, y }; } -std::pair<std::vector<TileID>, AnnotationIDs> -AnnotationManager::addPointAnnotations(const std::vector<LatLng>& points, - const std::vector<std::string>& symbols, - const MapData& data) { +std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> +AnnotationManager::addAnnotations(const AnnotationType type, + const std::vector<AnnotationSegments>& segments, + const std::vector<StyleProperties>& styleProperties, + const AnnotationsProperties& annotationsProperties, + const MapData& data) { std::lock_guard<std::mutex> lock(mtx); + assert(type != AnnotationType::Any); + // We pre-generate tiles to contain each annotation up to the map's max zoom. // We do this for fast rendering without projection conversions on the fly, as well as // to simplify bounding box queries of annotations later. Tiles get invalidated when // annotations are added or removed in order to refresh the map render without // touching the base map underneath. - const uint16_t extent = 4096; + AnnotationIDs annotationIDs; + annotationIDs.reserve((type == AnnotationType::Shape ? + segments.size() : // shapes + segments[0][0].size())); // points + + std::unordered_set<TileID, TileID::Hash> affectedTiles; + + const uint8_t maxZoom = data.transform.getMaxZoom(); + + for (size_t s = 0; s < segments.size(); ++s) { + + if (type == AnnotationType::Point) { - std::vector<uint32_t> annotationIDs; - annotationIDs.reserve(points.size()); + for (size_t l = 0; l < segments[s].size(); ++l) { - std::vector<TileID> affectedTiles; + for (size_t p = 0; p < segments[s][l].size(); ++p) { - for (size_t i = 0; i < points.size(); ++i) { - const uint32_t annotationID = nextID(); + auto& point = segments[s][l][p]; - // track the annotation global ID and its geometry - auto anno_it = annotations.emplace( - annotationID, - std::make_unique<Annotation>(AnnotationType::Point, - AnnotationSegments({ { points[i] } }))); + // projection conversion into unit space + const auto pp = projectPoint(point); + + const uint32_t pointAnnotationID = nextID(); + + // at render time we style the point according to its {sprite} field + std::unordered_map<std::string, std::string> pointFeatureProperties; + const std::string& symbol = annotationsProperties.at("symbols")[p]; + if (symbol.length()) { + pointFeatureProperties.emplace("sprite", symbol); + } else { + pointFeatureProperties.emplace("sprite", defaultPointAnnotationSymbol); + } + + // add individual point tile feature + auto featureAffectedTiles = addTileFeature( + pointAnnotationID, + AnnotationSegments({{ point }}), + std::vector<std::vector<vec2<double>>>({{ pp }}), + AnnotationType::Point, + {{ }}, + pointFeatureProperties, + maxZoom + ); + + std::copy(featureAffectedTiles.begin(), featureAffectedTiles.end(), std::inserter(affectedTiles, affectedTiles.begin())); + + annotationIDs.push_back(pointAnnotationID); + } + } + } else { + + const uint32_t shapeAnnotationID = nextID(); + + // current shape tiles are on-the-fly, so we don't get any "affected tiles" + // and just expire all annotation tiles for shape adds + + addTileFeature( + shapeAnnotationID, + segments[s], + {{ }}, + AnnotationType::Shape, + styleProperties[s], + {{ }}, + maxZoom + ); + + annotationIDs.push_back(shapeAnnotationID); + } + } + + // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. + return std::make_pair(affectedTiles, annotationIDs); +} - const uint8_t maxZoom = data.transform.getMaxZoom(); +std::unordered_set<TileID, TileID::Hash> +AnnotationManager::addTileFeature(const uint32_t annotationID, + const AnnotationSegments& segments, + const std::vector<std::vector<vec2<double>>>& projectedFeature, + const AnnotationType& type, + const StyleProperties& styleProperties, + const std::unordered_map<std::string, std::string>& featureProperties, + const uint8_t maxZoom) { - // side length of map at this zoom + assert(type != AnnotationType::Any); + + // track the annotation global ID and its original geometry + auto anno_it = annotations.emplace(annotationID, + std::make_unique<Annotation>(type, segments, styleProperties)); + + std::unordered_set<TileID, TileID::Hash> affectedTiles; + + if (type == AnnotationType::Shape) { + + orderedShapeAnnotations.push_back(annotationID); + + using namespace mapbox::util::geojsonvt; + + const uint32_t z2 = 1 << maxZoom; + const double baseTolerance = 3; + const uint16_t extent = 4096; + + const double tolerance = baseTolerance / (z2 * extent); + + ProjectedGeometryContainer rings; + + std::vector<LonLat> points; + + for (size_t i = 0; i < segments[0].size(); ++i) { // first segment for now (no holes) + const double constraintedLatitude = std::fmin(std::fmax(segments[0][i].latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + points.push_back(LonLat(segments[0][i].longitude, constraintedLatitude)); + } + + ProjectedFeatureType featureType; + + if (styleProperties.is<FillProperties>()) { + featureType = ProjectedFeatureType::Polygon; + + if (points.front().lon != points.back().lon || points.front().lat != points.back().lat) { + points.push_back(LonLat(points.front().lon, points.front().lat)); + } + } else { + featureType = ProjectedFeatureType::LineString; + } + + ProjectedGeometryContainer ring = Convert::project(points, tolerance); + + rings.members.push_back(ring); + + std::vector<ProjectedFeature> features; + + features.push_back(Convert::create(Tags(), featureType, rings)); + + shapeTilers.emplace(annotationID, std::make_unique<GeoJSONVT>(features, maxZoom)); + + } else { + + // side length of map at max zoom uint32_t z2 = 1 << maxZoom; - // projection conversion into unit space - const vec2<double> p = projectPoint(points[i]); + const uint16_t extent = 4096; - uint32_t x = p.x * z2; - uint32_t y = p.y * z2; + uint32_t x = 0; + uint32_t y = 0; for (int8_t z = maxZoom; z >= 0; z--) { - affectedTiles.emplace_back(z, x, y, z); - TileID tileID = affectedTiles.back(); - - // calculate tile coordinate - const Coordinate coordinate(extent * (p.x * z2 - x), extent * (p.y * z2 - y)); - - const GeometryCollection geometries({ { { { coordinate } } } }); - - // at render time we style the annotation according to its {sprite} field - const std::map<std::string, std::string> properties = { - { "sprite", (symbols[i].length() ? symbols[i] : defaultPointAnnotationSymbol) } - }; - - auto feature = - std::make_shared<const LiveTileFeature>(FeatureType::Point, geometries, properties); - - auto tile_it = tiles.find(tileID); - if (tile_it != tiles.end()) { - // - // We have this tile created already. Add this feature to it. - // - // get point layer & add feature - auto layer = - tile_it->second.second->getMutableLayer(layerID); - layer->addFeature(feature); - // record annotation association with tile - tile_it->second.first.insert(annotationID); + + std::unordered_map<TileID, GeometryCollection, TileID::Hash> featureTiles; + + if (type == AnnotationType::Point) { + auto& pp = projectedFeature[0][0]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + GeometryCollection geometries = {{ {{ coordinate }} }}; + + featureTiles.emplace(TileID(z, x, y, z), geometries); } else { - // - // We need to create a new tile for this feature. - // - // create point layer & add feature - util::ptr<LiveTileLayer> layer = std::make_shared<LiveTileLayer>(); - layer->addFeature(feature); - // create tile & record annotation association - auto tile_pos = tiles.emplace( - tileID, std::make_pair(std::unordered_set<uint32_t>({ annotationID }), - std::make_unique<LiveTile>())); - // add point layer to tile - tile_pos.first->second.second->addLayer(layerID, layer); + for (size_t l = 0; l < projectedFeature.size(); ++l) { + + std::vector<Coordinate> line; + + for (size_t p = 0; p < projectedFeature[l].size(); ++p) { + + auto& pp = projectedFeature[l][p]; + + x = pp.x * z2; + y = pp.y * z2; + + const Coordinate coordinate(extent * (pp.x * z2 - x), extent * (pp.y * z2 - y)); + + auto tile_it = featureTiles.find(TileID(z, x, y, z)); + + if (tile_it != featureTiles.end()) { + GeometryCollection& geometries = featureTiles.find(TileID(z, x, y, z))->second; + if (geometries.size()) { + geometries.back().push_back(coordinate); + } else { + geometries.push_back({{ coordinate }}); + } + } else { + GeometryCollection geometries = {{ {{ coordinate }} }}; + featureTiles.emplace(TileID(z, x, y, z), geometries); + } + } + } } - // Record annotation association with tile and tile feature. This is used to determine stale tiles, - // as well as to remove the feature from the tile upon annotation deletion. - anno_it.first->second->tileFeatures.emplace( - tileID, std::weak_ptr<const LiveTileFeature>(feature)); + for (auto& featureTile : featureTiles) { + // determine feature type + FeatureType featureType; + if (type == AnnotationType::Point) { + featureType = FeatureType::Point; + } else if (styleProperties.is<LineProperties>()) { + featureType = FeatureType::LineString; + } else if (styleProperties.is<FillProperties>()) { + featureType = FeatureType::Polygon; + } else { + throw std::runtime_error("Invalid feature type"); + } + + // create tile feature + auto feature = std::make_shared<const LiveTileFeature>( + featureType, + featureTile.second, + featureProperties + ); + + // check for tile & create if necessary + auto tile_pos = tiles.emplace(featureTile.first, + std::make_pair(std::unordered_set<uint32_t>({ annotationID }), + std::make_unique<LiveTile>())); + + // check for annotation layer & create if necessary + util::ptr<LiveTileLayer> layer; + std::string layerID = ""; + if (type == AnnotationType::Point) { + layerID = PointLayerID; + } else { + layerID = ShapeLayerID + "." + std::to_string(annotationID); + } + if (tile_pos.second || tile_pos.first->second.second->getMutableLayer(layerID) == nullptr) { + layer = std::make_shared<LiveTileLayer>(); + tile_pos.first->second.second->addLayer(layerID, layer); + } else { + layer = tile_pos.first->second.second->getMutableLayer(layerID); + + // associate annotation with tile + tile_pos.first->second.first.insert(annotationID); + } + + // add feature to layer + layer->addFeature(feature); + + // Record annotation association with tile and tile feature. This is used to determine stale tiles, + // as well as to remove the feature from the tile upon annotation deletion. + anno_it.first->second->tilePointFeatures.emplace(featureTile.first, std::weak_ptr<const LiveTileFeature>(feature)); + + // track affected tile + affectedTiles.insert(featureTile.first); + } // get ready for the next-lower zoom number z2 /= 2; x /= 2; y /= 2; } - - annotationIDs.push_back(annotationID); } - // Tile:IDs that need refreshed and the annotation identifiers held onto by the client. - return std::make_pair(affectedTiles, annotationIDs); + return affectedTiles; +} + +std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> +AnnotationManager::addPointAnnotations(const AnnotationSegment& points, + const AnnotationsProperties& annotationsProperties, + const MapData& data) { + return addAnnotations(AnnotationType::Point, + {{ points }}, + {{ }}, + annotationsProperties, + data); } -std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& ids, - const MapData& data) { +std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> +AnnotationManager::addShapeAnnotations(const std::vector<AnnotationSegments>& shapes, + const std::vector<StyleProperties>& styleProperties, + const AnnotationsProperties& annotationsProperties, + const MapData& data) { + return addAnnotations(AnnotationType::Shape, + shapes, + styleProperties, + annotationsProperties, + data); +} + +std::unordered_set<TileID, TileID::Hash> AnnotationManager::removeAnnotations(const AnnotationIDs& ids, + const MapData& data) { std::lock_guard<std::mutex> lock(mtx); - std::vector<TileID> affectedTiles; + std::unordered_set<TileID, TileID::Hash> affectedTiles; std::vector<uint32_t> z2s; const uint8_t zoomCount = data.transform.getMaxZoom() + 1; @@ -207,25 +394,44 @@ std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& id const auto& annotation_it = annotations.find(annotationID); if (annotation_it != annotations.end()) { const auto& annotation = annotation_it->second; - // calculate annotation's affected tile for each zoom - for (uint8_t z = 0; z < zoomCount; ++z) { - latLng = annotation->getPoint(); - p = projectPoint(latLng); - x = z2s[z] * p.x; - y = z2s[z] * p.y; - TileID tid(z, x, y, z); - // erase annotation from tile's list - auto& tileAnnotations = tiles[tid].first; - tileAnnotations.erase(annotationID); - // remove annotation's features from tile - const auto& features_it = annotation->tileFeatures.find(tid); - if (features_it != annotation->tileFeatures.end()) { - const auto& layer = - tiles[tid].second->getMutableLayer(layerID); - layer->removeFeature(features_it->second); - affectedTiles.push_back(tid); + // remove feature(s) from relevant tiles + if (annotation->type == AnnotationType::Point) { + // calculate annotation's affected tile for each zoom + for (uint8_t z = 0; z < zoomCount; ++z) { + latLng = annotation->getPoint(); + p = projectPoint(latLng); + x = z2s[z] * p.x; + y = z2s[z] * p.y; + TileID tid(z, x, y, z); + // erase annotation from tile's list + auto& tileAnnotations = tiles[tid].first; + tileAnnotations.erase(annotationID); + // remove annotation's features from tile + const auto& features_it = annotation->tilePointFeatures.find(tid); + if (features_it != annotation->tilePointFeatures.end()) { + // points share a layer; remove feature + auto layer = tiles[tid].second->getMutableLayer(PointLayerID); + layer->removeFeature(features_it->second); + affectedTiles.insert(tid); + } + } + } else { + // remove shape layer from tiles if relevant + for (auto tile_it = tiles.begin(); tile_it != tiles.end(); ++tile_it) { + if (tile_it->second.first.count(annotationID)) { + tile_it->second.second->removeLayer(ShapeLayerID + "." + std::to_string(annotationID)); + affectedTiles.insert(tile_it->first); + } } + + // clear shape from render order + auto shape_it = std::find(orderedShapeAnnotations.begin(), orderedShapeAnnotations.end(), annotationID); + orderedShapeAnnotations.erase(shape_it); + + // clear shape tiler + shapeTilers.erase(annotationID); } + annotations.erase(annotationID); } } @@ -234,8 +440,18 @@ std::vector<TileID> AnnotationManager::removeAnnotations(const AnnotationIDs& id return affectedTiles; } -std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, - const MapData& data) const { +const StyleProperties AnnotationManager::getAnnotationStyleProperties(uint32_t annotationID) const { + std::lock_guard<std::mutex> lock(mtx); + + auto anno_it = annotations.find(annotationID); + assert(anno_it != annotations.end()); + + return anno_it->second->styleProperties; +} + +AnnotationIDs AnnotationManager::getAnnotationsInBounds(const LatLngBounds& queryBounds, + const MapData& data, + const AnnotationType& type) const { std::lock_guard<std::mutex> lock(mtx); const uint8_t z = data.transform.getMaxZoom(); @@ -247,7 +463,7 @@ std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBoun const TileID nwTile(z, swPoint.x * z2, nePoint.y * z2, z); const TileID seTile(z, nePoint.x * z2, swPoint.y * z2, z); - std::vector<uint32_t> matchingAnnotations; + std::unordered_set<uint32_t> matchingAnnotations; for (auto& tile : tiles) { TileID id = tile.first; @@ -255,32 +471,50 @@ std::vector<uint32_t> AnnotationManager::getAnnotationsInBounds(const LatLngBoun if (id.x >= nwTile.x && id.x <= seTile.x && id.y >= nwTile.y && id.y <= seTile.y) { if (id.x > nwTile.x && id.x < seTile.x && id.y > nwTile.y && id.y < seTile.y) { // Trivial accept; this tile is completely inside the query bounds, so - // we'll return all of its annotations. - std::copy(tile.second.first.begin(), tile.second.first.end(), - std::back_inserter(matchingAnnotations)); + // we'll return all of its annotations that match type (if specified). + if (type != AnnotationType::Any) { + std::copy_if(tile.second.first.begin(), tile.second.first.end(), + std::inserter(matchingAnnotations, matchingAnnotations.begin()), + [&](const uint32_t annotationID) -> bool { + const auto it = annotations.find(annotationID); + if (it != annotations.end()) { + return (it->second->type == type); + } else { + return false; + } + }); + } else { + std::copy(tile.second.first.begin(), tile.second.first.end(), + std::inserter(matchingAnnotations, matchingAnnotations.begin())); + } } else { // This tile is intersected by the query bounds. We need to check the // tile's annotations' bounding boxes individually. std::copy_if(tile.second.first.begin(), tile.second.first.end(), - std::back_inserter(matchingAnnotations), + std::inserter(matchingAnnotations, matchingAnnotations.begin()), [&](const uint32_t annotationID) -> bool { const auto it = annotations.find(annotationID); if (it != annotations.end()) { - const LatLngBounds annoBounds = it->second->getBounds(); - return (annoBounds.sw.latitude >= queryBounds.sw.latitude && - annoBounds.ne.latitude <= queryBounds.ne.latitude && - annoBounds.sw.longitude >= queryBounds.sw.longitude && - annoBounds.ne.longitude <= queryBounds.ne.longitude); - } else { - return false; + // check type + if (type != AnnotationType::Any && it->second->type != type) { + return false; + } + + // check bounds + if (it->second->type == AnnotationType::Point) { + return queryBounds.contains(it->second->getPoint()); + } else if (it->second->type == AnnotationType::Shape) { + return queryBounds.intersects(it->second->getBounds()); + } } + return false; }); } } } } - return matchingAnnotations; + return AnnotationIDs(matchingAnnotations.begin(), matchingAnnotations.end()); } LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids) const { @@ -290,7 +524,7 @@ LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids for (auto id : ids) { const auto annotation_it = annotations.find(id); if (annotation_it != annotations.end()) { - bounds.extend(annotation_it->second->getPoint()); + bounds.extend(annotation_it->second->getBounds()); } } @@ -300,13 +534,89 @@ LatLngBounds AnnotationManager::getBoundsForAnnotations(const AnnotationIDs& ids const LiveTile* AnnotationManager::getTile(const TileID& id) { std::lock_guard<std::mutex> lock(mtx); - const auto tile_it = tiles.find(id); - if (tile_it != tiles.end()) { - return tile_it->second.second.get(); + // look up any existing annotation tile + LiveTile *renderTile = nullptr; + const auto tile_lookup_it = tiles.find(id); + if (tile_lookup_it != tiles.end()) { + // it exists and may have annotations already + renderTile = tile_lookup_it->second.second.get(); + } else if (orderedShapeAnnotations.size()) { + // it needs created, but only for on-demand shapes + renderTile = tiles.emplace(id, + std::make_pair(std::unordered_set<uint32_t>(), std::make_unique<LiveTile>()) + ).first->second.second.get(); + } + + if (renderTile != nullptr && orderedShapeAnnotations.size()) { + + // create shape tile layers from GeoJSONVT queries + for (auto& tiler_it : shapeTilers) { + const auto annotationID = tiler_it.first; + const std::string layerID = ShapeLayerID + "." + std::to_string(annotationID); + + // check for existing render layer + auto renderLayer = renderTile->getMutableLayer(layerID); + + if (renderLayer == nullptr) { + // we might need to create a tile layer for this shape + const auto& shapeTile = tiler_it.second->getTile(id.z, id.x, id.y); + + if (shapeTile) { + + // shape exists on this tile; let's make a layer + renderLayer = std::make_shared<LiveTileLayer>(); + + // convert the features and add to render layer + for (auto& shapeFeature : shapeTile.features) { + + using namespace mapbox::util::geojsonvt; + + FeatureType renderType = FeatureType::Unknown; + + if (shapeFeature.type == TileFeatureType::LineString) { + renderType = FeatureType::LineString; + } else if (shapeFeature.type == TileFeatureType::Polygon) { + renderType = FeatureType::Polygon; + } + + assert(renderType != FeatureType::Unknown); + + GeometryCollection renderGeometry; + + for (auto& shapeGeometry : shapeFeature.geometry) { + + std::vector<Coordinate> renderLine; + + auto& shapeRing = shapeGeometry.get<TileRing>(); + + for (auto& shapePoint : shapeRing.points) { + renderLine.emplace_back(shapePoint.x, shapePoint.y); + } + + renderGeometry.push_back(renderLine); + } + + auto renderFeature = std::make_shared<LiveTileFeature>(renderType, renderGeometry); + + renderLayer->addFeature(renderFeature); + } + + // move the layer to the render tile + renderTile->addLayer(layerID, renderLayer); + + // associate the annotation with the tile + auto tile_update_it = tiles.find(id); + assert(tile_update_it != tiles.end()); + tile_update_it->second.first.insert(annotationID); + } + } + } } - return nullptr; + + return renderTile; } -const std::string AnnotationManager::layerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::PointLayerID = "com.mapbox.annotations.points"; +const std::string AnnotationManager::ShapeLayerID = "com.mapbox.annotations.shape"; } diff --git a/src/mbgl/map/annotation.hpp b/src/mbgl/map/annotation.hpp index 0c9a078e57..213ff608a0 100644 --- a/src/mbgl/map/annotation.hpp +++ b/src/mbgl/map/annotation.hpp @@ -1,8 +1,13 @@ #ifndef MBGL_MAP_ANNOTATIONS #define MBGL_MAP_ANNOTATIONS +#include <mbgl/map/map.hpp> +#include <mbgl/map/geometry_tile.hpp> #include <mbgl/map/tile_id.hpp> +#include <mbgl/style/style_properties.hpp> +#include <mbgl/style/types.hpp> #include <mbgl/util/geo.hpp> +#include <mbgl/util/geojsonvt/geojsonvt.hpp> #include <mbgl/util/noncopyable.hpp> #include <mbgl/util/vec.hpp> @@ -18,35 +23,87 @@ namespace mbgl { class Annotation; class Map; class LiveTile; +class LiveTileFeature; class MapData; -using AnnotationIDs = std::vector<uint32_t>; +using AnnotationsProperties = std::unordered_map<std::string, std::vector<std::string>>; + +using GeoJSONVT = mapbox::util::geojsonvt::GeoJSONVT; + +class Annotation : private util::noncopyable { + friend class AnnotationManager; +public: + Annotation(AnnotationType, const AnnotationSegments&, const StyleProperties&); + +public: + const StyleProperties styleProperties; + +private: + LatLng getPoint() const; + LatLngBounds getBounds() const { return bounds; } + +private: + const AnnotationType type = AnnotationType::Point; + const AnnotationSegments geometry; + std::unordered_map<TileID, std::weak_ptr<const LiveTileFeature>, TileID::Hash> tilePointFeatures; + const LatLngBounds bounds; +}; class AnnotationManager : private util::noncopyable { public: AnnotationManager(); ~AnnotationManager(); + void markStaleTiles(std::unordered_set<TileID, TileID::Hash>); + std::unordered_set<TileID, TileID::Hash> resetStaleTiles(); + void setDefaultPointAnnotationSymbol(const std::string& symbol); - std::pair<std::vector<TileID>, AnnotationIDs> addPointAnnotations( - const std::vector<LatLng>&, const std::vector<std::string>& symbols, const MapData&); - std::vector<TileID> removeAnnotations(const AnnotationIDs&, const MapData&); - AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&) const; + std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> addPointAnnotations( + const AnnotationSegment&, + const AnnotationsProperties&, + const MapData&); + std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> addShapeAnnotations( + const std::vector<AnnotationSegments>&, + const std::vector<StyleProperties>&, + const AnnotationsProperties&, + const MapData&); + std::unordered_set<TileID, TileID::Hash> removeAnnotations(const AnnotationIDs&, const MapData&); + AnnotationIDs getOrderedShapeAnnotations() const { return orderedShapeAnnotations; } + const StyleProperties getAnnotationStyleProperties(uint32_t) const; + AnnotationIDs getAnnotationsInBounds(const LatLngBounds&, const MapData&, const AnnotationType& = AnnotationType::Any) const; LatLngBounds getBoundsForAnnotations(const AnnotationIDs&) const; const LiveTile* getTile(const TileID& id); - static const std::string layerID; + static const std::string PointLayerID; + static const std::string ShapeLayerID; private: inline uint32_t nextID(); static vec2<double> projectPoint(const LatLng& point); + std::pair<std::unordered_set<TileID, TileID::Hash>, AnnotationIDs> addAnnotations( + const AnnotationType, + const std::vector<AnnotationSegments>&, + const std::vector<StyleProperties>&, + const AnnotationsProperties&, + const MapData&); + std::unordered_set<TileID, TileID::Hash> addTileFeature( + const uint32_t annotationID, + const AnnotationSegments&, + const std::vector<std::vector<vec2<double>>>& projectedFeature, + const AnnotationType&, + const StyleProperties&, + const std::unordered_map<std::string, std::string>& featureProperties, + const uint8_t maxZoom); private: mutable std::mutex mtx; std::string defaultPointAnnotationSymbol; std::unordered_map<uint32_t, std::unique_ptr<Annotation>> annotations; + std::vector<uint32_t> orderedShapeAnnotations; std::unordered_map<TileID, std::pair<std::unordered_set<uint32_t>, std::unique_ptr<LiveTile>>, TileID::Hash> tiles; + std::unordered_map<uint32_t, std::unique_ptr<GeoJSONVT>> shapeTilers; + std::unordered_set<TileID, TileID::Hash> staleTiles; uint32_t nextID_ = 0; }; diff --git a/src/mbgl/map/live_tile.cpp b/src/mbgl/map/live_tile.cpp index 06337af184..f7a70f7d46 100644 --- a/src/mbgl/map/live_tile.cpp +++ b/src/mbgl/map/live_tile.cpp @@ -3,7 +3,8 @@ namespace mbgl { -LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, std::map<std::string, std::string> properties_) +LiveTileFeature::LiveTileFeature(FeatureType type_, GeometryCollection geometries_, + std::unordered_map<std::string, std::string> properties_) : type(type_), properties(properties_), geometries(geometries_) {} @@ -41,6 +42,10 @@ void LiveTile::addLayer(const std::string& name, util::ptr<LiveTileLayer> layer) layers.emplace(name, std::move(layer)); } +void LiveTile::removeLayer(const std::string& name) { + layers.erase(name); +} + util::ptr<GeometryTileLayer> LiveTile::getLayer(const std::string& name) const { return getMutableLayer(name); } diff --git a/src/mbgl/map/live_tile.hpp b/src/mbgl/map/live_tile.hpp index b13b5a1f66..5b4c2d1c16 100644 --- a/src/mbgl/map/live_tile.hpp +++ b/src/mbgl/map/live_tile.hpp @@ -2,6 +2,7 @@ #define MBGL_MAP_LIVE_TILE #include <map> +#include <unordered_map> #include <mbgl/map/geometry_tile.hpp> @@ -9,7 +10,8 @@ namespace mbgl { class LiveTileFeature : public GeometryTileFeature { public: - LiveTileFeature(FeatureType, GeometryCollection, std::map<std::string, std::string> properties = {{}}); + LiveTileFeature(FeatureType, GeometryCollection, + std::unordered_map<std::string, std::string> properties = {{}}); FeatureType getType() const override { return type; } mapbox::util::optional<Value> getValue(const std::string&) const override; @@ -17,7 +19,7 @@ public: private: FeatureType type = FeatureType::Unknown; - std::map<std::string, std::string> properties; + std::unordered_map<std::string, std::string> properties; GeometryCollection geometries; }; @@ -40,6 +42,7 @@ public: LiveTile(); void addLayer(const std::string&, util::ptr<LiveTileLayer>); + void removeLayer(const std::string&); util::ptr<GeometryTileLayer> getLayer(const std::string&) const override; util::ptr<LiveTileLayer> getMutableLayer(const std::string&) const; diff --git a/src/mbgl/map/map.cpp b/src/mbgl/map/map.cpp index 47c9472d59..8d5b544191 100644 --- a/src/mbgl/map/map.cpp +++ b/src/mbgl/map/map.cpp @@ -6,6 +6,8 @@ #include <mbgl/util/projection.hpp> #include <mbgl/util/thread.hpp> +#include <unordered_map> + namespace mbgl { Map::Map(View& view, FileSource& fileSource, MapMode mode) @@ -239,8 +241,22 @@ uint32_t Map::addPointAnnotation(const LatLng& point, const std::string& symbol) return addPointAnnotations({ point }, { symbol }).front(); } -std::vector<uint32_t> Map::addPointAnnotations(const std::vector<LatLng>& points, const std::vector<std::string>& symbols) { - auto result = data->annotationManager.addPointAnnotations(points, symbols, *data); +AnnotationIDs Map::addPointAnnotations(const AnnotationSegment& points, + const std::vector<std::string>& symbols) { + AnnotationsProperties properties = { { "symbols", symbols } }; + auto result = data->annotationManager.addPointAnnotations(points, properties, *data); + context->invoke(&MapContext::updateAnnotationTiles, result.first); + return result.second; +} + +uint32_t Map::addShapeAnnotation(const AnnotationSegments& shape, + const StyleProperties& styleProperties) { + return addShapeAnnotations({ shape }, { styleProperties }).front(); +} + +AnnotationIDs Map::addShapeAnnotations(const std::vector<AnnotationSegments>& shapes, + const std::vector<StyleProperties>& styleProperties) { + auto result = data->annotationManager.addShapeAnnotations(shapes, styleProperties, {{}}, *data); context->invoke(&MapContext::updateAnnotationTiles, result.first); return result.second; } @@ -254,8 +270,8 @@ void Map::removeAnnotations(const std::vector<uint32_t>& annotations) { context->invoke(&MapContext::updateAnnotationTiles, result); } -std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds) { - return data->annotationManager.getAnnotationsInBounds(bounds, *data); +std::vector<uint32_t> Map::getAnnotationsInBounds(const LatLngBounds& bounds, const AnnotationType& type) { + return data->annotationManager.getAnnotationsInBounds(bounds, *data, type); } LatLngBounds Map::getBoundsForAnnotations(const std::vector<uint32_t>& annotations) { diff --git a/src/mbgl/map/map_context.cpp b/src/mbgl/map/map_context.cpp index c349bdd155..bd288a5cda 100644 --- a/src/mbgl/map/map_context.cpp +++ b/src/mbgl/map/map_context.cpp @@ -3,6 +3,7 @@ #include <mbgl/map/view.hpp> #include <mbgl/map/environment.hpp> #include <mbgl/map/still_image.hpp> +#include <mbgl/map/annotation.hpp> #include <mbgl/platform/log.hpp> @@ -12,12 +13,16 @@ #include <mbgl/storage/response.hpp> #include <mbgl/style/style.hpp> +#include <mbgl/style/style_bucket.hpp> +#include <mbgl/style/style_layer.hpp> #include <mbgl/util/uv_detail.hpp> #include <mbgl/util/worker.hpp> #include <mbgl/util/texture_pool.hpp> #include <mbgl/util/exception.hpp> +#include <algorithm> + namespace mbgl { MapContext::MapContext(uv_loop_t* loop, View& view_, FileSource& fileSource, MapData& data_) @@ -112,6 +117,11 @@ void MapContext::loadStyleJSON(const std::string& json, const std::string& base) style->setObserver(this); triggerUpdate(Update::Zoom); + + auto staleTiles = data.annotationManager.resetStaleTiles(); + if (staleTiles.size()) { + updateAnnotationTiles(staleTiles); + } } void MapContext::updateTiles() { @@ -120,15 +130,111 @@ void MapContext::updateTiles() { style->update(data, transformState, *texturePool); } -void MapContext::updateAnnotationTiles(const std::vector<TileID>& ids) { +void MapContext::updateAnnotationTiles(const std::unordered_set<TileID, TileID::Hash>& ids) { assert(Environment::currentlyOn(ThreadType::Map)); + + data.annotationManager.markStaleTiles(ids); + if (!style) return; + + // grab existing, single shape annotations source + const auto& shapeID = AnnotationManager::ShapeLayerID; + + const auto source_it = std::find_if(style->sources.begin(), style->sources.end(), + [&shapeID](util::ptr<Source> source) { + return (source->info.source_id == shapeID); + }); + assert(source_it != style->sources.end()); + source_it->get()->enabled = true; + + // create (if necessary) layers and buckets for each shape + for (const auto &shapeAnnotationID : data.annotationManager.getOrderedShapeAnnotations()) { + const std::string shapeLayerID = shapeID + "." + std::to_string(shapeAnnotationID); + + const auto layer_it = std::find_if(style->layers.begin(), style->layers.end(), + [&shapeLayerID](util::ptr<StyleLayer> layer) { + return (layer->id == shapeLayerID); + }); + + if (layer_it == style->layers.end()) { + // query shape styling + auto& shapeStyle = data.annotationManager.getAnnotationStyleProperties(shapeAnnotationID); + + // apply shape paint properties + ClassProperties paintProperties; + + if (shapeStyle.is<LineProperties>()) { + // opacity + PropertyValue lineOpacity = ConstantFunction<float>(shapeStyle.get<LineProperties>().opacity); + paintProperties.set(PropertyKey::LineOpacity, lineOpacity); + + // line width + PropertyValue lineWidth = ConstantFunction<float>(shapeStyle.get<LineProperties>().width); + paintProperties.set(PropertyKey::LineWidth, lineWidth); + + // stroke color + PropertyValue strokeColor = ConstantFunction<Color>(shapeStyle.get<LineProperties>().color); + paintProperties.set(PropertyKey::LineColor, strokeColor); + } else if (shapeStyle.is<FillProperties>()) { + // opacity + PropertyValue fillOpacity = ConstantFunction<float>(shapeStyle.get<FillProperties>().opacity); + paintProperties.set(PropertyKey::FillOpacity, fillOpacity); + + // fill color + PropertyValue fillColor = ConstantFunction<Color>(shapeStyle.get<FillProperties>().fill_color); + paintProperties.set(PropertyKey::FillColor, fillColor); + + // stroke color + PropertyValue strokeColor = ConstantFunction<Color>(shapeStyle.get<FillProperties>().stroke_color); + paintProperties.set(PropertyKey::FillOutlineColor, strokeColor); + } + + std::map<ClassID, ClassProperties> shapePaints; + shapePaints.emplace(ClassID::Default, std::move(paintProperties)); + + // create shape layer + util::ptr<StyleLayer> shapeLayer = std::make_shared<StyleLayer>(shapeLayerID, std::move(shapePaints)); + shapeLayer->type = (shapeStyle.is<LineProperties>() ? StyleLayerType::Line : StyleLayerType::Fill); + + // add to end of other shape layers just before (last) point layer + style->layers.emplace((style->layers.end() - 2), shapeLayer); + + // create shape bucket & connect to source + util::ptr<StyleBucket> shapeBucket = std::make_shared<StyleBucket>(shapeLayer->type); + shapeBucket->name = shapeLayer->id; + shapeBucket->source_layer = shapeLayer->id; + shapeBucket->source = *source_it; + + // apply line layout properties to bucket + if (shapeStyle.is<LineProperties>()) { + shapeBucket->layout.set(PropertyKey::LineJoin, ConstantFunction<JoinType>(JoinType::Round)); + } + + // connect layer to bucket + shapeLayer->bucket = shapeBucket; + } + } + + // invalidate annotations layer tiles for (const auto &source : style->sources) { if (source->info.type == SourceType::Annotations) { source->invalidateTiles(ids); } } - triggerUpdate(); + + cascadeClasses(); + + triggerUpdate(Update::Classes); + + data.annotationManager.resetStaleTiles(); +} + +void MapContext::cascadeClasses() { + style->cascade(data.getClasses()); +} + +void MapContext::recalculateStyle(TimePoint now) { + style->recalculate(transformState.getNormalizedZoom(), now); } void MapContext::update() { @@ -146,12 +252,12 @@ void MapContext::update() { } if (updated & static_cast<UpdateType>(Update::Classes)) { - style->cascade(data.getClasses()); + cascadeClasses(); } if (updated & static_cast<UpdateType>(Update::Classes) || updated & static_cast<UpdateType>(Update::Zoom)) { - style->recalculate(transformState.getNormalizedZoom(), now); + recalculateStyle(now); } updateTiles(); diff --git a/src/mbgl/map/map_context.hpp b/src/mbgl/map/map_context.hpp index 81e757c4cf..16fdea1820 100644 --- a/src/mbgl/map/map_context.hpp +++ b/src/mbgl/map/map_context.hpp @@ -49,7 +49,7 @@ public: std::string getStyleJSON() const { return styleJSON; } double getTopOffsetPixelsForAnnotationSymbol(const std::string& symbol); - void updateAnnotationTiles(const std::vector<TileID>&); + void updateAnnotationTiles(const std::unordered_set<TileID, TileID::Hash>&); void setSourceTileCacheSize(size_t size); void onLowMemory(); @@ -63,6 +63,10 @@ public: private: void updateTiles(); + // Style-related updates. + void cascadeClasses(); + void recalculateStyle(TimePoint); + // Update the state indicated by the accumulated Update flags, then render. void update(); diff --git a/src/mbgl/map/source.cpp b/src/mbgl/map/source.cpp index 97adacad6d..7fb015c1da 100644 --- a/src/mbgl/map/source.cpp +++ b/src/mbgl/map/source.cpp @@ -521,11 +521,16 @@ bool Source::update(MapData& data, return allTilesUpdated; } -void Source::invalidateTiles(const std::vector<TileID>& ids) { +void Source::invalidateTiles(const std::unordered_set<TileID, TileID::Hash>& ids) { cache.clear(); - for (auto& id : ids) { - tiles.erase(id); - tile_data.erase(id); + if (ids.size()) { + for (auto& id : ids) { + tiles.erase(id); + tile_data.erase(id); + } + } else { + tiles.clear(); + tile_data.clear(); } updateTilePtrs(); } diff --git a/src/mbgl/map/source.hpp b/src/mbgl/map/source.hpp index be869559b5..aea8939c45 100644 --- a/src/mbgl/map/source.hpp +++ b/src/mbgl/map/source.hpp @@ -17,6 +17,7 @@ #include <forward_list> #include <iosfwd> #include <map> +#include <unordered_set> namespace mbgl { @@ -46,6 +47,7 @@ public: std::string attribution; std::array<float, 3> center = {{0, 0, 0}}; std::array<float, 4> bounds = {{-180, -90, 180, 90}}; + std::string source_id = ""; void parseTileJSONProperties(const rapidjson::Value&); std::string tileURL(const TileID& id, float pixelRatio) const; @@ -86,7 +88,7 @@ public: TexturePool&, bool shouldReparsePartialTiles); - void invalidateTiles(const std::vector<TileID>&); + void invalidateTiles(const std::unordered_set<TileID, TileID::Hash>&); void updateMatrices(const mat4 &projMatrix, const TransformState &transform); void drawClippingMasks(Painter &painter); diff --git a/src/mbgl/style/style_parser.cpp b/src/mbgl/style/style_parser.cpp index 83a113f885..c2aa115fa4 100644 --- a/src/mbgl/style/style_parser.cpp +++ b/src/mbgl/style/style_parser.cpp @@ -36,20 +36,30 @@ void StyleParser::parse(JSVal document) { if (document.HasMember("layers")) { parseLayers(document["layers"]); + // create shape annotations source + const std::string& shapeID = AnnotationManager::ShapeLayerID; + + util::ptr<Source> shapeAnnotationsSource = std::make_shared<Source>(); + sourcesMap.emplace(shapeID, shapeAnnotationsSource); + sources.emplace_back(shapeAnnotationsSource); + shapeAnnotationsSource->info.type = SourceType::Annotations; + shapeAnnotationsSource->info.source_id = shapeID; + // create point annotations layer - // - const std::string& id = AnnotationManager::layerID; + const std::string& pointID = AnnotationManager::PointLayerID; - std::map<ClassID, ClassProperties> paints; - util::ptr<StyleLayer> annotations = std::make_shared<StyleLayer>(id, std::move(paints)); - annotations->type = StyleLayerType::Symbol; - layersMap.emplace(id, std::pair<JSVal, util::ptr<StyleLayer>> { JSVal(id), annotations }); - layers.emplace_back(annotations); + std::map<ClassID, ClassProperties> pointPaints; + util::ptr<StyleLayer> pointAnnotationsLayer = std::make_shared<StyleLayer>(pointID, std::move(pointPaints)); + pointAnnotationsLayer->type = StyleLayerType::Symbol; + layersMap.emplace(pointID, std::pair<JSVal, util::ptr<StyleLayer>> { JSVal(pointID), pointAnnotationsLayer }); + layers.emplace_back(pointAnnotationsLayer); - util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(annotations->type); - pointBucket->name = annotations->id; - pointBucket->source_layer = annotations->id; + // create point annotations symbol bucket + util::ptr<StyleBucket> pointBucket = std::make_shared<StyleBucket>(pointAnnotationsLayer->type); + pointBucket->name = pointAnnotationsLayer->id; + pointBucket->source_layer = pointAnnotationsLayer->id; + // build up point annotations style rapidjson::Document d; rapidjson::Value iconImage(rapidjson::kObjectType); iconImage.AddMember("icon-image", "{sprite}", d.GetAllocator()); @@ -58,14 +68,14 @@ void StyleParser::parse(JSVal document) { iconOverlap.AddMember("icon-allow-overlap", true, d.GetAllocator()); parseLayout(iconOverlap, pointBucket); - util::ptr<Source> source = std::make_shared<Source>(); - sourcesMap.emplace(id, source); - sources.emplace_back(source); - source->info.type = SourceType::Annotations; - pointBucket->source = source; - annotations->bucket = pointBucket; - // - // end point annotations + // create point annotations source & connect to bucket & layer + util::ptr<Source> pointAnnotationsSource = std::make_shared<Source>(); + sourcesMap.emplace(pointID, pointAnnotationsSource); + sources.emplace_back(pointAnnotationsSource); + pointAnnotationsSource->info.type = SourceType::Annotations; + pointAnnotationsSource->info.source_id = pointID; + pointBucket->source = pointAnnotationsSource; + pointAnnotationsLayer->bucket = pointBucket; } if (document.HasMember("sprite")) { diff --git a/src/mbgl/style/style_properties.cpp b/src/mbgl/style/style_properties.cpp index 29730fb85b..3c5b525c1d 100644 --- a/src/mbgl/style/style_properties.cpp +++ b/src/mbgl/style/style_properties.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/style_properties.hpp> +#include <mbgl/style/piecewisefunction_properties.hpp> namespace mbgl { diff --git a/src/mbgl/style/style_properties.hpp b/src/mbgl/style/style_properties.hpp deleted file mode 100644 index 8e8619fb99..0000000000 --- a/src/mbgl/style/style_properties.hpp +++ /dev/null @@ -1,115 +0,0 @@ -#ifndef MBGL_STYLE_STYLE_PROPERTIES -#define MBGL_STYLE_STYLE_PROPERTIES - -#include <mbgl/util/variant.hpp> -#include <mbgl/style/types.hpp> -#include <mbgl/style/piecewisefunction_properties.hpp> - -#include <array> -#include <string> -#include <type_traits> -#include <memory> -#include <vector> - -namespace mbgl { - -struct FillProperties { - FillProperties() {} - bool antialias = true; - float opacity = 1.0f; - Color fill_color = {{ 0, 0, 0, 1 }}; - Color stroke_color = {{ 0, 0, 0, -1 }}; - std::array<float, 2> translate = {{ 0, 0 }}; - TranslateAnchorType translateAnchor = TranslateAnchorType::Map; - Faded<std::string> image; - - inline bool isVisible() const { - return opacity > 0 && (fill_color[3] > 0 || stroke_color[3] > 0); - } -}; - -struct LineProperties { - inline LineProperties() {} - float opacity = 1.0f; - Color color = {{ 0, 0, 0, 1 }}; - std::array<float, 2> translate = {{ 0, 0 }}; - TranslateAnchorType translateAnchor = TranslateAnchorType::Map; - float width = 1; - float gap_width = 0; - float blur = 0; - Faded<std::vector<float>> dash_array; - float dash_line_width = 1; - Faded<std::string> image; - - inline bool isVisible() const { - return opacity > 0 && color[3] > 0 && width > 0; - } -}; - -struct SymbolProperties { - inline SymbolProperties() {} - - struct { - float opacity = 1.0f; - float size = 1.0f; - Color color = {{ 0, 0, 0, 1 }}; - Color halo_color = {{ 0, 0, 0, 0 }}; - float halo_width = 0.0f; - float halo_blur = 0.0f; - std::array<float, 2> translate = {{ 0, 0 }}; - TranslateAnchorType translate_anchor = TranslateAnchorType::Map; - } icon; - - struct { - float opacity = 1.0f; - float size = 16.0f; - Color color = {{ 0, 0, 0, 1 }}; - Color halo_color = {{ 0, 0, 0, 0 }}; - float halo_width = 0.0f; - float halo_blur = 0.0f; - std::array<float, 2> translate = {{ 0, 0 }}; - TranslateAnchorType translate_anchor = TranslateAnchorType::Map; - } text; - - inline bool isVisible() const { - return (icon.opacity > 0 && (icon.color[3] > 0 || icon.halo_color[3] > 0) && icon.size > 0) || - (text.opacity > 0 && (text.color[3] > 0 || text.halo_color[3] > 0) && text.size > 0); - } -}; - -struct RasterProperties { - inline RasterProperties() {} - float opacity = 1.0f; - float hue_rotate = 0.0f; - std::array<float, 2> brightness = {{ 0, 1 }}; - float saturation = 0.0f; - float contrast = 0.0f; - float fade = 0.0f; - - inline bool isVisible() const { - return opacity > 0; - } -}; - -struct BackgroundProperties { - inline BackgroundProperties() {} - float opacity = 1.0f; - Color color = {{ 0, 0, 0, 1 }}; - Faded<std::string> image; -}; - -typedef mapbox::util::variant< - FillProperties, - LineProperties, - SymbolProperties, - RasterProperties, - BackgroundProperties, - std::false_type -> StyleProperties; - -template <typename T> -const T &defaultStyleProperties(); - -} - -#endif diff --git a/src/mbgl/style/types.hpp b/src/mbgl/style/types.hpp deleted file mode 100644 index f6ffcd6865..0000000000 --- a/src/mbgl/style/types.hpp +++ /dev/null @@ -1,210 +0,0 @@ -#ifndef MBGL_STYLE_TYPES -#define MBGL_STYLE_TYPES - -#include <mbgl/util/enum.hpp> - -#include <string> -#include <array> - -namespace mbgl { - -// Stores a premultiplied color, with all four channels ranging from 0..1 -typedef std::array<float, 4> Color; - - -template <typename T> -struct Faded { - T from; - float fromScale; - T to; - float toScale; - float t; -}; - -// ------------------------------------------------------------------------------------------------- - -enum class StyleLayerType : uint8_t { - Unknown, - Fill, - Line, - Symbol, - Raster, - Background -}; - -MBGL_DEFINE_ENUM_CLASS(StyleLayerTypeClass, StyleLayerType, { - { StyleLayerType::Unknown, "unknown" }, - { StyleLayerType::Fill, "fill" }, - { StyleLayerType::Line, "line" }, - { StyleLayerType::Symbol, "symbol" }, - { StyleLayerType::Raster, "raster" }, - { StyleLayerType::Background, "background" }, - { StyleLayerType(-1), "unknown" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class SourceType : uint8_t { - Vector, - Raster, - GeoJSON, - Video, - Annotations -}; - -MBGL_DEFINE_ENUM_CLASS(SourceTypeClass, SourceType, { - { SourceType::Vector, "vector" }, - { SourceType::Raster, "raster" }, - { SourceType::GeoJSON, "geojson" }, - { SourceType::Video, "video" }, - { SourceType::Annotations, "annotations" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class VisibilityType : bool { - Visible, - None, -}; - -MBGL_DEFINE_ENUM_CLASS(VisibilityTypeClass, VisibilityType, { - { VisibilityType::Visible, "visible" }, - { VisibilityType::None, "none" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class CapType : uint8_t { - Round, - Butt, - Square, -}; - -MBGL_DEFINE_ENUM_CLASS(CapTypeClass, CapType, { - { CapType::Round, "round" }, - { CapType::Butt, "butt" }, - { CapType::Square, "square" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class JoinType : uint8_t { - Miter, - Bevel, - Round, - FlipBevel -}; - -MBGL_DEFINE_ENUM_CLASS(JoinTypeClass, JoinType, { - { JoinType::Miter, "miter" }, - { JoinType::Bevel, "bevel" }, - { JoinType::Round, "round" }, - { JoinType::FlipBevel, "flipbevel" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class TranslateAnchorType : bool { - Map, - Viewport -}; - -MBGL_DEFINE_ENUM_CLASS(TranslateAnchorTypeClass, TranslateAnchorType, { - { TranslateAnchorType::Map, "map" }, - { TranslateAnchorType::Viewport, "viewport" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class RotateAnchorType : bool { - Map, - Viewport, -}; - -MBGL_DEFINE_ENUM_CLASS(RotateAnchorTypeClass, RotateAnchorType, { - { RotateAnchorType::Map, "map" }, - { RotateAnchorType::Viewport, "viewport" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class PlacementType : bool { - Point, - Line, -}; - -MBGL_DEFINE_ENUM_CLASS(PlacementTypeClass, PlacementType, { - { PlacementType::Point, "point" }, - { PlacementType::Line, "line" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class RotationAlignmentType : bool { - Map, - Viewport, -}; - -MBGL_DEFINE_ENUM_CLASS(RotationAlignmentTypeClass, RotationAlignmentType, { - { RotationAlignmentType::Map, "map" }, - { RotationAlignmentType::Viewport, "viewport" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class TextJustifyType : uint8_t { - Center, - Left, - Right -}; - -MBGL_DEFINE_ENUM_CLASS(TextJustifyTypeClass, TextJustifyType, { - { TextJustifyType::Center, "center" }, - { TextJustifyType::Left, "left" }, - { TextJustifyType::Right, "right" }, -}); - -// ------------------------------------------------------------------------------------------------- - -enum class TextAnchorType : uint8_t { - Center, - Left, - Right, - Top, - Bottom, - TopLeft, - TopRight, - BottomLeft, - BottomRight -}; - -MBGL_DEFINE_ENUM_CLASS(TextAnchorTypeClass, TextAnchorType, { - { TextAnchorType::Center, "center" }, - { TextAnchorType::Left, "left" }, - { TextAnchorType::Right, "right" }, - { TextAnchorType::Top, "top" }, - { TextAnchorType::Bottom, "bottom" }, - { TextAnchorType::TopLeft, "top-left" }, - { TextAnchorType::TopRight, "top-right" }, - { TextAnchorType::BottomLeft, "bottom-left" }, - { TextAnchorType::BottomRight, "bottom-right" } -}); - -// ------------------------------------------------------------------------------------------------- - -enum class TextTransformType : uint8_t { - None, - Uppercase, - Lowercase, -}; - -MBGL_DEFINE_ENUM_CLASS(TextTransformTypeClass, TextTransformType, { - { TextTransformType::None, "none" }, - { TextTransformType::Uppercase, "uppercase" }, - { TextTransformType::Lowercase, "lowercase" }, -}); - -} - -#endif - diff --git a/src/mbgl/util/geojsonvt b/src/mbgl/util/geojsonvt new file mode 160000 +Subproject ed99a6290fa42107a982e7f3675aae49d29026b |