summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/mbgl/style/conversion/make_property_setters.hpp1
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp4
-rw-r--r--include/mbgl/util/size.hpp4
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/Property.java26
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java23
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/SymbolLayer.java12
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/light/Light.java20
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/CircleLayerTest.java54
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/LineLayerTest.java181
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java48
-rw-r--r--platform/android/src/style/layers/symbol_layer.cpp7
-rw-r--r--platform/android/src/style/layers/symbol_layer.hpp2
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h52
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm33
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm43
-rw-r--r--src/mbgl/layout/symbol_layout.cpp5
-rw-r--r--src/mbgl/programs/symbol_program.cpp7
-rw-r--r--src/mbgl/programs/symbol_program.hpp10
-rw-r--r--src/mbgl/renderer/layers/render_symbol_layer.cpp2
-rw-r--r--src/mbgl/shaders/symbol_icon.cpp17
-rw-r--r--src/mbgl/shaders/symbol_sdf.cpp19
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp16
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp6
23 files changed, 466 insertions, 126 deletions
diff --git a/include/mbgl/style/conversion/make_property_setters.hpp b/include/mbgl/style/conversion/make_property_setters.hpp
index 1c26aeb5e4..f29d5f5b6f 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<V, SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
result["icon-keep-upright"] = &setProperty<V, SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
result["icon-offset"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::array<float, 2>>, &SymbolLayer::setIconOffset>;
+ result["icon-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
result["text-pitch-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
result["text-rotation-alignment"] = &setProperty<V, SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
result["text-field"] = &setProperty<V, SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index 8158f267c9..f4d0322dc7 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -98,6 +98,10 @@ public:
DataDrivenPropertyValue<std::array<float, 2>> getIconOffset() const;
void setIconOffset(DataDrivenPropertyValue<std::array<float, 2>>);
+ static PropertyValue<AlignmentType> getDefaultIconPitchAlignment();
+ PropertyValue<AlignmentType> getIconPitchAlignment() const;
+ void setIconPitchAlignment(PropertyValue<AlignmentType>);
+
static PropertyValue<AlignmentType> getDefaultTextPitchAlignment();
PropertyValue<AlignmentType> getTextPitchAlignment() const;
void setTextPitchAlignment(PropertyValue<AlignmentType>);
diff --git a/include/mbgl/util/size.hpp b/include/mbgl/util/size.hpp
index 45c303969c..12c0ad056b 100644
--- a/include/mbgl/util/size.hpp
+++ b/include/mbgl/util/size.hpp
@@ -15,6 +15,10 @@ public:
constexpr uint32_t area() const {
return width * height;
}
+
+ constexpr float aspectRatio() const {
+ return static_cast<float>(width) / static_cast<float>(height);
+ }
constexpr bool isEmpty() const {
return width == 0 || height == 0;
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 d3d0e060dc..be24b65d27 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 c12d6b7133..1c68878772 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
@@ -1882,6 +1882,29 @@ public class PropertyFactory {
}
/**
+ * Orientation of icon when map is pitched.
+ *
+ * @param value a String value
+ * @return property wrapper around String
+ */
+ public static PropertyValue<String> iconPitchAlignment(@Property.ICON_PITCH_ALIGNMENT String value) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", value);
+ }
+
+
+
+ /**
+ * Orientation of icon when map is pitched.
+ *
+ * @param <Z> the zoom parameter type
+ * @param function a wrapper {@link CameraFunction} for String
+ * @return property wrapper around a String function
+ */
+ public static <Z extends Number> PropertyValue<CameraFunction<Z, String>> iconPitchAlignment(CameraFunction<Z, String> function) {
+ return new LayoutPropertyValue<>("icon-pitch-alignment", function);
+ }
+
+ /**
* Orientation of text when map is pitched.
*
* @param value a String value
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 290e162da8..fe81dcb638 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
@@ -252,6 +252,16 @@ public class SymbolLayer extends Layer {
}
/**
+ * Get the IconPitchAlignment property
+ *
+ * @return property wrapper value around String
+ */
+ @SuppressWarnings("unchecked")
+ public PropertyValue<String> getIconPitchAlignment() {
+ return (PropertyValue<String>) new PropertyValue("icon-pitch-alignment", nativeGetIconPitchAlignment());
+ }
+
+ /**
* Get the TextPitchAlignment property
*
* @return property wrapper value around String
@@ -891,6 +901,8 @@ public class SymbolLayer extends Layer {
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 cb6465a6b1..8f23e7d01e 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 class Light {
*
* @return anchor as String
*/
- @Property.ANCHOR
- public String getAnchor() {
+ @Property.ANCHOR public String getAnchor() {
return nativeGetAnchor();
}
@@ -107,7 +106,7 @@ public class Light {
*
* @return color as String
*/
- public String getColor() {
+ public String getColor() {
return nativeGetColor();
}
@@ -143,7 +142,7 @@ public class Light {
*
* @return intensity as Float
*/
- public float getIntensity() {
+ public float getIntensity() {
return nativeGetIntensity();
}
@@ -166,30 +165,17 @@ public class Light {
}
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 84f4c16801..559e446307 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 class CircleLayerTest extends BaseActivityTest {
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 class CircleLayerTest extends BaseActivityTest {
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 234bc583d7..7bdf47aff4 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 class LineLayerTest extends BaseActivityTest {
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.<Float>identity()))
- );
+ // Set
+ layer.setProperties(
+ lineWidth(property("FeaturePropertyA", Stops.<Float>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 class LineLayerTest extends BaseActivityTest {
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 class LineLayerTest extends BaseActivityTest {
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 class LineLayerTest extends BaseActivityTest {
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<Stop.CompositeValue<Float, Float>, Float> stops =
- (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineWidth().getFunction().getStops();
- Stop<Stop.CompositeValue<Float, Float>, 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<Stop.CompositeValue<Float, Float>, Float> stops =
+ (ExponentialStops<Stop.CompositeValue<Float, Float>, Float>) layer.getLineWidth().getFunction().getStops();
+ Stop<Stop.CompositeValue<Float, Float>, 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 b0854f4a47..fc8c4320a5 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
@@ -1214,6 +1214,54 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@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();
setupLayer();
diff --git a/platform/android/src/style/layers/symbol_layer.cpp b/platform/android/src/style/layers/symbol_layer.cpp
index 3a560a5deb..b6cf51ec7e 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<jni::ObjectTag>(*converted);
}
+ jni::Object<jni::ObjectTag> SymbolLayer::getIconPitchAlignment(jni::JNIEnv& env) {
+ using namespace mbgl::android::conversion;
+ Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->SymbolLayer::getIconPitchAlignment());
+ return jni::Object<jni::ObjectTag>(*converted);
+ }
+
jni::Object<jni::ObjectTag> SymbolLayer::getTextPitchAlignment(jni::JNIEnv& env) {
using namespace mbgl::android::conversion;
Result<jni::jobject*> converted = convert<jni::jobject*>(env, layer.as<mbgl::style::SymbolLayer>()->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 8366051c6e..6d3da13ae9 100644
--- a/platform/android/src/style/layers/symbol_layer.hpp
+++ b/platform/android/src/style/layers/symbol_layer.hpp
@@ -59,6 +59,8 @@ public:
jni::Object<jni::ObjectTag> getIconOffset(jni::JNIEnv&);
+ jni::Object<jni::ObjectTag> getIconPitchAlignment(jni::JNIEnv&);
+
jni::Object<jni::ObjectTag> getTextPitchAlignment(jni::JNIEnv&);
jni::Object<jni::ObjectTag> getTextRotationAlignment(jni::JNIEnv&);
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index 5df995aa01..f8df073efe 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -8,6 +8,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.
@@ -477,6 +498,24 @@ MGL_EXPORT
@property (nonatomic, null_resettable) MGLStyleValue<NSNumber *> *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<NSValue *> *iconPitchAlignment;
+
+/**
Rotates the icon clockwise.
This property is measured in degrees.
@@ -1925,6 +1964,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.
@param iconRotationAlignment The value for the new object.
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 5a8f8c6084..dd43ebd73c 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 @@ namespace mbgl {
return MGLStyleValueTransformer<float, NSNumber *>().toStyleValue(propertyValue);
}
+- (void)setIconPitchAlignment:(MGLStyleValue<NSValue *> *)iconPitchAlignment {
+ MGLAssertStyleLayerIsValid();
+
+ auto mbglValue = MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumPropertyValue(iconPitchAlignment);
+ self.rawLayer->setIconPitchAlignment(mbglValue);
+}
+
+- (MGLStyleValue<NSValue *> *)iconPitchAlignment {
+ MGLAssertStyleLayerIsValid();
+
+ auto propertyValue = self.rawLayer->getIconPitchAlignment();
+ if (propertyValue.isUndefined()) {
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(self.rawLayer->getDefaultIconPitchAlignment());
+ }
+ return MGLStyleValueTransformer<mbgl::style::AlignmentType, NSValue *, mbgl::style::AlignmentType, MGLIconPitchAlignment>().toEnumStyleValue(propertyValue);
+}
+
- (void)setIconRotation:(MGLStyleValue<NSNumber *> *)iconRotation {
MGLAssertStyleLayerIsValid();
@@ -1321,6 +1344,16 @@ namespace mbgl {
@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 367ebf363c..5e969e27ca 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -301,6 +301,45 @@
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<NSValue *> *defaultStyleValue = layer.iconPitchAlignment;
+
+ MGLStyleValue<NSValue *> *constantStyleValue = [MGLStyleValue<NSValue *> valueWithRawValue:[NSValue valueWithMGLIconPitchAlignment:MGLIconPitchAlignmentAuto]];
+ layer.iconPitchAlignment = constantStyleValue;
+ mbgl::style::PropertyValue<mbgl::style::AlignmentType> 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<NSValue *> * functionStyleValue = [MGLStyleValue<NSValue *> valueWithInterpolationMode:MGLInterpolationModeInterval cameraStops:@{@18: constantStyleValue} options:nil];
+ layer.iconPitchAlignment = functionStyleValue;
+
+ mbgl::style::IntervalStops<mbgl::style::AlignmentType> intervalStops = { {{18, mbgl::style::AlignmentType::Auto}} };
+ propertyValue = mbgl::style::CameraFunction<mbgl::style::AlignmentType> { 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<NSValue *> 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<NSValue *> 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 @@
[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)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 e308da618f..956ba770dd 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -75,10 +75,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
}
}
- // If unspecified `text-pitch-alignment` inherits `text-rotation-alignment`
+ // If unspecified `*-pitch-alignment` inherits `*-rotation-alignment`
if (layout.get<TextPitchAlignment>() == AlignmentType::Auto) {
layout.get<TextPitchAlignment>() = layout.get<TextRotationAlignment>();
}
+ if (layout.get<IconPitchAlignment>() == AlignmentType::Auto) {
+ layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>();
+ }
const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty();
const bool hasIcon = has<IconImage>(layout);
diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp
index 8790adcc63..58174ff8a7 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>(args)...
};
}
diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp
index 79a961ad21..c74837b121 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 @@ public:
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 2fe6dd971e..2af7b2f7ca 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<style::IconRotationAlignment>(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment
+ layout_.get<style::IconPitchAlignment>(),
layout_.get<style::IconRotationAlignment>(),
layout_.get<style::IconKeepUpright>(),
evaluated.get<style::IconTranslate>(),
diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp
index cb00cdad05..c0e3a0ac01 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 b4158bacc5..2050886957 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 182b4b0a48..c102c64a94 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<std::array<float, 2>> va
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
+PropertyValue<AlignmentType> SymbolLayer::getDefaultIconPitchAlignment() {
+ return IconPitchAlignment::defaultValue();
+}
+
+PropertyValue<AlignmentType> SymbolLayer::getIconPitchAlignment() const {
+ return impl().layout.get<IconPitchAlignment>();
+}
+
+void SymbolLayer::setIconPitchAlignment(PropertyValue<AlignmentType> value) {
+ if (value == getIconPitchAlignment())
+ return;
+ auto impl_ = mutableImpl();
+ impl_->layout.get<IconPitchAlignment>() = value;
+ baseImpl = std::move(impl_);
+ observer->onLayerChanged(*this);
+}
PropertyValue<AlignmentType> 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 f7ceaecdaa..4b2bff01b8 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<std::array<float, 2>> {
static std::array<float, 2> defaultValue() { return {{ 0, 0 }}; }
};
+struct IconPitchAlignment : LayoutProperty<AlignmentType> {
+ static constexpr const char * key = "icon-pitch-alignment";
+ static AlignmentType defaultValue() { return AlignmentType::Auto; }
+};
+
struct TextPitchAlignment : LayoutProperty<AlignmentType> {
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,