diff --git a/include/mbgl/annotation/shape_annotation.hpp b/include/mbgl/annotation/shape_annotation.hpp index 5e6a13149..90f2a96dd 100644 --- a/include/mbgl/annotation/shape_annotation.hpp +++ b/include/mbgl/annotation/shape_annotation.hpp @@ -15,14 +15,17 @@ using AnnotationSegments = std::vector; class ShapeAnnotation { public: - using Properties = mapbox::util::variant; + using Properties = mapbox::util::variant< + FillPaintProperties, // creates a fill annotation + LinePaintProperties, // creates a line annotation + std::string>; // creates an annotation whose type and properties are sourced from a style layer - inline ShapeAnnotation(const AnnotationSegments& segments_, const Properties& styleProperties_) - : segments(segments_), styleProperties(styleProperties_) { + ShapeAnnotation(const AnnotationSegments& segments_, const Properties& properties_) + : segments(segments_), properties(properties_) { } const AnnotationSegments segments; - const Properties styleProperties; + const Properties properties; }; } diff --git a/src/mbgl/annotation/shape_annotation_impl.cpp b/src/mbgl/annotation/shape_annotation_impl.cpp index 5b3c07fbc..4ab9958fd 100644 --- a/src/mbgl/annotation/shape_annotation_impl.cpp +++ b/src/mbgl/annotation/shape_annotation_impl.cpp @@ -15,92 +15,115 @@ using namespace mapbox::util::geojsonvt; ShapeAnnotationImpl::ShapeAnnotationImpl(const AnnotationID id_, const ShapeAnnotation& shape_, - const uint8_t maxZoom) + const uint8_t maxZoom_) : id(id_), layerID("com.mapbox.annotations.shape." + util::toString(id)), shape(shape_), - shapeTiler(([&] { - const double baseTolerance = 3; - const uint16_t extent = 4096; - const double tolerance = baseTolerance / ((1 << maxZoom) * extent); - - ProjectedGeometryContainer rings; - std::vector points; - - for (size_t i = 0; i < shape.segments[0].size(); ++i) { // first segment for now (no holes) - const double constraintedLatitude = ::fmin(::fmax(shape.segments[0][i].latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); - points.push_back(LonLat(shape.segments[0][i].longitude, constraintedLatitude)); - } - - ProjectedFeatureType featureType; - - if (shape.styleProperties.is()) { - 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 features; - features.push_back(Convert::create(Tags(), featureType, rings)); - return features; - })(), maxZoom, 4, 100, 10) {} + maxZoom(maxZoom_) { +} void ShapeAnnotationImpl::updateStyle(Style& style) { if (style.getLayer(layerID)) return; - if (shape.styleProperties.is()) { - const LinePaintProperties& properties = shape.styleProperties.get(); + std::unique_ptr layer; - std::unique_ptr layer = std::make_unique(); - layer->id = layerID; - layer->type = StyleLayerType::Line; + if (shape.properties.is()) { + layer = createLineLayer(); + const LinePaintProperties& properties = shape.properties.get(); ClassProperties paintProperties; paintProperties.set(PropertyKey::LineOpacity, ConstantFunction(properties.opacity)); paintProperties.set(PropertyKey::LineWidth, ConstantFunction(properties.width)); paintProperties.set(PropertyKey::LineColor, ConstantFunction(properties.color)); layer->styles.emplace(ClassID::Default, std::move(paintProperties)); - layer->bucket = std::make_shared(layer->type); - layer->bucket->name = layer->id; - layer->bucket->source = AnnotationManager::SourceID; - layer->bucket->source_layer = layer->id; - layer->bucket->layout.set(PropertyKey::LineJoin, ConstantFunction(JoinType::Round)); - - style.addLayer(std::move(layer), AnnotationManager::PointLayerID); - - } else if (shape.styleProperties.is()) { - const FillPaintProperties& properties = shape.styleProperties.get(); - - std::unique_ptr layer = std::make_unique(); - layer->id = layerID; - layer->type = StyleLayerType::Fill; + } else if (shape.properties.is()) { + layer = createFillLayer(); + const FillPaintProperties& properties = shape.properties.get(); ClassProperties paintProperties; paintProperties.set(PropertyKey::FillOpacity, ConstantFunction(properties.opacity)); paintProperties.set(PropertyKey::FillColor, ConstantFunction(properties.fill_color)); paintProperties.set(PropertyKey::FillOutlineColor, ConstantFunction(properties.stroke_color)); layer->styles.emplace(ClassID::Default, std::move(paintProperties)); - layer->bucket = std::make_shared(layer->type); - layer->bucket->name = layer->id; - layer->bucket->source = AnnotationManager::SourceID; - layer->bucket->source_layer = layer->id; + } else { + const StyleLayer* sourceLayer = style.getLayer(shape.properties.get()); + if (!sourceLayer) return; - style.addLayer(std::move(layer), AnnotationManager::PointLayerID); + switch (sourceLayer->type) { + case StyleLayerType::Line: + layer = createLineLayer(); + break; + + case StyleLayerType::Fill: + layer = createFillLayer(); + break; + + default: + return; + } + + layer->styles = sourceLayer->styles; + layer->bucket->layout = sourceLayer->bucket->layout; } + + layer->bucket->name = layer->id; + layer->bucket->source = AnnotationManager::SourceID; + layer->bucket->source_layer = layer->id; + + style.addLayer(std::move(layer), AnnotationManager::PointLayerID); +} + +std::unique_ptr ShapeAnnotationImpl::createLineLayer() { + type = ProjectedFeatureType::LineString; + std::unique_ptr layer = std::make_unique(); + layer->id = layerID; + layer->type = StyleLayerType::Line; + layer->bucket = std::make_shared(layer->type); + layer->bucket->layout.set(PropertyKey::LineJoin, ConstantFunction(JoinType::Round)); + return std::move(layer); +} + +std::unique_ptr ShapeAnnotationImpl::createFillLayer() { + type = ProjectedFeatureType::Polygon; + std::unique_ptr layer = std::make_unique(); + layer->id = layerID; + layer->type = StyleLayerType::Fill; + layer->bucket = std::make_shared(layer->type); + return std::move(layer); } void ShapeAnnotationImpl::updateTile(const TileID& tileID, AnnotationTile& tile) { - const auto& shapeTile = shapeTiler.getTile(tileID.z, tileID.x, tileID.y); + if (!shapeTiler) { + const double baseTolerance = 3; + const uint16_t extent = 4096; + const double tolerance = baseTolerance / ((1 << maxZoom) * extent); + + ProjectedGeometryContainer rings; + std::vector points; + + for (size_t i = 0; i < shape.segments[0].size(); ++i) { // first segment for now (no holes) + const double constraintedLatitude = ::fmin(::fmax(shape.segments[0][i].latitude, -util::LATITUDE_MAX), util::LATITUDE_MAX); + points.push_back(LonLat(shape.segments[0][i].longitude, constraintedLatitude)); + } + + if (type == ProjectedFeatureType::Polygon && + (points.front().lon != points.back().lon || points.front().lat != points.back().lat)) { + points.push_back(LonLat(points.front().lon, points.front().lat)); + } + + ProjectedGeometryContainer ring = Convert::project(points, tolerance); + rings.members.push_back(ring); + + std::vector features; + features.push_back(Convert::create(Tags(), type, rings)); + + shapeTiler = std::make_unique(features, maxZoom, 4, 100, 10); + } + + const auto& shapeTile = shapeTiler->getTile(tileID.z, tileID.x, tileID.y); if (!shapeTile) return; diff --git a/src/mbgl/annotation/shape_annotation_impl.hpp b/src/mbgl/annotation/shape_annotation_impl.hpp index 4cf6c23f6..1b1dacf37 100644 --- a/src/mbgl/annotation/shape_annotation_impl.hpp +++ b/src/mbgl/annotation/shape_annotation_impl.hpp @@ -13,6 +13,7 @@ namespace mbgl { class Style; +class StyleLayer; class AnnotationTile; class ShapeAnnotationImpl { @@ -30,7 +31,12 @@ public: const ShapeAnnotation shape; private: - mapbox::util::geojsonvt::GeoJSONVT shapeTiler; + std::unique_ptr createLineLayer(); + std::unique_ptr createFillLayer(); + + const uint8_t maxZoom; + mapbox::util::geojsonvt::ProjectedFeatureType type; + std::unique_ptr shapeTiler; }; } diff --git a/src/mbgl/style/class_properties.hpp b/src/mbgl/style/class_properties.hpp index 888a90c5d..0f147fdda 100644 --- a/src/mbgl/style/class_properties.hpp +++ b/src/mbgl/style/class_properties.hpp @@ -11,10 +11,6 @@ namespace mbgl { class ClassProperties { public: - inline ClassProperties() {} - inline ClassProperties(ClassProperties &&properties_) - : properties(std::move(properties_.properties)) {} - inline void set(PropertyKey key, const PropertyValue &value) { properties.emplace(key, value); } diff --git a/test/api/annotations.cpp b/test/api/annotations.cpp index 415b2ddb6..a9667c434 100644 --- a/test/api/annotations.cpp +++ b/test/api/annotations.cpp @@ -75,6 +75,21 @@ TEST(Annotations, FillAnnotation) { util::write_file("test/output/fill_annotation.png", renderPNG(map)); } +TEST(Annotations, StyleSourcedShapeAnnotation) { + auto display = std::make_shared(); + HeadlessView view(display, 1); + DefaultFileSource fileSource(nullptr); + + Map map(view, fileSource, MapMode::Still); + map.setStyleJSON(util::read_file("test/fixtures/api/annotation.json"), ""); + + AnnotationSegments segments = {{ {{ { 0, 0 }, { 0, 45 }, { 45, 45 }, { 45, 0 } }} }}; + + map.addShapeAnnotation(ShapeAnnotation(segments, "annotation")); + + util::write_file("test/output/style_sourced_shape_annotation.png", renderPNG(map)); +} + TEST(Annotations, AddMultiple) { auto display = std::make_shared(); HeadlessView view(display, 1); diff --git a/test/fixtures/api/annotation.json b/test/fixtures/api/annotation.json new file mode 100644 index 000000000..75fcfca41 --- /dev/null +++ b/test/fixtures/api/annotation.json @@ -0,0 +1,19 @@ +{ + "version": 8, + "sources": { + "fake": { + "type": "vector", + "url": "asset://TEST_DATA/fixtures/tiles/streets.json" + } + }, + "layers": [{ + "id": "annotation", + "type": "fill", + "source": "fake", + "source-layer": "fake", + "paint": { + "fill-color": "rgba(255,0,0,1)" + } + }], + "sprite": "asset://TEST_DATA/fixtures/resources/sprite" +}