From 0ec9633b39e9fd55b878970add2eefcd7a95266d Mon Sep 17 00:00:00 2001 From: Chris Loer Date: Tue, 11 Jul 2017 11:22:39 -0700 Subject: [PATCH] [core] Update shaders. Implements 'icon-pitch-alignment' (issue #9345) Fixes issue #9456 (map-aligned point label regression) --- .../conversion/make_property_setters.hpp | 1 + include/mbgl/style/layers/symbol_layer.hpp | 4 + include/mbgl/util/size.hpp | 4 + mapbox-gl-js | 2 +- .../mapboxsdk/style/layers/Property.java | 26 +++ .../style/layers/PropertyFactory.java | 23 +++ .../mapboxsdk/style/layers/SymbolLayer.java | 12 ++ .../mapbox/mapboxsdk/style/light/Light.java | 20 +- .../testapp/style/CircleLayerTest.java | 54 +++--- .../testapp/style/LineLayerTest.java | 181 ++++++++++-------- .../testapp/style/SymbolLayerTest.java | 48 +++++ .../android/src/style/layers/symbol_layer.cpp | 7 + .../android/src/style/layers/symbol_layer.hpp | 2 + platform/darwin/src/MGLSymbolStyleLayer.h | 52 +++++ platform/darwin/src/MGLSymbolStyleLayer.mm | 33 ++++ .../darwin/test/MGLSymbolStyleLayerTests.mm | 43 +++++ src/mbgl/layout/symbol_layout.cpp | 6 +- src/mbgl/programs/symbol_program.cpp | 7 + src/mbgl/programs/symbol_program.hpp | 10 +- .../renderer/layers/render_symbol_layer.cpp | 2 +- src/mbgl/shaders/preludes.cpp | 23 +-- src/mbgl/shaders/symbol_icon.cpp | 17 +- src/mbgl/shaders/symbol_sdf.cpp | 19 +- src/mbgl/style/layers/symbol_layer.cpp | 16 ++ .../style/layers/symbol_layer_properties.hpp | 6 + 25 files changed, 470 insertions(+), 148 deletions(-) diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp index 1c26aeb5e48..f29d5f5b6fb 100644 --- a/include/mbgl/style/conversion/make_property_setters.hpp +++ b/include/mbgl/style/conversion/make_property_setters.hpp @@ -45,6 +45,7 @@ auto makeLayoutPropertySetters() { result["icon-padding"] = &setProperty, &SymbolLayer::setIconPadding>; result["icon-keep-upright"] = &setProperty, &SymbolLayer::setIconKeepUpright>; result["icon-offset"] = &setProperty>, &SymbolLayer::setIconOffset>; + result["icon-pitch-alignment"] = &setProperty, &SymbolLayer::setIconPitchAlignment>; result["text-pitch-alignment"] = &setProperty, &SymbolLayer::setTextPitchAlignment>; result["text-rotation-alignment"] = &setProperty, &SymbolLayer::setTextRotationAlignment>; result["text-field"] = &setProperty, &SymbolLayer::setTextField>; diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 8158f267c9b..f4d0322dc71 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -98,6 +98,10 @@ class SymbolLayer : public Layer { DataDrivenPropertyValue> getIconOffset() const; void setIconOffset(DataDrivenPropertyValue>); + static PropertyValue getDefaultIconPitchAlignment(); + PropertyValue getIconPitchAlignment() const; + void setIconPitchAlignment(PropertyValue); + static PropertyValue getDefaultTextPitchAlignment(); PropertyValue getTextPitchAlignment() const; void setTextPitchAlignment(PropertyValue); diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp index 45c303969c5..12c0ad056bd 100644 --- a/include/mbgl/util/size.hpp +++ b/include/mbgl/util/size.hpp @@ -15,6 +15,10 @@ class Size { constexpr uint32_t area() const { return width * height; } + + constexpr float aspectRatio() const { + return static_cast(width) / static_cast(height); + } constexpr bool isEmpty() const { return width == 0 || height == 0; diff --git a/mapbox-gl-js b/mapbox-gl-js index c2e061bda53..d670d0a0809 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit c2e061bda532ea848ed39550fe3aa4f95847cc90 +Subproject commit d670d0a08096b5ed41c0ca5c0762f057666b622d diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java index d3d0e060dc5..be24b65d277 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java @@ -160,6 +160,32 @@ public final class Property { @Retention(RetentionPolicy.SOURCE) public @interface ICON_TEXT_FIT {} + // ICON_PITCH_ALIGNMENT: Orientation of icon when map is pitched. + + /** + * The icon is aligned to the plane of the map. + */ + public static final String ICON_PITCH_ALIGNMENT_MAP = "map"; + /** + * The icon is aligned to the plane of the viewport. + */ + public static final String ICON_PITCH_ALIGNMENT_VIEWPORT = "viewport"; + /** + * Automatically matches the value of {@link ICON_ROTATION_ALIGNMENT}. + */ + public static final String ICON_PITCH_ALIGNMENT_AUTO = "auto"; + + /** + * Orientation of icon when map is pitched. + */ + @StringDef({ + ICON_PITCH_ALIGNMENT_MAP, + ICON_PITCH_ALIGNMENT_VIEWPORT, + ICON_PITCH_ALIGNMENT_AUTO, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface ICON_PITCH_ALIGNMENT {} + // TEXT_PITCH_ALIGNMENT: Orientation of text when map is pitched. /** diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java index c12d6b7133b..1c68878772b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java @@ -1881,6 +1881,29 @@ public static PropertyValue> iconOffset(Function("icon-offset", function); } + /** + * Orientation of icon when map is pitched. + * + * @param value a String value + * @return property wrapper around String + */ + public static PropertyValue iconPitchAlignment(@Property.ICON_PITCH_ALIGNMENT String value) { + return new LayoutPropertyValue<>("icon-pitch-alignment", value); + } + + + + /** + * Orientation of icon when map is pitched. + * + * @param the zoom parameter type + * @param function a wrapper {@link CameraFunction} for String + * @return property wrapper around a String function + */ + public static PropertyValue> iconPitchAlignment(CameraFunction function) { + return new LayoutPropertyValue<>("icon-pitch-alignment", function); + } + /** * Orientation of text when map is pitched. * diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java index 290e162da85..fe81dcb638b 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java @@ -251,6 +251,16 @@ public PropertyValue getIconOffset() { return (PropertyValue) new PropertyValue("icon-offset", nativeGetIconOffset()); } + /** + * Get the IconPitchAlignment property + * + * @return property wrapper value around String + */ + @SuppressWarnings("unchecked") + public PropertyValue getIconPitchAlignment() { + return (PropertyValue) new PropertyValue("icon-pitch-alignment", nativeGetIconPitchAlignment()); + } + /** * Get the TextPitchAlignment property * @@ -891,6 +901,8 @@ public PropertyValue getTextTranslateAnchor() { private native Object nativeGetIconOffset(); + private native Object nativeGetIconPitchAlignment(); + private native Object nativeGetTextPitchAlignment(); private native Object nativeGetTextRotationAlignment(); diff --git a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java index cb6465a6b13..8f23e7d01ec 100644 --- a/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java +++ b/platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java @@ -43,8 +43,7 @@ public void setAnchor(@Property.ANCHOR String anchor) { * * @return anchor as String */ - @Property.ANCHOR - public String getAnchor() { + @Property.ANCHOR public String getAnchor() { return nativeGetAnchor(); } @@ -107,7 +106,7 @@ public void setColor(String color) { * * @return color as String */ - public String getColor() { + public String getColor() { return nativeGetColor(); } @@ -143,7 +142,7 @@ public void setIntensity(float intensity) { * * @return intensity as Float */ - public float getIntensity() { + public float getIntensity() { return nativeGetIntensity(); } @@ -166,30 +165,17 @@ public void setIntensityTransition(TransitionOptions options) { } private native void nativeSetAnchor(String anchor); - private native String nativeGetAnchor(); - private native void nativeSetPosition(Position position); - private native Position nativeGetPosition(); - private native TransitionOptions nativeGetPositionTransition(); - private native void nativeSetPositionTransition(long duration, long delay); - private native void nativeSetColor(String color); - private native String nativeGetColor(); - private native TransitionOptions nativeGetColorTransition(); - private native void nativeSetColorTransition(long duration, long delay); - private native void nativeSetIntensity(float intensity); - private native float nativeGetIntensity(); - private native TransitionOptions nativeGetIntensityTransition(); - private native void nativeSetIntensityTransition(long duration, long delay); } \ No newline at end of file diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java index 84f4c168015..559e4463074 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java @@ -1052,11 +1052,16 @@ public void testCirclePitchAlignmentAsConstant() { validateTestSetup(); setupLayer(); Timber.i("circle-pitch-alignment"); - assertNotNull(layer); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); - // Set and Get - layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP)); - assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP); + // Set and Get + layer.setProperties(circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP)); + assertEquals((String) layer.getCirclePitchAlignment().getValue(), (String) CIRCLE_PITCH_ALIGNMENT_MAP); + } + }); } @Test @@ -1064,25 +1069,30 @@ public void testCirclePitchAlignmentAsCameraFunction() { validateTestSetup(); setupLayer(); Timber.i("circle-pitch-alignment"); - assertNotNull(layer); - - // Set - layer.setProperties( - circlePitchAlignment( - zoom( - interval( - stop(2, circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP)) + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + circlePitchAlignment( + zoom( + interval( + stop(2, circlePitchAlignment(CIRCLE_PITCH_ALIGNMENT_MAP)) + ) + ) ) - ) - ) - ); - - // Verify - assertNotNull(layer.getCirclePitchAlignment()); - assertNotNull(layer.getCirclePitchAlignment().getFunction()); - assertEquals(CameraFunction.class, layer.getCirclePitchAlignment().getFunction().getClass()); - assertEquals(IntervalStops.class, layer.getCirclePitchAlignment().getFunction().getStops().getClass()); - assertEquals(1, ((IntervalStops) layer.getCirclePitchAlignment().getFunction().getStops()).size()); + ); + + // Verify + assertNotNull(layer.getCirclePitchAlignment()); + assertNotNull(layer.getCirclePitchAlignment().getFunction()); + assertEquals(CameraFunction.class, layer.getCirclePitchAlignment().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getCirclePitchAlignment().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getCirclePitchAlignment().getFunction().getStops()).size()); + } + }); } @Test diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java index 234bc583d73..7bdf47aff43 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java @@ -865,19 +865,24 @@ public void testLineWidthAsIdentitySourceFunction() { validateTestSetup(); setupLayer(); Timber.i("line-width"); - assertNotNull(layer); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); - // Set - layer.setProperties( - lineWidth(property("FeaturePropertyA", Stops.identity())) - ); + // Set + layer.setProperties( + lineWidth(property("FeaturePropertyA", Stops.identity())) + ); - // Verify - assertNotNull(layer.getLineWidth()); - assertNotNull(layer.getLineWidth().getFunction()); - assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); - assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); - assertEquals(IdentityStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + } + }); } @Test @@ -885,26 +890,31 @@ public void testLineWidthAsExponentialSourceFunction() { validateTestSetup(); setupLayer(); Timber.i("line-width"); - assertNotNull(layer); - - // Set - layer.setProperties( - lineWidth( - property( - "FeaturePropertyA", - exponential( - stop(0.3f, lineWidth(0.3f)) - ).withBase(0.5f) - ) - ) - ); - - // Verify - assertNotNull(layer.getLineWidth()); - assertNotNull(layer.getLineWidth().getFunction()); - assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); - assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); - assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, lineWidth(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + } + }); } @Test @@ -912,29 +922,35 @@ public void testLineWidthAsCategoricalSourceFunction() { validateTestSetup(); setupLayer(); Timber.i("line-width"); - assertNotNull(layer); - - // Set - layer.setProperties( - lineWidth( - property( - "FeaturePropertyA", - categorical( - stop(1.0f, lineWidth(0.3f)) + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, lineWidth(0.3f)) + ) + ).withDefaultValue(lineWidth(0.3f)) ) - ).withDefaultValue(lineWidth(0.3f)) - ) - ); + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue()); + assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue()); + assertEquals(0.3f, ((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue()); + } + }); - // Verify - assertNotNull(layer.getLineWidth()); - assertNotNull(layer.getLineWidth().getFunction()); - assertEquals(SourceFunction.class, layer.getLineWidth().getFunction().getClass()); - assertEquals("FeaturePropertyA", ((SourceFunction) layer.getLineWidth().getFunction()).getProperty()); - assertEquals(CategoricalStops.class, layer.getLineWidth().getFunction().getStops().getClass()); - assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue()); - assertNotNull(((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue()); - assertEquals(0.3f, ((SourceFunction) layer.getLineWidth().getFunction()).getDefaultValue().getValue()); } @Test @@ -942,34 +958,39 @@ public void testLineWidthAsCompositeFunction() { validateTestSetup(); setupLayer(); Timber.i("line-width"); - assertNotNull(layer); - - // Set - layer.setProperties( - lineWidth( - composite( - "FeaturePropertyA", - exponential( - stop(0, 0.3f, lineWidth(0.9f)) - ).withBase(0.5f) - ).withDefaultValue(lineWidth(0.3f)) - ) - ); - - // Verify - assertNotNull(layer.getLineWidth()); - assertNotNull(layer.getLineWidth().getFunction()); - assertEquals(CompositeFunction.class, layer.getLineWidth().getFunction().getClass()); - assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineWidth().getFunction()).getProperty()); - assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass()); - assertEquals(1, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).size()); - - ExponentialStops, Float> stops = - (ExponentialStops, Float>) layer.getLineWidth().getFunction().getStops(); - Stop, Float> stop = stops.iterator().next(); - assertEquals(0f, stop.in.zoom, 0.001); - assertEquals(0.3f, stop.in.value, 0.001f); - assertEquals(0.9f, stop.out, 0.001f); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + lineWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, lineWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(lineWidth(0.3f)) + ) + ); + + // Verify + assertNotNull(layer.getLineWidth()); + assertNotNull(layer.getLineWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getLineWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getLineWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getLineWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getLineWidth().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getLineWidth().getFunction().getStops(); + Stop, Float> stop = stops.iterator().next(); + assertEquals(0f, stop.in.zoom, 0.001); + assertEquals(0.3f, stop.in.value, 0.001f); + assertEquals(0.9f, stop.out, 0.001f); + } + }); } @Test diff --git a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java index b0854f4a470..fc8c4320a51 100644 --- a/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java +++ b/platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java @@ -1213,6 +1213,54 @@ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { }); } + @Test + public void testIconPitchAlignmentAsConstant() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-pitch-alignment"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set and Get + layer.setProperties(iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP)); + assertEquals((String) layer.getIconPitchAlignment().getValue(), (String) ICON_PITCH_ALIGNMENT_MAP); + } + }); + } + + @Test + public void testIconPitchAlignmentAsCameraFunction() { + validateTestSetup(); + setupLayer(); + Timber.i("icon-pitch-alignment"); + invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() { + @Override + public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) { + assertNotNull(layer); + + // Set + layer.setProperties( + iconPitchAlignment( + zoom( + interval( + stop(2, iconPitchAlignment(ICON_PITCH_ALIGNMENT_MAP)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconPitchAlignment()); + assertNotNull(layer.getIconPitchAlignment().getFunction()); + assertEquals(CameraFunction.class, layer.getIconPitchAlignment().getFunction().getClass()); + assertEquals(IntervalStops.class, layer.getIconPitchAlignment().getFunction().getStops().getClass()); + assertEquals(1, ((IntervalStops) layer.getIconPitchAlignment().getFunction().getStops()).size()); + } + }); + } + @Test public void testTextPitchAlignmentAsConstant() { validateTestSetup(); diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp index 3a560a5deb1..b6cf51ec7e4 100644 --- a/platform/android/src/style/layers/symbol_layer.cpp +++ b/platform/android/src/style/layers/symbol_layer.cpp @@ -125,6 +125,12 @@ namespace android { return jni::Object(*converted); } + jni::Object SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) { + using namespace mbgl::android::conversion; + Result converted = convert(env, layer.as()->SymbolLayer::getIconPitchAlignment()); + return jni::Object(*converted); + } + jni::Object SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) { using namespace mbgl::android::conversion; Result converted = convert(env, layer.as()->SymbolLayer::getTextPitchAlignment()); @@ -514,6 +520,7 @@ namespace android { METHOD(&SymbolLayer::getIconPadding, "nativeGetIconPadding"), METHOD(&SymbolLayer::getIconKeepUpright, "nativeGetIconKeepUpright"), METHOD(&SymbolLayer::getIconOffset, "nativeGetIconOffset"), + METHOD(&SymbolLayer::getIconPitchAlignment, "nativeGetIconPitchAlignment"), METHOD(&SymbolLayer::getTextPitchAlignment, "nativeGetTextPitchAlignment"), METHOD(&SymbolLayer::getTextRotationAlignment, "nativeGetTextRotationAlignment"), METHOD(&SymbolLayer::getTextField, "nativeGetTextField"), diff --git a/platform/android/src/style/layers/symbol_layer.hpp b/platform/android/src/style/layers/symbol_layer.hpp index 8366051c6ee..6d3da13ae98 100644 --- a/platform/android/src/style/layers/symbol_layer.hpp +++ b/platform/android/src/style/layers/symbol_layer.hpp @@ -59,6 +59,8 @@ class SymbolLayer : public Layer { jni::Object getIconOffset(jni::JNIEnv&); + jni::Object getIconPitchAlignment(jni::JNIEnv&); + jni::Object getTextPitchAlignment(jni::JNIEnv&); jni::Object getTextRotationAlignment(jni::JNIEnv&); diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index 5df995aa018..f8df073efe6 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -7,6 +7,27 @@ NS_ASSUME_NONNULL_BEGIN +/** + Orientation of icon when map is pitched. + + Values of this type are used in the `MGLSymbolStyleLayer.iconPitchAlignment` + property. + */ +typedef NS_ENUM(NSUInteger, MGLIconPitchAlignment) { + /** + The icon is aligned to the plane of the map. + */ + MGLIconPitchAlignmentMap, + /** + The icon is aligned to the plane of the viewport. + */ + MGLIconPitchAlignmentViewport, + /** + Automatically matches the value of `iconRotationAlignment`. + */ + MGLIconPitchAlignmentAuto, +}; + /** In combination with `symbolPlacement`, determines the rotation behavior of icons. @@ -476,6 +497,24 @@ MGL_EXPORT */ @property (nonatomic, null_resettable) MGLStyleValue *iconPadding; +/** + Orientation of icon when map is pitched. + + The default value of this property is an `MGLStyleValue` object containing an + `NSValue` object containing `MGLIconPitchAlignmentAuto`. Set this property to + `nil` to reset it to the default value. + + This property is only applied to the style if `iconImageName` is non-`nil`. + Otherwise, it is ignored. + + You can set this property to an instance of: + + * `MGLConstantStyleValue` + * `MGLCameraStyleFunction` with an interpolation mode of + `MGLInterpolationModeInterval` + */ +@property (nonatomic, null_resettable) MGLStyleValue *iconPitchAlignment; + /** Rotates the icon clockwise. @@ -1924,6 +1963,19 @@ MGL_EXPORT #pragma mark Working with Symbol Style Layer Attribute Values +/** + Creates a new value object containing the given `MGLIconPitchAlignment` enumeration. + + @param iconPitchAlignment The value for the new object. + @return A new value object that contains the enumeration value. + */ ++ (instancetype)valueWithMGLIconPitchAlignment:(MGLIconPitchAlignment)iconPitchAlignment; + +/** + The `MGLIconPitchAlignment` enumeration representation of the value. + */ +@property (readonly) MGLIconPitchAlignment MGLIconPitchAlignmentValue; + /** Creates a new value object containing the given `MGLIconRotationAlignment` enumeration. diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 5a8f8c60847..dd43ebd73c5 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -13,6 +13,12 @@ namespace mbgl { + MBGL_DEFINE_ENUM(MGLIconPitchAlignment, { + { MGLIconPitchAlignmentMap, "map" }, + { MGLIconPitchAlignmentViewport, "viewport" }, + { MGLIconPitchAlignmentAuto, "auto" }, + }); + MBGL_DEFINE_ENUM(MGLIconRotationAlignment, { { MGLIconRotationAlignmentMap, "map" }, { MGLIconRotationAlignmentViewport, "viewport" }, @@ -259,6 +265,23 @@ - (void)setIconPadding:(MGLStyleValue *)iconPadding { return MGLStyleValueTransformer().toStyleValue(propertyValue); } +- (void)setIconPitchAlignment:(MGLStyleValue *)iconPitchAlignment { + MGLAssertStyleLayerIsValid(); + + auto mbglValue = MGLStyleValueTransformer().toEnumPropertyValue(iconPitchAlignment); + self.rawLayer->setIconPitchAlignment(mbglValue); +} + +- (MGLStyleValue *)iconPitchAlignment { + MGLAssertStyleLayerIsValid(); + + auto propertyValue = self.rawLayer->getIconPitchAlignment(); + if (propertyValue.isUndefined()) { + return MGLStyleValueTransformer().toEnumStyleValue(self.rawLayer->getDefaultIconPitchAlignment()); + } + return MGLStyleValueTransformer().toEnumStyleValue(propertyValue); +} + - (void)setIconRotation:(MGLStyleValue *)iconRotation { MGLAssertStyleLayerIsValid(); @@ -1321,6 +1344,16 @@ - (void)setTextTranslateAnchor:(MGLStyleValue *)textTranslateAnchor { @implementation NSValue (MGLSymbolStyleLayerAdditions) ++ (NSValue *)valueWithMGLIconPitchAlignment:(MGLIconPitchAlignment)iconPitchAlignment { + return [NSValue value:&iconPitchAlignment withObjCType:@encode(MGLIconPitchAlignment)]; +} + +- (MGLIconPitchAlignment)MGLIconPitchAlignmentValue { + MGLIconPitchAlignment iconPitchAlignment; + [self getValue:&iconPitchAlignment]; + return iconPitchAlignment; +} + + (NSValue *)valueWithMGLIconRotationAlignment:(MGLIconRotationAlignment)iconRotationAlignment { return [NSValue value:&iconRotationAlignment withObjCType:@encode(MGLIconRotationAlignment)]; } diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index 367ebf363c8..5e969e27ca0 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -301,6 +301,45 @@ - (void)testProperties { XCTAssertThrowsSpecificNamed(layer.iconPadding = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } + // icon-pitch-alignment + { + XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(), + @"icon-pitch-alignment should be unset initially."); + MGLStyleValue *defaultStyleValue = layer.iconPitchAlignment; + + MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:[NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto]]; + layer.iconPitchAlignment = constantStyleValue; + mbgl::style::PropertyValue propertyValue = { mbgl::style::AlignmentType::Auto }; + XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue, + @"Setting iconPitchAlignment to a constant value should update icon-pitch-alignment."); + XCTAssertEqualObjects(layer.iconPitchAlignment, constantStyleValue, + @"iconPitchAlignment should round-trip constant values."); + + MGLStyleValue * functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil]; + layer.iconPitchAlignment = functionStyleValue; + + mbgl::style::IntervalStops intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} }; + propertyValue = mbgl::style::CameraFunction { intervalStops }; + + XCTAssertEqual(rawLayer->getIconPitchAlignment(), propertyValue, + @"Setting iconPitchAlignment to a camera function should update icon-pitch-alignment."); + XCTAssertEqualObjects(layer.iconPitchAlignment, functionStyleValue, + @"iconPitchAlignment should round-trip camera functions."); + + + + layer.iconPitchAlignment = nil; + XCTAssertTrue(rawLayer->getIconPitchAlignment().isUndefined(), + @"Unsetting iconPitchAlignment should return icon-pitch-alignment to the default value."); + XCTAssertEqualObjects(layer.iconPitchAlignment, defaultStyleValue, + @"iconPitchAlignment should return the default value after being unset."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil]; + XCTAssertThrowsSpecificNamed(layer.iconPitchAlignment = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); + } + // icon-rotate { XCTAssertTrue(rawLayer->getIconRotate().isUndefined(), @@ -2321,6 +2360,7 @@ - (void)testPropertyNames { [self testPropertyName:@"icon-offset" isBoolean:NO]; [self testPropertyName:@"is-icon-optional" isBoolean:YES]; [self testPropertyName:@"icon-padding" isBoolean:NO]; + [self testPropertyName:@"icon-pitch-alignment" isBoolean:NO]; [self testPropertyName:@"icon-rotation" isBoolean:NO]; [self testPropertyName:@"icon-rotation-alignment" isBoolean:NO]; [self testPropertyName:@"icon-scale" isBoolean:NO]; @@ -2366,6 +2406,9 @@ - (void)testPropertyNames { } - (void)testValueAdditions { + XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentMap].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentMap); + XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentViewport].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentViewport); + XCTAssertEqual([NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto].MGLIconPitchAlignmentValue, MGLIconPitchAlignmentAuto); XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentMap].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentMap); XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentViewport].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentViewport); XCTAssertEqual([NSValue valueWithMGLIconRotationAlignment:MGLIconRotationAlignmentAuto].MGLIconRotationAlignmentValue, MGLIconRotationAlignmentAuto); diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index e308da618ff..e51ce2a3544 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -75,10 +75,14 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } } - // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment` + // If unspecified `*-pitch-alignment` inherits `*-rotation-alignment` if (layout.get() == AlignmentType::Auto) { layout.get() = layout.get(); } + // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment` + if (layout.get() == AlignmentType::Auto) { + layout.get() = layout.get(); + } const bool hasText = has(layout) && !layout.get().empty(); const bool hasIcon = has(layout); diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 8790adcc63c..58174ff8a7e 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -52,6 +52,11 @@ Values makeValues(const bool isText, const float pixelsToTileUnits = tile.id.pixelsToTileUnits(1.0, state.getZoom()); const bool pitchWithMap = values.pitchAlignment == style::AlignmentType::Map; const bool rotateWithMap = values.rotationAlignment == style::AlignmentType::Map; + + // Line label rotation happens in `updateLineLabels` + // Pitched point labels are automatically rotated by the labelPlaneMatrix projection + // Unpitched point labels need to have their rotation applied after projection + const bool rotateInShader = rotateWithMap && !pitchWithMap && !alongLine; mat4 labelPlaneMatrix; if (alongLine) { @@ -84,6 +89,8 @@ Values makeValues(const bool isText, uniforms::u_pitch::Value{ state.getPitch() }, uniforms::u_pitch_with_map::Value{ pitchWithMap }, uniforms::u_max_camera_distance::Value{ values.maxCameraDistance }, + uniforms::u_rotate_symbol::Value{ rotateInShader }, + uniforms::u_aspect_ratio::Value{ state.getSize().aspectRatio() }, std::forward(args)... }; } diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index 79a961ad218..c74837b121c 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -41,6 +41,8 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_size_feature_constant); MBGL_DEFINE_UNIFORM_SCALAR(float, u_size_t); MBGL_DEFINE_UNIFORM_SCALAR(float, u_size); MBGL_DEFINE_UNIFORM_SCALAR(float, u_max_camera_distance); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_symbol); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< @@ -348,7 +350,9 @@ class SymbolIconProgram : public SymbolProgram< uniforms::u_camera_to_center_distance, uniforms::u_pitch, uniforms::u_pitch_with_map, - uniforms::u_max_camera_distance>, + uniforms::u_max_camera_distance, + uniforms::u_rotate_symbol, + uniforms::u_aspect_ratio>, style::IconPaintProperties> { public: @@ -387,6 +391,8 @@ class SymbolSDFProgram : public SymbolProgram< uniforms::u_pitch, uniforms::u_pitch_with_map, uniforms::u_max_camera_distance, + uniforms::u_rotate_symbol, + uniforms::u_aspect_ratio, uniforms::u_gamma_scale, uniforms::u_is_halo>, PaintProperties> @@ -409,6 +415,8 @@ class SymbolSDFProgram : public SymbolProgram< uniforms::u_pitch, uniforms::u_pitch_with_map, uniforms::u_max_camera_distance, + uniforms::u_rotate_symbol, + uniforms::u_aspect_ratio, uniforms::u_gamma_scale, uniforms::u_is_halo>, PaintProperties>; diff --git a/src/mbgl/renderer/layers/render_symbol_layer.cpp b/src/mbgl/renderer/layers/render_symbol_layer.cpp index 2fe6dd971e9..2af7b2f7ca5 100644 --- a/src/mbgl/renderer/layers/render_symbol_layer.cpp +++ b/src/mbgl/renderer/layers/render_symbol_layer.cpp @@ -82,7 +82,7 @@ style::TextPaintProperties::PossiblyEvaluated RenderSymbolLayer::textPaintProper style::SymbolPropertyValues RenderSymbolLayer::iconPropertyValues(const style::SymbolLayoutProperties::PossiblyEvaluated& layout_) const { return style::SymbolPropertyValues { - layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment + layout_.get(), layout_.get(), layout_.get(), evaluated.get(), diff --git a/src/mbgl/shaders/preludes.cpp b/src/mbgl/shaders/preludes.cpp index 95fa624e8de..feb185a684f 100644 --- a/src/mbgl/shaders/preludes.cpp +++ b/src/mbgl/shaders/preludes.cpp @@ -24,25 +24,6 @@ precision highp float; #endif -float evaluate_zoom_function_1(const vec4 values, const float t) { - if (t < 1.0) { - return mix(values[0], values[1], t); - } else if (t < 2.0) { - return mix(values[1], values[2], t - 1.0); - } else { - return mix(values[2], values[3], t - 2.0); - } -} -vec4 evaluate_zoom_function_4(const vec4 value0, const vec4 value1, const vec4 value2, const vec4 value3, const float t) { - if (t < 1.0) { - return mix(value0, value1, t); - } else if (t < 2.0) { - return mix(value1, value2, t - 1.0); - } else { - return mix(value2, value3, t - 2.0); - } -} - // Unpack a pair of values that have been packed into a single float. // The packed values are assumed to be 8-bit unsigned integers, and are // packed like so: @@ -54,8 +35,8 @@ vec2 unpack_float(const float packedValue) { } -// To minimize the number of attributes needed in the mapbox-gl-native shaders, -// we encode a 4-component color into a pair of floats (i.e. a vec2) as follows: +// To minimize the number of attributes needed, we encode a 4-component +// color into a pair of floats (i.e. a vec2) as follows: // [ floor(color.r * 255) * 256 + color.g * 255, // floor(color.b * 255) * 256 + color.g * 255 ] vec4 decode_color(const vec2 encodedColor) { diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index cb00cdad05c..c0e3a0ac013 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -19,6 +19,8 @@ uniform highp float u_size_t; // used to interpolate between zoom stops when siz uniform highp float u_size; // used when size is both zoom and feature constant uniform highp float u_camera_to_center_distance; uniform highp float u_pitch; +uniform bool u_rotate_symbol; +uniform highp float u_aspect_ratio; uniform highp float u_collision_y_stretch; @@ -83,8 +85,19 @@ void main() { float fontScale = u_is_text ? size / 24.0 : size; - highp float angle_sin = sin(segment_angle); - highp float angle_cos = cos(segment_angle); + highp float symbol_rotation = 0.0; + if (u_rotate_symbol) { + // See comments in symbol_sdf.vertex + vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1); + + vec2 a = projectedPoint.xy / projectedPoint.w; + vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + + symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x); + } + + highp float angle_sin = sin(segment_angle + symbol_rotation); + highp float angle_cos = cos(segment_angle + symbol_rotation); mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index b4158bacc58..20508869575 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -73,6 +73,8 @@ uniform mat4 u_gl_coord_matrix; uniform bool u_is_text; uniform bool u_pitch_with_map; uniform highp float u_pitch; +uniform bool u_rotate_symbol; +uniform highp float u_aspect_ratio; uniform highp float u_camera_to_center_distance; uniform highp float u_collision_y_stretch; @@ -151,8 +153,21 @@ void main() { float fontScale = u_is_text ? size / 24.0 : size; - highp float angle_sin = sin(segment_angle); - highp float angle_cos = cos(segment_angle); + highp float symbol_rotation = 0.0; + if (u_rotate_symbol) { + // Point labels with 'rotation-alignment: map' are horizontal with respect to tile units + // To figure out that angle in projected space, we draw a short horizontal line in tile + // space, project it, and measure its angle in projected space. + vec4 offsetProjectedPoint = u_matrix * vec4(a_pos + vec2(1, 0), 0, 1); + + vec2 a = projectedPoint.xy / projectedPoint.w; + vec2 b = offsetProjectedPoint.xy / offsetProjectedPoint.w; + + symbol_rotation = atan((b.y - a.y) / u_aspect_ratio, b.x - a.x); + } + + highp float angle_sin = sin(segment_angle + symbol_rotation); + highp float angle_cos = cos(segment_angle + symbol_rotation); mat2 rotation_matrix = mat2(angle_cos, -1.0 * angle_sin, angle_sin, angle_cos); vec4 projected_pos = u_label_plane_matrix * vec4(a_projected_pos.xy, 0.0, 1.0); diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index 182b4b0a488..c102c64a949 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -332,6 +332,22 @@ void SymbolLayer::setIconOffset(DataDrivenPropertyValue> va baseImpl = std::move(impl_); observer->onLayerChanged(*this); } +PropertyValue SymbolLayer::getDefaultIconPitchAlignment() { + return IconPitchAlignment::defaultValue(); +} + +PropertyValue SymbolLayer::getIconPitchAlignment() const { + return impl().layout.get(); +} + +void SymbolLayer::setIconPitchAlignment(PropertyValue value) { + if (value == getIconPitchAlignment()) + return; + auto impl_ = mutableImpl(); + impl_->layout.get() = value; + baseImpl = std::move(impl_); + observer->onLayerChanged(*this); +} PropertyValue SymbolLayer::getDefaultTextPitchAlignment() { return TextPitchAlignment::defaultValue(); } diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index f7ceaecdaad..4b2bff01b85 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -87,6 +87,11 @@ struct IconOffset : DataDrivenLayoutProperty> { static std::array defaultValue() { return {{ 0, 0 }}; } }; +struct IconPitchAlignment : LayoutProperty { + static constexpr const char * key = "icon-pitch-alignment"; + static AlignmentType defaultValue() { return AlignmentType::Auto; } +}; + struct TextPitchAlignment : LayoutProperty { static constexpr const char * key = "text-pitch-alignment"; static AlignmentType defaultValue() { return AlignmentType::Auto; } @@ -254,6 +259,7 @@ class SymbolLayoutProperties : public Properties< IconPadding, IconKeepUpright, IconOffset, + IconPitchAlignment, TextPitchAlignment, TextRotationAlignment, TextField,