diff options
author | danswick <dan.swick@gmail.com> | 2019-09-18 11:39:43 -0700 |
---|---|---|
committer | danswick <dan.swick@gmail.com> | 2019-09-18 11:39:43 -0700 |
commit | 46914ecff8b3f2a1945b33358a879c2aac074f17 (patch) | |
tree | 697cf276a43ae4c18c0ad7029c00e7d771448822 /platform | |
parent | 47ea84ad1a5fed3431d026c5765c17c507d4623e (diff) | |
parent | 8805defe57aa0d8886c7828d39b1b9b1f17f21b8 (diff) | |
download | qtlocation-mapboxgl-upstream/android-docs-automation.tar.gz |
Merge branch 'master' into android-docs-automationupstream/android-docs-automation
Diffstat (limited to 'platform')
-rw-r--r-- | platform/android/CHANGELOG.md | 1 | ||||
-rw-r--r-- | platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java | 2 | ||||
-rw-r--r-- | platform/darwin/src/MGLRasterDEMSource.mm | 11 | ||||
-rw-r--r-- | platform/darwin/src/MGLRasterTileSource.mm | 15 | ||||
-rw-r--r-- | platform/darwin/src/MGLRasterTileSource_Private.h | 4 | ||||
-rw-r--r-- | platform/darwin/test/MGLStyleTests.mm | 95 | ||||
-rw-r--r-- | platform/glfw/glfw_view.cpp | 103 | ||||
-rw-r--r-- | platform/glfw/glfw_view.hpp | 4 | ||||
-rw-r--r-- | platform/ios/CHANGELOG.md | 1 | ||||
-rw-r--r-- | platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m | 16 | ||||
-rw-r--r-- | platform/node/CHANGELOG.md | 3 | ||||
-rw-r--r-- | platform/node/src/node_feature.cpp | 6 | ||||
-rw-r--r-- | platform/node/src/node_map.cpp | 221 | ||||
-rw-r--r-- | platform/node/src/node_map.hpp | 4 | ||||
-rw-r--r-- | platform/node/test/ignores.json | 15 | ||||
-rw-r--r-- | platform/node/test/js/map.test.js | 3 | ||||
-rw-r--r-- | platform/node/test/suite_implementation.js | 6 |
17 files changed, 417 insertions, 93 deletions
diff --git a/platform/android/CHANGELOG.md b/platform/android/CHANGELOG.md index 734647e4c8..76704a44cf 100644 --- a/platform/android/CHANGELOG.md +++ b/platform/android/CHANGELOG.md @@ -8,6 +8,7 @@ Mapbox welcomes participation and contributions from everyone. If you'd like to - Fixed constant repainting for the sources with invisible layers, caused by `RenderSource::hasFadingTiles()` returning `true` all the time. [#15600](https://github.com/mapbox/mapbox-gl-native/pull/15600) - Fixed an issue that caused the state of CompassView not up to date when `UiSettings.setCompassEnabled()` is set to true. [#15606](https://github.com/mapbox/mapbox-gl-native/pull/15606) - Fixed an issue that `maxzoom` in style `Sources` option was ignored when URL resource is provided. It may cause problems such as extra tiles downloading at higher zoom level than `maxzoom`, or problems that wrong setting of `overscaledZ` in `OverscaledTileID` that will be passed to `SymbolLayout`, leading wrong rendering appearance. [#15581](https://github.com/mapbox/mapbox-gl-native/pull/15581) + - Fixed an assertion hit caused by possibility of adding a layer to an incompatible source. [#15644](https://github.com/mapbox/mapbox-gl-native/pull/15644) ## 8.4.0-alpha.2 - September 11, 2019 [Changes](https://github.com/mapbox/mapbox-gl-native/compare/android-v8.4.0-alpha.1...android-v8.4.0-alpha.2) since [Mapbox Maps SDK for Android v8.4.0-alpha.1](https://github.com/mapbox/mapbox-gl-native/releases/tag/android-v8.4.0-alpha.1): diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java index 1e0069c25f..990bd32262 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/snapshotter/MapSnapshotter.java @@ -235,7 +235,7 @@ public class MapSnapshotter { */ @NonNull public Options withApiBaseUri(String apiBaseUri) { - this.apiBaseUrl = apiBaseUrl; + this.apiBaseUrl = apiBaseUri; return this; } diff --git a/platform/darwin/src/MGLRasterDEMSource.mm b/platform/darwin/src/MGLRasterDEMSource.mm index 27614b9ef4..753499ff94 100644 --- a/platform/darwin/src/MGLRasterDEMSource.mm +++ b/platform/darwin/src/MGLRasterDEMSource.mm @@ -7,11 +7,10 @@ @implementation MGLRasterDEMSource -- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize { - NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString; - return std::make_unique<mbgl::style::RasterDEMSource>(identifier.UTF8String, - configurationURLString.UTF8String, - uint16_t(round(tileSize))); +- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset tileSize:(uint16_t)tileSize { + auto source = std::make_unique<mbgl::style::RasterDEMSource>(identifier.UTF8String, + urlOrTileset, + tileSize); + return source; } - @end diff --git a/platform/darwin/src/MGLRasterTileSource.mm b/platform/darwin/src/MGLRasterTileSource.mm index e89367711e..b31cee296f 100644 --- a/platform/darwin/src/MGLRasterTileSource.mm +++ b/platform/darwin/src/MGLRasterTileSource.mm @@ -33,15 +33,16 @@ static const CGFloat MGLRasterTileSourceRetinaTileSize = 512; } - (instancetype)initWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize { - auto source = [self pendingSourceWithIdentifier:identifier configurationURL:configurationURL tileSize:tileSize]; + NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString; + auto source = [self pendingSourceWithIdentifier:identifier urlOrTileset:configurationURLString.UTF8String tileSize:uint16_t(round(tileSize))]; return self = [super initWithPendingSource:std::move(source)]; } -- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize { - NSString *configurationURLString = configurationURL.mgl_URLByStandardizingScheme.absoluteString; - return std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String, - configurationURLString.UTF8String, - uint16_t(round(tileSize))); +- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset tileSize:(uint16_t)tileSize { + auto source = std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String, + urlOrTileset, + tileSize); + return source; } - (instancetype)initWithIdentifier:(NSString *)identifier tileURLTemplates:(NSArray<NSString *> *)tileURLTemplates options:(nullable NSDictionary<MGLTileSourceOption, id> *)options { @@ -56,7 +57,7 @@ static const CGFloat MGLRasterTileSourceRetinaTileSize = 512; tileSize = static_cast<uint16_t>(round(tileSizeNumber.doubleValue)); } - auto source = std::make_unique<mbgl::style::RasterSource>(identifier.UTF8String, tileSet, tileSize); + auto source = [self pendingSourceWithIdentifier:identifier urlOrTileset:tileSet tileSize:tileSize]; return self = [super initWithPendingSource:std::move(source)]; } diff --git a/platform/darwin/src/MGLRasterTileSource_Private.h b/platform/darwin/src/MGLRasterTileSource_Private.h index 8502b811e2..55f342c7ff 100644 --- a/platform/darwin/src/MGLRasterTileSource_Private.h +++ b/platform/darwin/src/MGLRasterTileSource_Private.h @@ -1,8 +1,10 @@ #import "MGLRasterTileSource.h" #include <memory> +#include <mbgl/util/variant.hpp> namespace mbgl { + class Tileset; namespace style { class RasterSource; } @@ -14,7 +16,7 @@ NS_ASSUME_NONNULL_BEGIN @property (nonatomic, readonly, nullable) mbgl::style::RasterSource *rawSource; -- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier configurationURL:(NSURL *)configurationURL tileSize:(CGFloat)tileSize; +- (std::unique_ptr<mbgl::style::RasterSource>)pendingSourceWithIdentifier:(NSString *)identifier urlOrTileset:(mbgl::variant<std::string, mbgl::Tileset>)urlOrTileset tileSize:(uint16_t)tileSize; @end diff --git a/platform/darwin/test/MGLStyleTests.mm b/platform/darwin/test/MGLStyleTests.mm index 7aaf70a80a..ec2605646c 100644 --- a/platform/darwin/test/MGLStyleTests.mm +++ b/platform/darwin/test/MGLStyleTests.mm @@ -229,23 +229,23 @@ - (void)testRemovingSourceInUse { // Add a raster tile source - MGLRasterTileSource *rasterTileSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil]; - [self.style addSource:rasterTileSource]; + MGLVectorTileSource *vectorTileSource = [[MGLVectorTileSource alloc] initWithIdentifier:@"some-identifier" tileURLTemplates:@[] options:nil]; + [self.style addSource:vectorTileSource]; // Add a layer using it - MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:rasterTileSource]; + MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fillLayer" source:vectorTileSource]; [self.style addLayer:fillLayer]; // Attempt to remove the raster tile source NSError *error; - BOOL result = [self.style removeSource:rasterTileSource error:&error]; + BOOL result = [self.style removeSource:vectorTileSource error:&error]; XCTAssertFalse(result); XCTAssertEqualObjects(error.domain, MGLErrorDomain); XCTAssertEqual(error.code, MGLErrorCodeSourceIsInUseCannotRemove); // Ensure it is still there - XCTAssertTrue([[self.style sourceWithIdentifier:rasterTileSource.identifier] isMemberOfClass:[MGLRasterTileSource class]]); + XCTAssertTrue([[self.style sourceWithIdentifier:vectorTileSource.identifier] isMemberOfClass:[MGLVectorTileSource class]]); } - (void)testLayers { @@ -311,54 +311,61 @@ } - (void)testRemovingLayerBeforeAddingSameLayer { - MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"shape-source-removing-before-adding" shape:nil options:nil]; - - // Attempting to find a layer with identifier will trigger an exception if the source associated with the layer is not added - [self.style addSource:source]; - - MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fill-layer" source:source]; - [self.style removeLayer:fillLayer]; - [self.style addLayer:fillLayer]; - XCTAssertNotNil([self.style layerWithIdentifier:fillLayer.identifier]); - - MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"raster-layer" source:source]; - [self.style removeLayer:rasterLayer]; - [self.style addLayer:rasterLayer]; - XCTAssertNotNil([self.style layerWithIdentifier:rasterLayer.identifier]); - - MGLSymbolStyleLayer *symbolLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"symbol-layer" source:source]; - [self.style removeLayer:symbolLayer]; - [self.style addLayer:symbolLayer]; - XCTAssertNotNil([self.style layerWithIdentifier:symbolLayer.identifier]); - - MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"line-layer" source:source]; - [self.style removeLayer:lineLayer]; - [self.style addLayer:lineLayer]; - XCTAssertNotNil([self.style layerWithIdentifier:lineLayer.identifier]); - - MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"circle-layer" source:source]; - [self.style removeLayer:circleLayer]; - [self.style addLayer:circleLayer]; - XCTAssertNotNil([self.style layerWithIdentifier:circleLayer.identifier]); - - MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"background-layer"]; - [self.style removeLayer:backgroundLayer]; - [self.style addLayer:backgroundLayer]; - XCTAssertNotNil([self.style layerWithIdentifier:backgroundLayer.identifier]); + { + MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"shape-source-removing-before-adding" shape:nil options:nil]; + + // Attempting to find a layer with identifier will trigger an exception if the source associated with the layer is not added + [self.style addSource:source]; + + MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"fill-layer" source:source]; + [self.style removeLayer:fillLayer]; + [self.style addLayer:fillLayer]; + XCTAssertNotNil([self.style layerWithIdentifier:fillLayer.identifier]); + + MGLSymbolStyleLayer *symbolLayer = [[MGLSymbolStyleLayer alloc] initWithIdentifier:@"symbol-layer" source:source]; + [self.style removeLayer:symbolLayer]; + [self.style addLayer:symbolLayer]; + XCTAssertNotNil([self.style layerWithIdentifier:symbolLayer.identifier]); + + MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"line-layer" source:source]; + [self.style removeLayer:lineLayer]; + [self.style addLayer:lineLayer]; + XCTAssertNotNil([self.style layerWithIdentifier:lineLayer.identifier]); + + MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"circle-layer" source:source]; + [self.style removeLayer:circleLayer]; + [self.style addLayer:circleLayer]; + XCTAssertNotNil([self.style layerWithIdentifier:circleLayer.identifier]); + + MGLBackgroundStyleLayer *backgroundLayer = [[MGLBackgroundStyleLayer alloc] initWithIdentifier:@"background-layer"]; + [self.style removeLayer:backgroundLayer]; + [self.style addLayer:backgroundLayer]; + XCTAssertNotNil([self.style layerWithIdentifier:backgroundLayer.identifier]); + } + + { + MGLRasterTileSource *rasterSource = [[MGLRasterTileSource alloc] initWithIdentifier:@"raster-tile-source" tileURLTemplates:@[] options:nil]; + [self.style addSource:rasterSource]; + + MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"raster-layer" source:rasterSource]; + [self.style removeLayer:rasterLayer]; + [self.style addLayer:rasterLayer]; + XCTAssertNotNil([self.style layerWithIdentifier:rasterLayer.identifier]); + } } - (void)testAddingLayerOfTypeABeforeRemovingLayerOfTypeBWithSameIdentifier { MGLShapeSource *source = [[MGLShapeSource alloc] initWithIdentifier:@"shape-source-identifier" shape:nil options:nil]; [self.style addSource:source]; - + // Add a fill layer MGLFillStyleLayer *fillLayer = [[MGLFillStyleLayer alloc] initWithIdentifier:@"some-identifier" source:source]; [self.style addLayer:fillLayer]; - + // Attempt to remove a line layer with the same identifier as the fill layer MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:fillLayer.identifier source:source]; [self.style removeLayer:lineLayer]; - + XCTAssertTrue([[self.style layerWithIdentifier:fillLayer.identifier] isMemberOfClass:[MGLFillStyleLayer class]]); } @@ -382,10 +389,10 @@ MGLImage *image = [[NSBundle bundleForClass:[self class]] imageForResource:imageName]; #endif XCTAssertNotNil(image); - + [self.style setImage:image forName:imageName]; MGLImage *styleImage = [self.style imageForName:imageName]; - + XCTAssertNotNil(styleImage); XCTAssertEqual(image.size.width, styleImage.size.width); XCTAssertEqual(image.size.height, styleImage.size.height); diff --git a/platform/glfw/glfw_view.cpp b/platform/glfw/glfw_view.cpp index 43c4de9759..c39b2c904a 100644 --- a/platform/glfw/glfw_view.cpp +++ b/platform/glfw/glfw_view.cpp @@ -4,22 +4,24 @@ #include "ny_route.hpp" #include <mbgl/annotation/annotation.hpp> -#include <mbgl/style/style.hpp> -#include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/gfx/backend.hpp> +#include <mbgl/gfx/backend_scope.hpp> +#include <mbgl/map/camera.hpp> +#include <mbgl/renderer/renderer.hpp> +#include <mbgl/style/expression/dsl.hpp> #include <mbgl/style/image.hpp> -#include <mbgl/style/transition_options.hpp> #include <mbgl/style/layers/fill_extrusion_layer.hpp> +#include <mbgl/style/layers/fill_layer.hpp> #include <mbgl/style/layers/line_layer.hpp> -#include <mbgl/style/expression/dsl.hpp> +#include <mbgl/style/sources/custom_geometry_source.hpp> +#include <mbgl/style/sources/geojson_source.hpp> +#include <mbgl/style/style.hpp> +#include <mbgl/style/transition_options.hpp> +#include <mbgl/util/chrono.hpp> +#include <mbgl/util/geo.hpp> #include <mbgl/util/logging.hpp> #include <mbgl/util/platform.hpp> #include <mbgl/util/string.hpp> -#include <mbgl/util/chrono.hpp> -#include <mbgl/util/geo.hpp> -#include <mbgl/renderer/renderer.hpp> -#include <mbgl/gfx/backend.hpp> -#include <mbgl/gfx/backend_scope.hpp> -#include <mbgl/map/camera.hpp> #include <mapbox/cheap_ruler.hpp> #include <mapbox/geometry.hpp> @@ -323,6 +325,52 @@ void GLFWView::onKey(GLFWwindow *window, int key, int /*scancode*/, int action, case GLFW_KEY_T: view->toggleCustomSource(); break; + case GLFW_KEY_F: { + using namespace mbgl; + using namespace mbgl::style; + using namespace mbgl::style::expression::dsl; + + auto &style = view->map->getStyle(); + if (!style.getSource("states")) { + std::string url = "https://docs.mapbox.com/mapbox-gl-js/assets/us_states.geojson"; + auto source = std::make_unique<GeoJSONSource>("states"); + source->setURL(url); + style.addSource(std::move(source)); + + mbgl::CameraOptions cameraOptions; + cameraOptions.center = mbgl::LatLng{42.619626, -103.523181}; + cameraOptions.zoom = 3; + cameraOptions.pitch = 0; + cameraOptions.bearing = 0; + view->map->jumpTo(cameraOptions); + } + + auto layer = style.getLayer("state-fills"); + if (!layer) { + auto fillLayer = std::make_unique<FillLayer>("state-fills", "states"); + fillLayer->setFillColor(mbgl::Color{0.0, 0.0, 1.0, 0.5}); + fillLayer->setFillOpacity(PropertyExpression<float>( + createExpression(R"(["case", ["boolean", ["feature-state", "hover"], false], 1, 0.5])"))); + style.addLayer(std::move(fillLayer)); + } else { + layer->setVisibility(layer->getVisibility() == mbgl::style::VisibilityType::Visible + ? mbgl::style::VisibilityType::None + : mbgl::style::VisibilityType::Visible); + } + + layer = style.getLayer("state-borders"); + if (!layer) { + auto borderLayer = std::make_unique<LineLayer>("state-borders", "states"); + borderLayer->setLineColor(mbgl::Color{0.0, 0.0, 1.0, 1.0}); + borderLayer->setLineWidth(PropertyExpression<float>( + createExpression(R"(["case", ["boolean", ["feature-state", "hover"], false], 2, 1])"))); + style.addLayer(std::move(borderLayer)); + } else { + layer->setVisibility(layer->getVisibility() == mbgl::style::VisibilityType::Visible + ? mbgl::style::VisibilityType::None + : mbgl::style::VisibilityType::Visible); + } + } break; } } @@ -558,6 +606,41 @@ void GLFWView::onMouseMove(GLFWwindow *window, double x, double y) { } view->lastX = x; view->lastY = y; + + auto &style = view->map->getStyle(); + if (style.getLayer("state-fills")) { + auto screenCoordinate = mbgl::ScreenCoordinate{view->lastX, view->lastY}; + const mbgl::RenderedQueryOptions queryOptions({{{"state-fills"}}, {}}); + auto result = view->rendererFrontend->getRenderer()->queryRenderedFeatures(screenCoordinate, queryOptions); + using namespace mbgl; + FeatureState newState; + + if (result.size() > 0) { + FeatureIdentifier id = result[0].id; + optional<std::string> idStr = featureIDtoString(id); + + if (idStr) { + if (view->featureID && (*view->featureID != *idStr)) { + newState["hover"] = false; + view->rendererFrontend->getRenderer()->setFeatureState("states", {}, *view->featureID, newState); + view->featureID = nullopt; + } + + if (!view->featureID) { + newState["hover"] = true; + view->featureID = featureIDtoString(id); + view->rendererFrontend->getRenderer()->setFeatureState("states", {}, *view->featureID, newState); + } + } + } else { + if (view->featureID) { + newState["hover"] = false; + view->rendererFrontend->getRenderer()->setFeatureState("states", {}, *view->featureID, newState); + view->featureID = nullopt; + } + } + view->invalidate(); + } } void GLFWView::onWindowFocus(GLFWwindow *window, int focused) { diff --git a/platform/glfw/glfw_view.hpp b/platform/glfw/glfw_view.hpp index 54b89ba2d9..dbe6ceb046 100644 --- a/platform/glfw/glfw_view.hpp +++ b/platform/glfw/glfw_view.hpp @@ -1,9 +1,10 @@ #pragma once #include <mbgl/map/map.hpp> +#include <mbgl/util/geometry.hpp> +#include <mbgl/util/optional.hpp> #include <mbgl/util/run_loop.hpp> #include <mbgl/util/timer.hpp> -#include <mbgl/util/geometry.hpp> struct GLFWwindow; class GLFWBackend; @@ -134,4 +135,5 @@ private: GLFWwindow *window = nullptr; bool dirty = false; + mbgl::optional<std::string> featureID; }; diff --git a/platform/ios/CHANGELOG.md b/platform/ios/CHANGELOG.md index f2394d1b84..089cc18606 100644 --- a/platform/ios/CHANGELOG.md +++ b/platform/ios/CHANGELOG.md @@ -7,6 +7,7 @@ Mapbox welcomes participation and contributions from everyone. Please read [CONT ### Styles and rendering * Added an `-[MGLMapSnapshotter startWithOverlayHandler:completionHandler:]` method to provide the snapshot's current `CGContext` in order to perform custom drawing on `MGLMapSnapShot` objects. ([#15530](https://github.com/mapbox/mapbox-gl-native/pull/15530)) * Fixed an issue that `maxzoom` in style `Sources` option was ignored when URL resource is provided. It may cause problems such as extra tiles downloading at higher zoom level than `maxzoom`, or problems that wrong setting of `overscaledZ` in `OverscaledTileID` that will be passed to `SymbolLayout`, leading wrong rendering appearance. ([#15581](https://github.com/mapbox/mapbox-gl-native/pull/15581)) +* Fixed an assertion hit caused by possibility of adding a layer to an incompatible source. ([#15644](https://github.com/mapbox/mapbox-gl-native/pull/15644)) ### Performance improvements diff --git a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m index c018c457b9..f5f2f957d3 100644 --- a/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m +++ b/platform/ios/Integration Tests/MGLStyleLayerIntegrationTests.m @@ -82,31 +82,31 @@ // Testing generated layers MGLLineStyleLayer *lineLayer = [[MGLLineStyleLayer alloc] initWithIdentifier:@"lineLayerID" source:source]; - MGLRasterStyleLayer *rasterLayer = [[MGLRasterStyleLayer alloc] initWithIdentifier:@"rasterLayerID" source:source]; + MGLCircleStyleLayer *circleLayer = [[MGLCircleStyleLayer alloc] initWithIdentifier:@"circleLayerID" source:source]; [self.mapView.style addSource:source]; [self.mapView.style addLayer:lineLayer]; - [self.mapView.style addLayer:rasterLayer]; + [self.mapView.style addLayer:circleLayer]; XCTAssertNoThrow(lineLayer.isVisible); - XCTAssertNoThrow(rasterLayer.isVisible); + XCTAssertNoThrow(circleLayer.isVisible); XCTAssert(![source.description containsString:@"<unknown>"]); XCTAssert(![lineLayer.description containsString:@"<unknown>"]); - XCTAssert(![rasterLayer.description containsString:@"<unknown>"]); + XCTAssert(![circleLayer.description containsString:@"<unknown>"]); self.styleLoadingExpectation = nil; [self.mapView setStyleURL:[[NSBundle bundleForClass:[self class]] URLForResource:@"one-liner" withExtension:@"json"]]; [self waitForMapViewToFinishLoadingStyleWithTimeout:10]; - + XCTAssert([source.description containsString:@"<unknown>"]); XCTAssert([lineLayer.description containsString:@"<unknown>"]); - XCTAssert([rasterLayer.description containsString:@"<unknown>"]); + XCTAssert([circleLayer.description containsString:@"<unknown>"]); XCTAssertThrowsSpecificNamed(lineLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated"); - XCTAssertThrowsSpecificNamed(rasterLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated"); + XCTAssertThrowsSpecificNamed(circleLayer.isVisible, NSException, MGLInvalidStyleLayerException, @"Layer should raise an exception if its core peer got invalidated"); XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:lineLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)"); - XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:rasterLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)"); + XCTAssertThrowsSpecificNamed([self.mapView.style removeLayer:circleLayer], NSException, NSInvalidArgumentException, @"Style should raise an exception when attempting to remove an invalid layer (e.g. if its core peer got invalidated)"); } @end diff --git a/platform/node/CHANGELOG.md b/platform/node/CHANGELOG.md index 486dd44e10..84fbff741c 100644 --- a/platform/node/CHANGELOG.md +++ b/platform/node/CHANGELOG.md @@ -1,3 +1,6 @@ +# master +* Add support for feature state APIs. ([#15480](https://github.com/mapbox/mapbox-gl-native/pull/15480)) + # 4.3.0 * Introduce `text-writing-mode` layout property for symbol layer ([#14932](https://github.com/mapbox/mapbox-gl-native/pull/14932)). The `text-writing-mode` layout property allows control over symbol's preferred writing mode. The new property value is an array, whose values are enumeration values from a ( `horizontal` | `vertical` ) set. * Fixed rendering and collision detection issues with using `text-variable-anchor` and `icon-text-fit` properties on the same layer ([#15367](https://github.com/mapbox/mapbox-gl-native/pull/15367)). diff --git a/platform/node/src/node_feature.cpp b/platform/node/src/node_feature.cpp index 2dfab686a7..646cc23338 100644 --- a/platform/node/src/node_feature.cpp +++ b/platform/node/src/node_feature.cpp @@ -167,6 +167,12 @@ v8::Local<v8::Object> toJS(const Feature& feature) { Nan::Set(result, Nan::New("id").ToLocalChecked(), FeatureIdentifier::visit(feature.id, ToValue())); } + Nan::Set(result, Nan::New("source").ToLocalChecked(), toJS(feature.source)); + if (!feature.sourceLayer.empty()) { + Nan::Set(result, Nan::New("sourceLayer").ToLocalChecked(), toJS(feature.sourceLayer)); + } + Nan::Set(result, Nan::New("state").ToLocalChecked(), toJS(feature.state)); + return scope.Escape(result); } diff --git a/platform/node/src/node_map.cpp b/platform/node/src/node_map.cpp index 641816dc00..7450f461f8 100644 --- a/platform/node/src/node_map.cpp +++ b/platform/node/src/node_map.cpp @@ -117,6 +117,9 @@ ParseError)JS").ToLocalChecked()).ToLocalChecked(); Nan::SetPrototypeMethod(tpl, "setAxonometric", SetAxonometric); Nan::SetPrototypeMethod(tpl, "setXSkew", SetXSkew); Nan::SetPrototypeMethod(tpl, "setYSkew", SetYSkew); + Nan::SetPrototypeMethod(tpl, "setFeatureState", SetFeatureState); + Nan::SetPrototypeMethod(tpl, "getFeatureState", GetFeatureState); + Nan::SetPrototypeMethod(tpl, "removeFeatureState", RemoveFeatureState); Nan::SetPrototypeMethod(tpl, "dumpDebugLogs", DumpDebugLogs); Nan::SetPrototypeMethod(tpl, "queryRenderedFeatures", QueryRenderedFeatures); @@ -1099,6 +1102,224 @@ void NodeMap::SetYSkew(const Nan::FunctionCallbackInfo<v8::Value>& info) { info.GetReturnValue().SetUndefined(); } +void NodeMap::SetFeatureState(const Nan::FunctionCallbackInfo<v8::Value>& info) { + using namespace mbgl; + using namespace mbgl::style::conversion; + + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() < 2) { + return Nan::ThrowTypeError("Two arguments required"); + } + + if (!info[0]->IsObject() || !info[1]->IsObject()) { + return Nan::ThrowTypeError("Both arguments must be objects"); + } + + std::string sourceID, featureID; + mbgl::optional<std::string> sourceLayerID; + auto feature = Nan::To<v8::Object>(info[0]).ToLocalChecked(); + if (Nan::Has(feature, Nan::New("source").ToLocalChecked()).FromJust()) { + auto sourceOption = Nan::Get(feature, Nan::New("source").ToLocalChecked()).ToLocalChecked(); + if (!sourceOption->IsString()) { + return Nan::ThrowTypeError("Requires feature.source property to be a string"); + } + sourceID = *Nan::Utf8String(sourceOption); + } else { + return Nan::ThrowTypeError("SetFeatureState: Requires feature.source property"); + } + + if (Nan::Has(feature, Nan::New("sourceLayer").ToLocalChecked()).FromJust()) { + auto sourceLayerOption = Nan::Get(feature, Nan::New("sourceLayer").ToLocalChecked()).ToLocalChecked(); + if (!sourceLayerOption->IsString()) { + return Nan::ThrowTypeError("SetFeatureState: Requires feature.sourceLayer property to be a string"); + } + sourceLayerID = {*Nan::Utf8String(sourceLayerOption)}; + } + + if (Nan::Has(feature, Nan::New("id").ToLocalChecked()).FromJust()) { + auto idOption = Nan::Get(feature, Nan::New("id").ToLocalChecked()).ToLocalChecked(); + if (!idOption->IsString() && !(idOption->IsNumber() || idOption->IsString())) { + return Nan::ThrowTypeError("Requires feature.id property to be a string or a number"); + } + featureID = *Nan::Utf8String(idOption); + } else { + return Nan::ThrowTypeError("SetFeatureState: Requires feature.id property"); + } + + Convertible state(info[1]); + + if (!isObject(state)) { + return Nan::ThrowTypeError("Feature state must be an object"); + } + + std::string sourceLayer = sourceLayerID.value_or(std::string()); + std::string stateKey; + Value stateValue; + bool valueParsed = false; + FeatureState newState; + + const std::function<optional<Error>(const std::string&, const Convertible&)> convertFn = + [&](const std::string& k, const Convertible& v) -> optional<Error> { + optional<Value> value = toValue(v); + if (value) { + stateValue = std::move(*value); + valueParsed = true; + } else if (isArray(v)) { + std::vector<Value> array; + std::size_t length = arrayLength(v); + array.reserve(length); + for (size_t i = 0; i < length; ++i) { + optional<Value> arrayVal = toValue(arrayMember(v, i)); + if (arrayVal) { + array.emplace_back(*arrayVal); + } + } + std::unordered_map<std::string, Value> result; + result[k] = std::move(array); + stateValue = std::move(result); + valueParsed = true; + return {}; + + } else if (isObject(v)) { + eachMember(v, convertFn); + } + if (!valueParsed) { + Nan::ThrowTypeError("Could not get feature state value"); + return nullopt; + } + stateKey = k; + newState[stateKey] = stateValue; + return nullopt; + }; + + eachMember(state, convertFn); + + try { + nodeMap->frontend->getRenderer()->setFeatureState(sourceID, sourceLayerID, featureID, newState); + } catch (const std::exception& ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + +void NodeMap::GetFeatureState(const Nan::FunctionCallbackInfo<v8::Value>& info) { + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() < 1) { + return Nan::ThrowTypeError("One argument required"); + } + + if (!info[0]->IsObject() || !info[1]->IsObject()) { + return Nan::ThrowTypeError("Argument must be object"); + } + + std::string sourceID, featureID; + mbgl::optional<std::string> sourceLayerID; + auto feature = Nan::To<v8::Object>(info[0]).ToLocalChecked(); + if (Nan::Has(feature, Nan::New("source").ToLocalChecked()).FromJust()) { + auto sourceOption = Nan::Get(feature, Nan::New("source").ToLocalChecked()).ToLocalChecked(); + if (!sourceOption->IsString()) { + return Nan::ThrowTypeError("Requires feature.source property to be a string"); + } + sourceID = *Nan::Utf8String(sourceOption); + } else { + return Nan::ThrowTypeError("GetFeatureState: Requires feature.source property"); + } + + if (Nan::Has(feature, Nan::New("sourceLayer").ToLocalChecked()).FromJust()) { + auto sourceLayerOption = Nan::Get(feature, Nan::New("sourceLayer").ToLocalChecked()).ToLocalChecked(); + if (!sourceLayerOption->IsString()) { + return Nan::ThrowTypeError("GetFeatureState: Requires feature.sourceLayer property to be a string"); + } + sourceLayerID = {*Nan::Utf8String(sourceLayerOption)}; + } + + if (Nan::Has(feature, Nan::New("id").ToLocalChecked()).FromJust()) { + auto idOption = Nan::Get(feature, Nan::New("id").ToLocalChecked()).ToLocalChecked(); + if (!idOption->IsString() && !(idOption->IsNumber() || idOption->IsString())) { + return Nan::ThrowTypeError("Requires feature.id property to be a string or a number"); + } + featureID = *Nan::Utf8String(idOption); + } else { + return Nan::ThrowTypeError("GetFeatureState: Requires feature.id property"); + } + + mbgl::FeatureState state; + try { + nodeMap->frontend->getRenderer()->getFeatureState(state, sourceID, sourceLayerID, featureID); + } catch (const std::exception& ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + +void NodeMap::RemoveFeatureState(const Nan::FunctionCallbackInfo<v8::Value>& info) { + auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); + if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); + + if (info.Length() < 1) { + return Nan::ThrowTypeError("At least one argument required"); + } + + if (!info[0]->IsObject()) { + return Nan::ThrowTypeError("Argument 1 must be object"); + } + + if (info.Length() == 2 && !info[1]->IsString()) { + return Nan::ThrowTypeError("argument 2 must be string"); + } + + std::string sourceID; + mbgl::optional<std::string> sourceLayerID, featureID, stateKey; + auto feature = Nan::To<v8::Object>(info[0]).ToLocalChecked(); + if (Nan::Has(feature, Nan::New("source").ToLocalChecked()).FromJust()) { + auto sourceOption = Nan::Get(feature, Nan::New("source").ToLocalChecked()).ToLocalChecked(); + if (!sourceOption->IsString()) { + return Nan::ThrowTypeError("Requires feature.source property to be a string"); + } + sourceID = *Nan::Utf8String(sourceOption); + } else { + return Nan::ThrowTypeError("RemoveFeatureState: Requires feature.source property"); + } + + if (Nan::Has(feature, Nan::New("sourceLayer").ToLocalChecked()).FromJust()) { + auto sourceLayerOption = Nan::Get(feature, Nan::New("sourceLayer").ToLocalChecked()).ToLocalChecked(); + if (!sourceLayerOption->IsString()) { + return Nan::ThrowTypeError("RemoveFeatureState: Requires feature.sourceLayer property to be a string"); + } + sourceLayerID = {*Nan::Utf8String(sourceLayerOption)}; + } + + if (Nan::Has(feature, Nan::New("id").ToLocalChecked()).FromJust()) { + auto idOption = Nan::Get(feature, Nan::New("id").ToLocalChecked()).ToLocalChecked(); + if (!idOption->IsString() && !(idOption->IsNumber() || idOption->IsString())) { + return Nan::ThrowTypeError("Requires feature.id property to be a string or a number"); + } + featureID = {*Nan::Utf8String(idOption)}; + } + + if (info.Length() == 2) { + auto keyParam = Nan::To<v8::String>(info[1]).ToLocalChecked(); + if (!keyParam->IsString()) { + return Nan::ThrowTypeError("RemoveFeatureState: Requires feature key property to be a string"); + } + stateKey = {*Nan::Utf8String(keyParam)}; + } + + try { + nodeMap->frontend->getRenderer()->removeFeatureState(sourceID, sourceLayerID, featureID, stateKey); + } catch (const std::exception& ex) { + return Nan::ThrowError(ex.what()); + } + + info.GetReturnValue().SetUndefined(); +} + void NodeMap::DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>& info) { auto nodeMap = Nan::ObjectWrap::Unwrap<NodeMap>(info.Holder()); if (!nodeMap->map) return Nan::ThrowError(releasedMessage()); diff --git a/platform/node/src/node_map.hpp b/platform/node/src/node_map.hpp index 486754c1c3..381e10381b 100644 --- a/platform/node/src/node_map.hpp +++ b/platform/node/src/node_map.hpp @@ -64,6 +64,10 @@ public: static void DumpDebugLogs(const Nan::FunctionCallbackInfo<v8::Value>&); static void QueryRenderedFeatures(const Nan::FunctionCallbackInfo<v8::Value>&); + static void SetFeatureState(const Nan::FunctionCallbackInfo<v8::Value>&); + static void GetFeatureState(const Nan::FunctionCallbackInfo<v8::Value>&); + static void RemoveFeatureState(const Nan::FunctionCallbackInfo<v8::Value>&); + static v8::Local<v8::Value> ParseError(const char* msg); void startRender(RenderOptions options); diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json index d32fb6b0c4..f053b2add1 100644 --- a/platform/node/test/ignores.json +++ b/platform/node/test/ignores.json @@ -20,15 +20,8 @@ "query-tests/geometry/multipolygon": "needs investigation", "query-tests/geometry/polygon": "needs investigation", "query-tests/world-wrapping/box": "skip - needs issue", - "query-tests/circle-radius/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", - "query-tests/circle-stroke-width/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/fill-extrusion-translate/multiple-layers": "https://github.com/mapbox/mapbox-gl-native/issues/12701", "query-tests/fill-translate/multiple-layers": "https://github.com/mapbox/mapbox-gl-native/issues/12701", - "query-tests/line-gap-width/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", - "query-tests/line-offset/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", - "query-tests/line-offset/pattern-feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", - "query-tests/line-width/feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", - "query-tests/feature-state/default": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "query-tests/regressions/mapbox-gl-js#6555": "skip - no querySourceFeatures in mbgl-node; needs issue", "render-tests/background-color/transition": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/canvas/default": "skip - js specific", @@ -52,7 +45,6 @@ "render-tests/fill-extrusion-pattern/opacity": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/fill-extrusion-pattern/feature-expression": "https://github.com/mapbox/mapbox-gl-js/issues/3327", "render-tests/fill-extrusion-pattern/tile-buffer": "https://github.com/mapbox/mapbox-gl-js/issues/3327", - "render-tests/fill-pattern/update-feature-state": "skip - port https://github.com/mapbox/mapbox-gl-js/pull/6263 - needs issue", "render-tests/geojson/inline-linestring-fill": "current behavior is arbitrary", "render-tests/mixed-zoom/z10-z11": "https://github.com/mapbox/mapbox-gl-native/issues/10397", "render-tests/raster-masking/overlapping-zoom": "https://github.com/mapbox/mapbox-gl-native/issues/10195", @@ -73,19 +65,12 @@ "render-tests/symbol-cross-fade/chinese": "https://github.com/mapbox/mapbox-gl-native/issues/10619", "render-tests/video/default": "skip - https://github.com/mapbox/mapbox-gl-native/issues/601", "render-tests/background-color/colorSpace-hcl": "needs issue", - "render-tests/feature-state/composite-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12613", - "render-tests/feature-state/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12613", - "render-tests/feature-state/vector-source": "https://github.com/mapbox/mapbox-gl-native/issues/12613", "render-tests/text-variable-anchor/remember-last-placement": "skip - fails on gl-native, as symbol index is not functional at static map mode - needs issue", - "render-tests/remove-feature-state/composite-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12413", - "render-tests/remove-feature-state/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/12413", - "render-tests/remove-feature-state/vector-source": "https://github.com/mapbox/mapbox-gl-native/issues/12413", "render-tests/regressions/mapbox-gl-js#8026": "skip - js specific", "render-tests/fill-extrusion-geometry/linestring": "https://github.com/mapbox/mapbox-gl-native/pull/14240", "render-tests/circle-sort-key/literal": "https://github.com/mapbox/mapbox-gl-native/issues/15008", "render-tests/fill-sort-key/literal": "https://github.com/mapbox/mapbox-gl-native/issues/15008", "render-tests/line-sort-key/literal": "https://github.com/mapbox/mapbox-gl-native/issues/15008", - "query-tests/remove-feature-state/default": "https://github.com/mapbox/mapbox-gl-native/issues/12413", "query-tests/fill-extrusion/base-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", "query-tests/fill-extrusion/box-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", "query-tests/fill-extrusion/side-in": "https://github.com/mapbox/mapbox-gl-native/issues/13139", diff --git a/platform/node/test/js/map.test.js b/platform/node/test/js/map.test.js index b21c1519e3..d085e32f4c 100644 --- a/platform/node/test/js/map.test.js +++ b/platform/node/test/js/map.test.js @@ -126,6 +126,9 @@ test('Map', function(t) { 'setAxonometric', 'setXSkew', 'setYSkew', + 'setFeatureState', + 'getFeatureState', + 'removeFeatureState', 'dumpDebugLogs', 'queryRenderedFeatures' ]); diff --git a/platform/node/test/suite_implementation.js b/platform/node/test/suite_implementation.js index f868af8ece..386bd41092 100644 --- a/platform/node/test/suite_implementation.js +++ b/platform/node/test/suite_implementation.js @@ -122,6 +122,12 @@ export default function (style, options, callback) { map.load(operation[1]); applyOperations(operations.slice(1), callback); + } else if (operation[0] ==='setFeatureState' || operation[0] ==='getFeatureState' || operation[0] ==='removeFeatureState') { + map.render(options, function () { + map[operation[0]].apply(map, operation.slice(1)); + applyOperations(operations.slice(1), callback); + }); + } else { // Ensure that the next `map.render(options)` does not overwrite this change. if (operation[0] === 'setCenter') { |