From f901e776b3e63aaaa6bc0cc4476624bf84127fe6 Mon Sep 17 00:00:00 2001 From: Anand Thakker Date: Tue, 28 Feb 2017 19:54:24 -0800 Subject: [core] Implement data-driven styling for {text,icon}-{color,opacity,halo-color,halo-blur,halo-width} (#7939) * Add symbol dds attributes and adapt style code generation * Update to mapbox-gl-js/master * Refactor SymbolFeature as a subclass of GeometryTileFeature Prepares for enabling DDS on symbol paint properties by allowing the SymbolFeatures, which we keep around after constructing SymbolLayout, to be used in evaluating data-driven paint properties later in the layout process. * Draft approach for splitting icon/text paint properties The `Program` types are set up to bind GL attributes to each of the data-driven paint properties specified in the `PaintProperties` type provided. Since `SymbolPaintProperties` specifies both `Text*` and `Icon*` properties, the symbolIcon, symbolIconSDF, and symbolGlyph programs each attempt to bind roughly double the number of attributes that they actually need. This change addresses this by: - Adding the more specific `IconPaintProperties` and `TextPaintProperties` types, which are subsets of the full `SymbolPaintProperties`. - The symbol layer continues to use its `SymbolPaintProperties paint` member to track layer property state, but it provides helpers that construct objects of each the specific `{Icon,Text}PaintProperties::Evaluated` type, for use by the painter. - The three symbol programs instantiate `Program<>` using the appropriate `{Icon,Text}PaintProperties` type. * check in generated style code * Populate paint buffers for symbol DDS properties * Address first round of review comments * Refactor VectorTile{Layer,Feature} to explicitly share data * Update submodule --- include/mbgl/style/layers/symbol_layer.hpp | 60 +- mapbox-gl-js | 2 +- .../mapboxsdk/style/layers/PropertyFactory.java | 64 +- .../mapboxsdk/testapp/style/SymbolLayerTest.java | 994 ++++++++++++++++++++- platform/darwin/src/MGLSymbolStyleLayer.h | 126 +++ platform/darwin/src/MGLSymbolStyleLayer.mm | 60 +- platform/darwin/test/MGLSymbolStyleLayerTests.mm | 300 +++++-- scripts/generate-style-code.js | 15 +- src/mbgl/layout/symbol_feature.hpp | 17 +- src/mbgl/layout/symbol_instance.cpp | 5 +- src/mbgl/layout/symbol_instance.hpp | 3 +- src/mbgl/layout/symbol_layout.cpp | 51 +- src/mbgl/layout/symbol_layout.hpp | 7 +- src/mbgl/programs/attributes.hpp | 46 + src/mbgl/programs/programs.hpp | 4 +- src/mbgl/programs/symbol_program.cpp | 88 +- src/mbgl/programs/symbol_program.hpp | 77 +- src/mbgl/renderer/painter_symbol.cpp | 46 +- src/mbgl/renderer/symbol_bucket.cpp | 12 +- src/mbgl/renderer/symbol_bucket.hpp | 10 +- src/mbgl/shaders/symbol_icon.cpp | 13 +- src/mbgl/shaders/symbol_sdf.cpp | 63 +- src/mbgl/style/layers/symbol_layer.cpp | 132 ++- src/mbgl/style/layers/symbol_layer_impl.cpp | 56 +- src/mbgl/style/layers/symbol_layer_impl.hpp | 44 +- src/mbgl/style/layers/symbol_layer_properties.hpp | 20 +- src/mbgl/text/shaping.cpp | 9 +- src/mbgl/text/shaping.hpp | 3 +- src/mbgl/tile/vector_tile.cpp | 70 +- test/tile/vector_tile.test.cpp | 6 +- test/util/merge_lines.test.cpp | 136 +-- 31 files changed, 2023 insertions(+), 516 deletions(-) diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp index 8f655d6f77..aeccabb97e 100644 --- a/include/mbgl/style/layers/symbol_layer.hpp +++ b/include/mbgl/style/layers/symbol_layer.hpp @@ -169,29 +169,29 @@ public: // Paint properties - static PropertyValue getDefaultIconOpacity(); - PropertyValue getIconOpacity(const optional& klass = {}) const; - void setIconOpacity(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultIconOpacity(); + DataDrivenPropertyValue getIconOpacity(const optional& klass = {}) const; + void setIconOpacity(DataDrivenPropertyValue, const optional& klass = {}); void setIconOpacityTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultIconColor(); - PropertyValue getIconColor(const optional& klass = {}) const; - void setIconColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultIconColor(); + DataDrivenPropertyValue getIconColor(const optional& klass = {}) const; + void setIconColor(DataDrivenPropertyValue, const optional& klass = {}); void setIconColorTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultIconHaloColor(); - PropertyValue getIconHaloColor(const optional& klass = {}) const; - void setIconHaloColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultIconHaloColor(); + DataDrivenPropertyValue getIconHaloColor(const optional& klass = {}) const; + void setIconHaloColor(DataDrivenPropertyValue, const optional& klass = {}); void setIconHaloColorTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultIconHaloWidth(); - PropertyValue getIconHaloWidth(const optional& klass = {}) const; - void setIconHaloWidth(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultIconHaloWidth(); + DataDrivenPropertyValue getIconHaloWidth(const optional& klass = {}) const; + void setIconHaloWidth(DataDrivenPropertyValue, const optional& klass = {}); void setIconHaloWidthTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultIconHaloBlur(); - PropertyValue getIconHaloBlur(const optional& klass = {}) const; - void setIconHaloBlur(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultIconHaloBlur(); + DataDrivenPropertyValue getIconHaloBlur(const optional& klass = {}) const; + void setIconHaloBlur(DataDrivenPropertyValue, const optional& klass = {}); void setIconHaloBlurTransition(const TransitionOptions&, const optional& klass = {}); static PropertyValue> getDefaultIconTranslate(); @@ -204,29 +204,29 @@ public: void setIconTranslateAnchor(PropertyValue, const optional& klass = {}); void setIconTranslateAnchorTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultTextOpacity(); - PropertyValue getTextOpacity(const optional& klass = {}) const; - void setTextOpacity(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultTextOpacity(); + DataDrivenPropertyValue getTextOpacity(const optional& klass = {}) const; + void setTextOpacity(DataDrivenPropertyValue, const optional& klass = {}); void setTextOpacityTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultTextColor(); - PropertyValue getTextColor(const optional& klass = {}) const; - void setTextColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultTextColor(); + DataDrivenPropertyValue getTextColor(const optional& klass = {}) const; + void setTextColor(DataDrivenPropertyValue, const optional& klass = {}); void setTextColorTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultTextHaloColor(); - PropertyValue getTextHaloColor(const optional& klass = {}) const; - void setTextHaloColor(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultTextHaloColor(); + DataDrivenPropertyValue getTextHaloColor(const optional& klass = {}) const; + void setTextHaloColor(DataDrivenPropertyValue, const optional& klass = {}); void setTextHaloColorTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultTextHaloWidth(); - PropertyValue getTextHaloWidth(const optional& klass = {}) const; - void setTextHaloWidth(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultTextHaloWidth(); + DataDrivenPropertyValue getTextHaloWidth(const optional& klass = {}) const; + void setTextHaloWidth(DataDrivenPropertyValue, const optional& klass = {}); void setTextHaloWidthTransition(const TransitionOptions&, const optional& klass = {}); - static PropertyValue getDefaultTextHaloBlur(); - PropertyValue getTextHaloBlur(const optional& klass = {}) const; - void setTextHaloBlur(PropertyValue, const optional& klass = {}); + static DataDrivenPropertyValue getDefaultTextHaloBlur(); + DataDrivenPropertyValue getTextHaloBlur(const optional& klass = {}) const; + void setTextHaloBlur(DataDrivenPropertyValue, const optional& klass = {}); void setTextHaloBlurTransition(const TransitionOptions&, const optional& klass = {}); static PropertyValue> getDefaultTextTranslate(); diff --git a/mapbox-gl-js b/mapbox-gl-js index fd73395ef7..7804faf897 160000 --- a/mapbox-gl-js +++ b/mapbox-gl-js @@ -1 +1 @@ -Subproject commit fd73395ef71cf77993d717b2e23c5dab5e372b61 +Subproject commit 7804faf89741cf7db90582d9e0881253cb9d4cd8 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 03980e6a8b..5cd0d99270 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 @@ -454,11 +454,11 @@ public class PropertyFactory { /** * The opacity at which the icon will be drawn. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static PropertyValue> iconOpacity(CameraFunction function) { + public static PropertyValue> iconOpacity(Function function) { return new PaintPropertyValue<>("icon-opacity", function); } @@ -486,11 +486,11 @@ public class PropertyFactory { /** * The color of the icon. This can only be used with sdf icons. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static PropertyValue> iconColor(CameraFunction function) { + public static PropertyValue> iconColor(Function function) { return new PaintPropertyValue<>("icon-color", function); } @@ -518,11 +518,11 @@ public class PropertyFactory { /** * The color of the icon's halo. Icon halos can only be used with SDF icons. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static PropertyValue> iconHaloColor(CameraFunction function) { + public static PropertyValue> iconHaloColor(Function function) { return new PaintPropertyValue<>("icon-halo-color", function); } @@ -540,11 +540,11 @@ public class PropertyFactory { /** * Distance of halo to the icon outline. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static PropertyValue> iconHaloWidth(CameraFunction function) { + public static PropertyValue> iconHaloWidth(Function function) { return new PaintPropertyValue<>("icon-halo-width", function); } @@ -562,11 +562,11 @@ public class PropertyFactory { /** * Fade out the halo towards the outside. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static PropertyValue> iconHaloBlur(CameraFunction function) { + public static PropertyValue> iconHaloBlur(Function function) { return new PaintPropertyValue<>("icon-halo-blur", function); } @@ -628,11 +628,11 @@ public class PropertyFactory { /** * The opacity at which the text will be drawn. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static PropertyValue> textOpacity(CameraFunction function) { + public static PropertyValue> textOpacity(Function function) { return new PaintPropertyValue<>("text-opacity", function); } @@ -660,11 +660,11 @@ public class PropertyFactory { /** * The color with which the text will be drawn. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static PropertyValue> textColor(CameraFunction function) { + public static PropertyValue> textColor(Function function) { return new PaintPropertyValue<>("text-color", function); } @@ -692,11 +692,11 @@ public class PropertyFactory { /** * The color of the text's halo, which helps it stand out from backgrounds. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for String + * @param the function input type + * @param function a wrapper function for String * @return property wrapper around a String function */ - public static PropertyValue> textHaloColor(CameraFunction function) { + public static PropertyValue> textHaloColor(Function function) { return new PaintPropertyValue<>("text-halo-color", function); } @@ -714,11 +714,11 @@ public class PropertyFactory { /** * Distance of halo to the font outline. Max text halo width is 1/4 of the font-size. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static PropertyValue> textHaloWidth(CameraFunction function) { + public static PropertyValue> textHaloWidth(Function function) { return new PaintPropertyValue<>("text-halo-width", function); } @@ -736,11 +736,11 @@ public class PropertyFactory { /** * The halo's fadeout distance towards the outside. * - * @param the zoom parameter type - * @param function a wrapper {@link CameraFunction} for Float + * @param the function input type + * @param function a wrapper function for Float * @return property wrapper around a Float function */ - public static PropertyValue> textHaloBlur(CameraFunction function) { + public static PropertyValue> textHaloBlur(Function function) { return new PaintPropertyValue<>("text-halo-blur", function); } @@ -1742,7 +1742,7 @@ public class PropertyFactory { } /** - * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal `textField` values--not for property functions.) + * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.) * * @param value a String value * @return property wrapper around String @@ -1754,7 +1754,7 @@ public class PropertyFactory { /** - * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal `textField` values--not for property functions.) + * Value to use for a text label. Feature properties are specified using tokens like {field_name}. (Token replacement is only supported for literal {@link PropertyFactory#textField} values--not for property functions.) * * @param the function input type * @param function a wrapper function for String 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 74b9555183..3b3bc9c8b5 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 @@ -1597,6 +1597,112 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getIconOpacity().getFunction().getStops()).size()); } + @Test + public void testIconOpacityAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testIconOpacityAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, iconOpacity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testIconOpacityAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, iconOpacity(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getIconOpacity().getFunction()).getDefaultValue()); + } + + @Test + public void testIconOpacityAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconOpacity( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, iconOpacity(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconOpacity()); + assertNotNull(layer.getIconOpacity().getFunction()); + assertEquals(CompositeFunction.class, layer.getIconOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconOpacity().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getIconOpacity().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getIconOpacity().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 public void testIconColorAsConstant() { checkViewIsDisplayed(R.id.mapView); @@ -1634,6 +1740,77 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getIconColor().getFunction().getStops()).size()); } + @Test + public void testIconColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconColor(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getIconColor()); + assertNotNull(layer.getIconColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, iconColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconColor()); + assertNotNull(layer.getIconColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", iconColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconColor()); + assertNotNull(layer.getIconColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconColor().getFunction().getStops().getClass()); + } + @Test public void testIconColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); @@ -1682,6 +1859,77 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getIconHaloColor().getFunction().getStops()).size()); } + @Test + public void testIconHaloColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloColor(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getIconHaloColor()); + assertNotNull(layer.getIconHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, iconHaloColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloColor()); + assertNotNull(layer.getIconHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", iconHaloColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloColor()); + assertNotNull(layer.getIconHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconHaloColor().getFunction().getStops().getClass()); + } + @Test public void testIconHaloColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); @@ -1731,63 +1979,275 @@ public class SymbolLayerTest extends BaseStyleTest { } @Test - public void testIconHaloBlurAsConstant() { + public void testIconHaloWidthAsIdentitySourceFunction() { checkViewIsDisplayed(R.id.mapView); - Timber.i("icon-halo-blur"); + Timber.i("icon-halo-width"); assertNotNull(layer); - // Set and Get - layer.setProperties(iconHaloBlur(0.3f)); - assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f); + // Set + layer.setProperties( + iconHaloWidth(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); } @Test - public void testIconHaloBlurAsCameraFunction() { + public void testIconHaloWidthAsExponentialSourceFunction() { checkViewIsDisplayed(R.id.mapView); - Timber.i("icon-halo-blur"); + Timber.i("icon-halo-width"); assertNotNull(layer); // Set layer.setProperties( - iconHaloBlur( - zoom( + iconHaloWidth( + property( + "FeaturePropertyA", exponential( - stop(2, iconHaloBlur(0.3f)) + stop(0.3f, iconHaloWidth(0.3f)) ).withBase(0.5f) ) ) ); // Verify - assertNotNull(layer.getIconHaloBlur()); - assertNotNull(layer.getIconHaloBlur().getFunction()); - assertEquals(CameraFunction.class, layer.getIconHaloBlur().getFunction().getClass()); - assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); - assertEquals(0.5f, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).getBase(), 0.001); - assertEquals(1, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).size()); + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); } @Test - public void testIconTranslateAsConstant() { + public void testIconHaloWidthAsCategoricalSourceFunction() { checkViewIsDisplayed(R.id.mapView); - Timber.i("icon-translate"); + Timber.i("icon-halo-width"); assertNotNull(layer); - // Set and Get - layer.setProperties(iconTranslate(new Float[]{0f,0f})); - assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[]{0f,0f}); + // Set + layer.setProperties( + iconHaloWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, iconHaloWidth(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getIconHaloWidth().getFunction()).getDefaultValue()); } @Test - public void testIconTranslateAsCameraFunction() { + public void testIconHaloWidthAsCompositeFunction() { checkViewIsDisplayed(R.id.mapView); - Timber.i("icon-translate"); + Timber.i("icon-halo-width"); assertNotNull(layer); // Set layer.setProperties( - iconTranslate( - zoom( + iconHaloWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, iconHaloWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloWidth()); + assertNotNull(layer.getIconHaloWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getIconHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getIconHaloWidth().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getIconHaloWidth().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 + public void testIconHaloBlurAsConstant() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(iconHaloBlur(0.3f)); + assertEquals((Float) layer.getIconHaloBlur().getValue(), (Float) 0.3f); + } + + @Test + public void testIconHaloBlurAsCameraFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + zoom( + exponential( + stop(2, iconHaloBlur(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(CameraFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).size()); + } + + @Test + public void testIconHaloBlurAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloBlurAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, iconHaloBlur(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testIconHaloBlurAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, iconHaloBlur(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getIconHaloBlur().getFunction()).getDefaultValue()); + } + + @Test + public void testIconHaloBlurAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconHaloBlur( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, iconHaloBlur(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getIconHaloBlur()); + assertNotNull(layer.getIconHaloBlur().getFunction()); + assertEquals(CompositeFunction.class, layer.getIconHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getIconHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getIconHaloBlur().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getIconHaloBlur().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getIconHaloBlur().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 + public void testIconTranslateAsConstant() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-translate"); + assertNotNull(layer); + + // Set and Get + layer.setProperties(iconTranslate(new Float[]{0f,0f})); + assertEquals((Float[]) layer.getIconTranslate().getValue(), (Float[]) new Float[]{0f,0f}); + } + + @Test + public void testIconTranslateAsCameraFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("icon-translate"); + assertNotNull(layer); + + // Set + layer.setProperties( + iconTranslate( + zoom( exponential( stop(2, iconTranslate(new Float[]{0f,0f})) ).withBase(0.5f) @@ -1877,19 +2337,196 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getTextOpacity().getFunction().getStops()).size()); } + @Test + public void testTextOpacityAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testTextOpacityAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textOpacity(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + } + + @Test + public void testTextOpacityAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textOpacity(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(SourceFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getTextOpacity().getFunction()).getDefaultValue()); + } + + @Test + public void testTextOpacityAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-opacity"); + assertNotNull(layer); + + // Set + layer.setProperties( + textOpacity( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textOpacity(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextOpacity()); + assertNotNull(layer.getTextOpacity().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextOpacity().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextOpacity().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextOpacity().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextOpacity().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getTextOpacity().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 public void testTextColorAsConstant() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-color"); assertNotNull(layer); - // Set and Get - layer.setProperties(textColor("rgba(0, 0, 0, 1)")); - assertEquals((String) layer.getTextColor().getValue(), (String) "rgba(0, 0, 0, 1)"); + // Set and Get + layer.setProperties(textColor("rgba(0, 0, 0, 1)")); + assertEquals((String) layer.getTextColor().getValue(), (String) "rgba(0, 0, 0, 1)"); + } + + @Test + public void testTextColorAsCameraFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textColor( + zoom( + exponential( + stop(2, textColor("rgba(0, 0, 0, 1)")) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextColor()); + assertNotNull(layer.getTextColor().getFunction()); + assertEquals(CameraFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals(ExponentialStops.class, layer.getTextColor().getFunction().getStops().getClass()); + assertEquals(0.5f, ((ExponentialStops) layer.getTextColor().getFunction().getStops()).getBase(), 0.001); + assertEquals(1, ((ExponentialStops) layer.getTextColor().getFunction().getStops()).size()); + } + + @Test + public void testTextColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textColor(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getTextColor()); + assertNotNull(layer.getTextColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, textColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextColor()); + assertNotNull(layer.getTextColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextColor().getFunction().getStops().getClass()); } @Test - public void testTextColorAsCameraFunction() { + public void testTextColorAsCategoricalSourceFunction() { checkViewIsDisplayed(R.id.mapView); Timber.i("text-color"); assertNotNull(layer); @@ -1897,10 +2534,11 @@ public class SymbolLayerTest extends BaseStyleTest { // Set layer.setProperties( textColor( - zoom( - exponential( - stop(2, textColor("rgba(0, 0, 0, 1)")) - ).withBase(0.5f) + property( + "FeaturePropertyA", + categorical( + stop("valueA", textColor(Color.RED)) + ) ) ) ); @@ -1908,10 +2546,9 @@ public class SymbolLayerTest extends BaseStyleTest { // Verify assertNotNull(layer.getTextColor()); assertNotNull(layer.getTextColor().getFunction()); - assertEquals(CameraFunction.class, layer.getTextColor().getFunction().getClass()); - assertEquals(ExponentialStops.class, layer.getTextColor().getFunction().getStops().getClass()); - assertEquals(0.5f, ((ExponentialStops) layer.getTextColor().getFunction().getStops()).getBase(), 0.001); - assertEquals(1, ((ExponentialStops) layer.getTextColor().getFunction().getStops()).size()); + assertEquals(SourceFunction.class, layer.getTextColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextColor().getFunction().getStops().getClass()); } @Test @@ -1962,6 +2599,77 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getTextHaloColor().getFunction().getStops()).size()); } + @Test + public void testTextHaloColorAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloColor(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getTextHaloColor()); + assertNotNull(layer.getTextHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloColorAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloColor( + property( + "FeaturePropertyA", + exponential( + stop(Color.RED, textHaloColor(Color.RED)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloColor()); + assertNotNull(layer.getTextHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloColor().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloColorAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-color"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloColor( + property( + "FeaturePropertyA", + categorical( + stop("valueA", textHaloColor(Color.RED)) + ) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloColor()); + assertNotNull(layer.getTextHaloColor().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloColor().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloColor().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextHaloColor().getFunction().getStops().getClass()); + } + @Test public void testTextHaloColorAsIntConstant() { checkViewIsDisplayed(R.id.mapView); @@ -2010,6 +2718,112 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getTextHaloWidth().getFunction().getStops()).size()); } + @Test + public void testTextHaloWidthAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloWidthAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textHaloWidth(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloWidthAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textHaloWidth(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getTextHaloWidth().getFunction()).getDefaultValue()); + } + + @Test + public void testTextHaloWidthAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-width"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloWidth( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textHaloWidth(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloWidth()); + assertNotNull(layer.getTextHaloWidth().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextHaloWidth().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextHaloWidth().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloWidth().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextHaloWidth().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getTextHaloWidth().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 public void testTextHaloBlurAsConstant() { checkViewIsDisplayed(R.id.mapView); @@ -2047,6 +2861,112 @@ public class SymbolLayerTest extends BaseStyleTest { assertEquals(1, ((ExponentialStops) layer.getTextHaloBlur().getFunction().getStops()).size()); } + @Test + public void testTextHaloBlurAsIdentitySourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur(property("FeaturePropertyA", Stops.identity())) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(IdentityStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloBlurAsExponentialSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur( + property( + "FeaturePropertyA", + exponential( + stop(0.3f, textHaloBlur(0.3f)) + ).withBase(0.5f) + ) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + } + + @Test + public void testTextHaloBlurAsCategoricalSourceFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur( + property( + "FeaturePropertyA", + categorical( + stop(1.0f, textHaloBlur(0.3f)) + ) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(SourceFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(CategoricalStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + assertEquals(0.3f, ((SourceFunction) layer.getTextHaloBlur().getFunction()).getDefaultValue()); + } + + @Test + public void testTextHaloBlurAsCompositeFunction() { + checkViewIsDisplayed(R.id.mapView); + Timber.i("text-halo-blur"); + assertNotNull(layer); + + // Set + layer.setProperties( + textHaloBlur( + composite( + "FeaturePropertyA", + exponential( + stop(0, 0.3f, textHaloBlur(0.9f)) + ).withBase(0.5f) + ).withDefaultValue(0.3f) + ) + ); + + // Verify + assertNotNull(layer.getTextHaloBlur()); + assertNotNull(layer.getTextHaloBlur().getFunction()); + assertEquals(CompositeFunction.class, layer.getTextHaloBlur().getFunction().getClass()); + assertEquals("FeaturePropertyA", ((CompositeFunction) layer.getTextHaloBlur().getFunction()).getProperty()); + assertEquals(ExponentialStops.class, layer.getTextHaloBlur().getFunction().getStops().getClass()); + assertEquals(1, ((ExponentialStops) layer.getTextHaloBlur().getFunction().getStops()).size()); + + ExponentialStops, Float> stops = + (ExponentialStops, Float>) layer.getTextHaloBlur().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 public void testTextTranslateAsConstant() { checkViewIsDisplayed(R.id.mapView); diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h index d2892f9627..a4850a4e18 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.h +++ b/platform/darwin/src/MGLSymbolStyleLayer.h @@ -1204,6 +1204,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconColor; #else @@ -1224,6 +1233,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconColor; #endif @@ -1246,6 +1264,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconHaloBlur; @@ -1267,6 +1294,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconHaloColor; #else @@ -1287,6 +1323,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconHaloColor; #endif @@ -1309,6 +1354,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconHaloWidth; @@ -1328,6 +1382,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *iconOpacity; @@ -1426,6 +1489,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textColor; #else @@ -1445,6 +1517,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textColor; #endif @@ -1467,6 +1548,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textHaloBlur; @@ -1487,6 +1577,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textHaloColor; #else @@ -1506,6 +1605,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textHaloColor; #endif @@ -1529,6 +1637,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textHaloWidth; @@ -1548,6 +1665,15 @@ MGL_EXPORT * `MGLCameraStyleFunction` with an interpolation mode of: * `MGLInterpolationModeExponential` * `MGLInterpolationModeInterval` + * `MGLSourceStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` + * `MGLInterpolationModeIdentity` + * `MGLCompositeStyleFunction` with an interpolation mode of: + * `MGLInterpolationModeExponential` + * `MGLInterpolationModeInterval` + * `MGLInterpolationModeCategorical` */ @property (nonatomic, null_resettable) MGLStyleValue *textOpacity; diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm index 0b065cd7b9..52648e7a05 100644 --- a/platform/darwin/src/MGLSymbolStyleLayer.mm +++ b/platform/darwin/src/MGLSymbolStyleLayer.mm @@ -890,7 +890,7 @@ namespace mbgl { - (void)setIconColor:(MGLStyleValue *)iconColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(iconColor); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(iconColor); self.rawLayer->setIconColor(mbglValue); } @@ -899,15 +899,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultIconColor()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultIconColor()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setIconHaloBlur:(MGLStyleValue *)iconHaloBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(iconHaloBlur); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(iconHaloBlur); self.rawLayer->setIconHaloBlur(mbglValue); } @@ -916,15 +916,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconHaloBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultIconHaloBlur()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloBlur()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setIconHaloColor:(MGLStyleValue *)iconHaloColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(iconHaloColor); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(iconHaloColor); self.rawLayer->setIconHaloColor(mbglValue); } @@ -933,15 +933,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconHaloColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultIconHaloColor()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloColor()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setIconHaloWidth:(MGLStyleValue *)iconHaloWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(iconHaloWidth); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(iconHaloWidth); self.rawLayer->setIconHaloWidth(mbglValue); } @@ -950,15 +950,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconHaloWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultIconHaloWidth()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultIconHaloWidth()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setIconOpacity:(MGLStyleValue *)iconOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(iconOpacity); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(iconOpacity); self.rawLayer->setIconOpacity(mbglValue); } @@ -967,9 +967,9 @@ namespace mbgl { auto propertyValue = self.rawLayer->getIconOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultIconOpacity()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultIconOpacity()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setIconTranslation:(MGLStyleValue *)iconTranslation { @@ -1023,7 +1023,7 @@ namespace mbgl { - (void)setTextColor:(MGLStyleValue *)textColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(textColor); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(textColor); self.rawLayer->setTextColor(mbglValue); } @@ -1032,15 +1032,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultTextColor()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultTextColor()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setTextHaloBlur:(MGLStyleValue *)textHaloBlur { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(textHaloBlur); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(textHaloBlur); self.rawLayer->setTextHaloBlur(mbglValue); } @@ -1049,15 +1049,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextHaloBlur(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultTextHaloBlur()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloBlur()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setTextHaloColor:(MGLStyleValue *)textHaloColor { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(textHaloColor); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(textHaloColor); self.rawLayer->setTextHaloColor(mbglValue); } @@ -1066,15 +1066,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextHaloColor(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultTextHaloColor()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloColor()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setTextHaloWidth:(MGLStyleValue *)textHaloWidth { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(textHaloWidth); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(textHaloWidth); self.rawLayer->setTextHaloWidth(mbglValue); } @@ -1083,15 +1083,15 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextHaloWidth(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultTextHaloWidth()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultTextHaloWidth()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setTextOpacity:(MGLStyleValue *)textOpacity { MGLAssertStyleLayerIsValid(); - auto mbglValue = MGLStyleValueTransformer().toInterpolatablePropertyValue(textOpacity); + auto mbglValue = MGLStyleValueTransformer().toDataDrivenPropertyValue(textOpacity); self.rawLayer->setTextOpacity(mbglValue); } @@ -1100,9 +1100,9 @@ namespace mbgl { auto propertyValue = self.rawLayer->getTextOpacity(); if (propertyValue.isUndefined()) { - return MGLStyleValueTransformer().toStyleValue(self.rawLayer->getDefaultTextOpacity()); + return MGLStyleValueTransformer().toDataDrivenStyleValue(self.rawLayer->getDefaultTextOpacity()); } - return MGLStyleValueTransformer().toStyleValue(propertyValue); + return MGLStyleValueTransformer().toDataDrivenStyleValue(propertyValue); } - (void)setTextTranslation:(MGLStyleValue *)textTranslation { diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm index ce4aa11cee..1d599f5aca 100644 --- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm +++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm @@ -1421,7 +1421,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; layer.iconColor = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getIconColor(), propertyValue, @"Setting iconColor to a constant value should update icon-color."); XCTAssertEqualObjects(layer.iconColor, constantStyleValue, @@ -1438,6 +1438,29 @@ XCTAssertEqualObjects(layer.iconColor, functionStyleValue, @"iconColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconColor = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconColor(), propertyValue, + @"Setting iconColor to a source function should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionStyleValue, + @"iconColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconColor = functionStyleValue; + + std::map innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconColor(), propertyValue, + @"Setting iconColor to a composite function should update icon-color."); + XCTAssertEqualObjects(layer.iconColor, functionStyleValue, + @"iconColor should round-trip composite functions."); layer.iconColor = nil; @@ -1445,11 +1468,6 @@ @"Unsetting iconColor should return icon-color to the default value."); XCTAssertEqualObjects(layer.iconColor, defaultStyleValue, @"iconColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconColor = 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.iconColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-halo-blur @@ -1460,7 +1478,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:@0xff]; layer.iconHaloBlur = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, @"Setting iconHaloBlur to a constant value should update icon-halo-blur."); XCTAssertEqualObjects(layer.iconHaloBlur, constantStyleValue, @@ -1477,6 +1495,29 @@ XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, @"iconHaloBlur should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconHaloBlur = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, + @"Setting iconHaloBlur to a source function should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, + @"iconHaloBlur should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconHaloBlur = functionStyleValue; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconHaloBlur(), propertyValue, + @"Setting iconHaloBlur to a composite function should update icon-halo-blur."); + XCTAssertEqualObjects(layer.iconHaloBlur, functionStyleValue, + @"iconHaloBlur should round-trip composite functions."); layer.iconHaloBlur = nil; @@ -1484,11 +1525,6 @@ @"Unsetting iconHaloBlur should return icon-halo-blur to the default value."); XCTAssertEqualObjects(layer.iconHaloBlur, defaultStyleValue, @"iconHaloBlur should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloBlur = 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.iconHaloBlur = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-halo-color @@ -1499,7 +1535,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; layer.iconHaloColor = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, @"Setting iconHaloColor to a constant value should update icon-halo-color."); XCTAssertEqualObjects(layer.iconHaloColor, constantStyleValue, @@ -1516,6 +1552,29 @@ XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, @"iconHaloColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconHaloColor = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, + @"Setting iconHaloColor to a source function should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, + @"iconHaloColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconHaloColor = functionStyleValue; + + std::map innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconHaloColor(), propertyValue, + @"Setting iconHaloColor to a composite function should update icon-halo-color."); + XCTAssertEqualObjects(layer.iconHaloColor, functionStyleValue, + @"iconHaloColor should round-trip composite functions."); layer.iconHaloColor = nil; @@ -1523,11 +1582,6 @@ @"Unsetting iconHaloColor should return icon-halo-color to the default value."); XCTAssertEqualObjects(layer.iconHaloColor, defaultStyleValue, @"iconHaloColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloColor = 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.iconHaloColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-halo-width @@ -1538,7 +1592,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:@0xff]; layer.iconHaloWidth = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, @"Setting iconHaloWidth to a constant value should update icon-halo-width."); XCTAssertEqualObjects(layer.iconHaloWidth, constantStyleValue, @@ -1555,6 +1609,29 @@ XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, @"iconHaloWidth should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconHaloWidth = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, + @"Setting iconHaloWidth to a source function should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, + @"iconHaloWidth should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconHaloWidth = functionStyleValue; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconHaloWidth(), propertyValue, + @"Setting iconHaloWidth to a composite function should update icon-halo-width."); + XCTAssertEqualObjects(layer.iconHaloWidth, functionStyleValue, + @"iconHaloWidth should round-trip composite functions."); layer.iconHaloWidth = nil; @@ -1562,11 +1639,6 @@ @"Unsetting iconHaloWidth should return icon-halo-width to the default value."); XCTAssertEqualObjects(layer.iconHaloWidth, defaultStyleValue, @"iconHaloWidth should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconHaloWidth = 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.iconHaloWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-opacity @@ -1577,7 +1649,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:@0xff]; layer.iconOpacity = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, @"Setting iconOpacity to a constant value should update icon-opacity."); XCTAssertEqualObjects(layer.iconOpacity, constantStyleValue, @@ -1594,6 +1666,29 @@ XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, @"iconOpacity should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.iconOpacity = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, + @"Setting iconOpacity to a source function should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, + @"iconOpacity should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.iconOpacity = functionStyleValue; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getIconOpacity(), propertyValue, + @"Setting iconOpacity to a composite function should update icon-opacity."); + XCTAssertEqualObjects(layer.iconOpacity, functionStyleValue, + @"iconOpacity should round-trip composite functions."); layer.iconOpacity = nil; @@ -1601,11 +1696,6 @@ @"Unsetting iconOpacity should return icon-opacity to the default value."); XCTAssertEqualObjects(layer.iconOpacity, defaultStyleValue, @"iconOpacity should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.iconOpacity = 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.iconOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // icon-translate @@ -1700,7 +1790,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; layer.textColor = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getTextColor(), propertyValue, @"Setting textColor to a constant value should update text-color."); XCTAssertEqualObjects(layer.textColor, constantStyleValue, @@ -1717,6 +1807,29 @@ XCTAssertEqualObjects(layer.textColor, functionStyleValue, @"textColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textColor = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextColor(), propertyValue, + @"Setting textColor to a source function should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionStyleValue, + @"textColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textColor = functionStyleValue; + + std::map innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextColor(), propertyValue, + @"Setting textColor to a composite function should update text-color."); + XCTAssertEqualObjects(layer.textColor, functionStyleValue, + @"textColor should round-trip composite functions."); layer.textColor = nil; @@ -1724,11 +1837,6 @@ @"Unsetting textColor should return text-color to the default value."); XCTAssertEqualObjects(layer.textColor, defaultStyleValue, @"textColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textColor = 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.textColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-halo-blur @@ -1739,7 +1847,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:@0xff]; layer.textHaloBlur = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, @"Setting textHaloBlur to a constant value should update text-halo-blur."); XCTAssertEqualObjects(layer.textHaloBlur, constantStyleValue, @@ -1756,6 +1864,29 @@ XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, @"textHaloBlur should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textHaloBlur = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, + @"Setting textHaloBlur to a source function should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, + @"textHaloBlur should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textHaloBlur = functionStyleValue; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextHaloBlur(), propertyValue, + @"Setting textHaloBlur to a composite function should update text-halo-blur."); + XCTAssertEqualObjects(layer.textHaloBlur, functionStyleValue, + @"textHaloBlur should round-trip composite functions."); layer.textHaloBlur = nil; @@ -1763,11 +1894,6 @@ @"Unsetting textHaloBlur should return text-halo-blur to the default value."); XCTAssertEqualObjects(layer.textHaloBlur, defaultStyleValue, @"textHaloBlur should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloBlur = 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.textHaloBlur = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-halo-color @@ -1778,7 +1904,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:[MGLColor redColor]]; layer.textHaloColor = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { { 1, 0, 0, 1 } }; + mbgl::style::DataDrivenPropertyValue propertyValue = { { 1, 0, 0, 1 } }; XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, @"Setting textHaloColor to a constant value should update text-halo-color."); XCTAssertEqualObjects(layer.textHaloColor, constantStyleValue, @@ -1795,6 +1921,29 @@ XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, @"textHaloColor should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textHaloColor = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, { 1, 0, 0, 1 }}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, + @"Setting textHaloColor to a source function should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, + @"textHaloColor should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textHaloColor = functionStyleValue; + + std::map innerStops { {18, { 1, 0, 0, 1 }} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextHaloColor(), propertyValue, + @"Setting textHaloColor to a composite function should update text-halo-color."); + XCTAssertEqualObjects(layer.textHaloColor, functionStyleValue, + @"textHaloColor should round-trip composite functions."); layer.textHaloColor = nil; @@ -1802,11 +1951,6 @@ @"Unsetting textHaloColor should return text-halo-color to the default value."); XCTAssertEqualObjects(layer.textHaloColor, defaultStyleValue, @"textHaloColor should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloColor = 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.textHaloColor = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-halo-width @@ -1817,7 +1961,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:@0xff]; layer.textHaloWidth = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, @"Setting textHaloWidth to a constant value should update text-halo-width."); XCTAssertEqualObjects(layer.textHaloWidth, constantStyleValue, @@ -1834,6 +1978,29 @@ XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, @"textHaloWidth should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textHaloWidth = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, + @"Setting textHaloWidth to a source function should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, + @"textHaloWidth should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textHaloWidth = functionStyleValue; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextHaloWidth(), propertyValue, + @"Setting textHaloWidth to a composite function should update text-halo-width."); + XCTAssertEqualObjects(layer.textHaloWidth, functionStyleValue, + @"textHaloWidth should round-trip composite functions."); layer.textHaloWidth = nil; @@ -1841,11 +2008,6 @@ @"Unsetting textHaloWidth should return text-halo-width to the default value."); XCTAssertEqualObjects(layer.textHaloWidth, defaultStyleValue, @"textHaloWidth should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textHaloWidth = 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.textHaloWidth = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-opacity @@ -1856,7 +2018,7 @@ MGLStyleValue *constantStyleValue = [MGLStyleValue valueWithRawValue:@0xff]; layer.textOpacity = constantStyleValue; - mbgl::style::PropertyValue propertyValue = { 0xff }; + mbgl::style::DataDrivenPropertyValue propertyValue = { 0xff }; XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, @"Setting textOpacity to a constant value should update text-opacity."); XCTAssertEqualObjects(layer.textOpacity, constantStyleValue, @@ -1873,6 +2035,29 @@ XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, @"textOpacity should round-trip camera functions."); + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential sourceStops:@{@18: constantStyleValue} attributeName:@"keyName" options:nil]; + layer.textOpacity = functionStyleValue; + + mbgl::style::ExponentialStops exponentialStops = { {{18, 0xff}}, 1.0 }; + propertyValue = mbgl::style::SourceFunction { "keyName", exponentialStops }; + + XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, + @"Setting textOpacity to a source function should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, + @"textOpacity should round-trip source functions."); + + functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeExponential compositeStops:@{@10: @{@18: constantStyleValue}} attributeName:@"keyName" options:nil]; + layer.textOpacity = functionStyleValue; + + std::map innerStops { {18, 0xff} }; + mbgl::style::CompositeExponentialStops compositeStops { { {10.0, innerStops} }, 1.0 }; + + propertyValue = mbgl::style::CompositeFunction { "keyName", compositeStops }; + + XCTAssertEqual(rawLayer->getTextOpacity(), propertyValue, + @"Setting textOpacity to a composite function should update text-opacity."); + XCTAssertEqualObjects(layer.textOpacity, functionStyleValue, + @"textOpacity should round-trip composite functions."); layer.textOpacity = nil; @@ -1880,11 +2065,6 @@ @"Unsetting textOpacity should return text-opacity to the default value."); XCTAssertEqualObjects(layer.textOpacity, defaultStyleValue, @"textOpacity should return the default value after being unset."); - - functionStyleValue = [MGLStyleValue valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil]; - XCTAssertThrowsSpecificNamed(layer.textOpacity = 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.textOpacity = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it"); } // text-translate diff --git a/scripts/generate-style-code.js b/scripts/generate-style-code.js index bf9bef837a..9629c8fe45 100644 --- a/scripts/generate-style-code.js +++ b/scripts/generate-style-code.js @@ -47,7 +47,20 @@ global.evaluatedType = function (property) { }; function attributeType(property, type) { - const name = property.name.replace(type + '-', '').replace('-', '_'); + const attributeNameExceptions = { + 'text-opacity': 'opacity', + 'icon-opacity': 'opacity', + 'text-color': 'fill_color', + 'icon-color': 'fill_color', + 'text-halo-color': 'halo_color', + 'icon-halo-color': 'halo_color', + 'text-halo-blur': 'halo_blur', + 'icon-halo-blur': 'halo_blur', + 'text-halo-width': 'halo_width', + 'icon-halo-width': 'halo_width' + } + const name = attributeNameExceptions[property.name] || + property.name.replace(type + '-', '').replace(/-/g, '_'); return `attributes::a_${name}${name === 'offset' ? '<1>' : ''}`; } diff --git a/src/mbgl/layout/symbol_feature.hpp b/src/mbgl/layout/symbol_feature.hpp index e55995f952..f4dc1680bc 100644 --- a/src/mbgl/layout/symbol_feature.hpp +++ b/src/mbgl/layout/symbol_feature.hpp @@ -8,14 +8,23 @@ namespace mbgl { -class SymbolFeature { +class SymbolFeature : public GeometryTileFeature { public: - FeatureType type; + SymbolFeature(std::unique_ptr feature_) : + feature(std::move(feature_)), + geometry(feature->getGeometries()) // we need a mutable copy of the geometry for mergeLines() + {} + + FeatureType getType() const override { return feature->getType(); } + optional getValue(const std::string& key) const override { return feature->getValue(key); }; + std::unordered_map getProperties() const override { return feature->getProperties(); }; + optional getID() const override { return feature->getID(); }; + GeometryCollection getGeometries() const override { return geometry; }; + + std::unique_ptr feature; GeometryCollection geometry; optional text; optional icon; - std::array iconOffset; - float iconRotation; std::size_t index; }; diff --git a/src/mbgl/layout/symbol_instance.cpp b/src/mbgl/layout/symbol_instance.cpp index 4f425641e7..d81783b2f6 100644 --- a/src/mbgl/layout/symbol_instance.cpp +++ b/src/mbgl/layout/symbol_instance.cpp @@ -10,7 +10,7 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, const SymbolLayoutProperties::Evaluated& layout, const bool addToBuffers, const uint32_t index_, const float textBoxScale, const float textPadding, const SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, const SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedFeature) : + const GlyphPositions& face, const IndexedSubfeature& indexedFeature, const std::size_t featureIndex_) : point(anchor.point), index(index_), hasText(shapedTextOrientations.first || shapedTextOrientations.second), @@ -18,7 +18,8 @@ SymbolInstance::SymbolInstance(Anchor& anchor, const GeometryCoordinates& line, // Create the collision features that will be used to check whether this symbol instance can be placed textCollisionFeature(line, anchor, shapedTextOrientations.second ?: shapedTextOrientations.first, textBoxScale, textPadding, textPlacement, indexedFeature), - iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature) { + iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconPlacement, indexedFeature), + featureIndex(featureIndex_) { // Create the quads used for rendering the icon and glyphs. if (addToBuffers) { diff --git a/src/mbgl/layout/symbol_instance.hpp b/src/mbgl/layout/symbol_instance.hpp index 2dbb3bac23..532a4d30d8 100644 --- a/src/mbgl/layout/symbol_instance.hpp +++ b/src/mbgl/layout/symbol_instance.hpp @@ -17,7 +17,7 @@ public: const style::SymbolLayoutProperties::Evaluated&, const bool inside, const uint32_t index, const float textBoxScale, const float textPadding, style::SymbolPlacementType textPlacement, const float iconBoxScale, const float iconPadding, style::SymbolPlacementType iconPlacement, - const GlyphPositions& face, const IndexedSubfeature& indexedfeature); + const GlyphPositions& face, const IndexedSubfeature& indexedfeature, const std::size_t featureIndex); Point point; uint32_t index; @@ -28,6 +28,7 @@ public: CollisionFeature textCollisionFeature; CollisionFeature iconCollisionFeature; WritingModeType writingModes; + std::size_t featureIndex; }; } // namespace mbgl diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp index cf9e784c26..3a2c082ad8 100644 --- a/src/mbgl/layout/symbol_layout.cpp +++ b/src/mbgl/layout/symbol_layout.cpp @@ -87,7 +87,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, } for (const auto& layer : layers) { - layerPaintProperties.emplace(layer->getID(), layer->as()->impl->paint.evaluated); + layerPaintProperties.emplace(layer->getID(), std::make_pair( + layer->as()->impl->iconPaintProperties(), + layer->as()->impl->textPaintProperties() + )); } // Determine and load glyph ranges @@ -96,12 +99,13 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, auto feature = sourceLayer.getFeature(i); if (!leader.filter(feature->getType(), feature->getID(), [&] (const auto& key) { return feature->getValue(key); })) continue; + + SymbolFeature ft(std::move(feature)); - SymbolFeature ft; ft.index = i; - auto getValue = [&feature](const std::string& key) -> std::string { - auto value = feature->getValue(key); + auto getValue = [&ft](const std::string& key) -> std::string { + auto value = ft.getValue(key); if (!value) return std::string(); if (value->is()) @@ -118,12 +122,12 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, }; if (hasText) { - std::string u8string = layout.evaluate(zoom, *feature); + std::string u8string = layout.evaluate(zoom, ft); if (layout.get().isConstant()) { u8string = util::replaceTokens(u8string, getValue); } - auto textTransform = layout.evaluate(zoom, *feature); + auto textTransform = layout.evaluate(zoom, ft); if (textTransform == TextTransformType::Uppercase) { u8string = platform::uppercase(u8string); @@ -144,13 +148,9 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters, if (hasIcon) { ft.icon = util::replaceTokens(layout.get(), getValue); - ft.iconOffset = layout.evaluate(zoom, *feature); - ft.iconRotation = layout.evaluate(zoom, *feature) * util::DEG2RAD; } if (ft.text || ft.icon) { - ft.type = feature->getType(); - ft.geometry = feature->getGeometries(); features.push_back(std::move(ft)); } } @@ -229,7 +229,8 @@ void SymbolLayout::prepare(uintptr_t tileUID, const bool textAlongLine = layout.get() == AlignmentType::Map && layout.get() == SymbolPlacementType::Line; - for (const auto& feature : features) { + for (auto it = features.begin(); it != features.end(); ++it) { + auto& feature = *it; if (feature.geometry.empty()) continue; std::pair shapedTextOrientations; @@ -273,7 +274,9 @@ void SymbolLayout::prepare(uintptr_t tileUID, if (feature.icon) { auto image = spriteAtlas.getIcon(*feature.icon); if (image) { - shapedIcon = shapeIcon(*image, feature); + shapedIcon = shapeIcon(*image, + layout.evaluate(zoom, feature), + layout.evaluate(zoom, feature) * util::DEG2RAD); assert((*image).spriteImage); if ((*image).spriteImage->sdf) { sdfIcons = true; @@ -288,15 +291,17 @@ void SymbolLayout::prepare(uintptr_t tileUID, // if either shapedText or icon position is present, add the feature if (shapedTextOrientations.first || shapedIcon) { - addFeature(feature, shapedTextOrientations, shapedIcon, face); + addFeature(std::distance(features.begin(), it), feature, shapedTextOrientations, shapedIcon, face); } + + feature.geometry.clear(); } - features.clear(); compareText.clear(); } -void SymbolLayout::addFeature(const SymbolFeature& feature, +void SymbolLayout::addFeature(const std::size_t index, + const SymbolFeature& feature, const std::pair& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face) { @@ -345,8 +350,10 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, symbolInstances.emplace_back(anchor, line, shapedTextOrientations, shapedIcon, layout, addToBuffers, symbolInstances.size(), textBoxScale, textPadding, textPlacement, iconBoxScale, iconPadding, iconPlacement, - face, indexedFeature); + face, indexedFeature, index); }; + + const auto& type = feature.getType(); if (layout.get() == SymbolPlacementType::Line) { auto clippedLines = util::clipLines(feature.geometry, 0, 0, util::EXTENT, util::EXTENT); @@ -368,7 +375,7 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, } } } - } else if (feature.type == FeatureType::Polygon) { + } else if (type == FeatureType::Polygon) { for (const auto& polygon : classifyRings(feature.geometry)) { Polygon poly; for (const auto& ring : polygon) { @@ -384,12 +391,12 @@ void SymbolLayout::addFeature(const SymbolFeature& feature, Anchor anchor(poi.x, poi.y, 0, minScale); addSymbolInstance(polygon[0], anchor); } - } else if (feature.type == FeatureType::LineString) { + } else if (type == FeatureType::LineString) { for (const auto& line : feature.geometry) { Anchor anchor(line[0].x, line[0].y, 0, minScale); addSymbolInstance(line, anchor); } - } else if (feature.type == FeatureType::Point) { + } else if (type == FeatureType::Point) { for (const auto& points : feature.geometry) { for (const auto& point : points) { Anchor anchor(point.x, point.y, 0, minScale); @@ -503,6 +510,12 @@ std::unique_ptr SymbolLayout::place(CollisionTile& collisionTile) keepUpright, iconPlacement, collisionTile.config.angle, symbolInstance.writingModes); } } + + const auto& feature = features.at(symbolInstance.featureIndex); + for (auto& pair : bucket->paintPropertyBinders) { + pair.second.first.populateVertexVectors(feature, bucket->icon.vertices.vertexSize()); + pair.second.second.populateVertexVectors(feature, bucket->text.vertices.vertexSize()); + } } if (collisionTile.config.debug) { diff --git a/src/mbgl/layout/symbol_layout.hpp b/src/mbgl/layout/symbol_layout.hpp index dbfdad22d9..491d0078da 100644 --- a/src/mbgl/layout/symbol_layout.hpp +++ b/src/mbgl/layout/symbol_layout.hpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -51,10 +52,12 @@ public: State state = Pending; - std::unordered_map layerPaintProperties; + std::unordered_map> layerPaintProperties; private: - void addFeature(const SymbolFeature&, + void addFeature(const size_t, + const SymbolFeature&, const std::pair& shapedTextOrientations, const PositionedIcon& shapedIcon, const GlyphPositions& face); diff --git a/src/mbgl/programs/attributes.hpp b/src/mbgl/programs/attributes.hpp index c4cc5dea8b..bb90f2c13c 100644 --- a/src/mbgl/programs/attributes.hpp +++ b/src/mbgl/programs/attributes.hpp @@ -64,6 +64,34 @@ struct a_color : gl::Attribute, 4> { } }; +// used in the symbol sdf shader +struct a_fill_color : gl::Attribute, 4> { + static auto name() { return "a_fill_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized(color.r), + gl::Normalized(color.g), + gl::Normalized(color.b), + gl::Normalized(color.a) + }}; + } +}; + +// used in the symbol sdf shader +struct a_halo_color : gl::Attribute, 4> { + static auto name() { return "a_halo_color"; } + + static Value value(const Color& color) { + return {{ + gl::Normalized(color.r), + gl::Normalized(color.g), + gl::Normalized(color.b), + gl::Normalized(color.a) + }}; + } +}; + struct a_stroke_color : gl::Attribute, 4> { static auto name() { return "a_stroke_color"; } @@ -171,5 +199,23 @@ struct a_offset<1> : gl::Attribute { } }; +struct a_halo_width : gl::Attribute { + static auto name() { return "a_halo_width"; } + + static Value value(float width) { + return {{ width }}; + } +}; + +struct a_halo_blur : gl::Attribute { + static auto name() { return "a_halo_blur"; } + + static Value value(float blur) { + return {{ blur }}; + } +}; + + + } // namespace attributes } // namespace mbgl diff --git a/src/mbgl/programs/programs.hpp b/src/mbgl/programs/programs.hpp index dd71c2ce97..742c5a221b 100644 --- a/src/mbgl/programs/programs.hpp +++ b/src/mbgl/programs/programs.hpp @@ -40,8 +40,8 @@ public: LinePatternProgram linePattern; RasterProgram raster; SymbolIconProgram symbolIcon; - SymbolSDFProgram symbolIconSDF; - SymbolSDFProgram symbolGlyph; + SymbolSDFIconProgram symbolIconSDF; + SymbolSDFTextProgram symbolGlyph; DebugProgram debug; CollisionBoxProgram collisionBox; diff --git a/src/mbgl/programs/symbol_program.cpp b/src/mbgl/programs/symbol_program.cpp index 3f59000b94..19fe2bc2f6 100644 --- a/src/mbgl/programs/symbol_program.cpp +++ b/src/mbgl/programs/symbol_program.cpp @@ -2,6 +2,7 @@ #include #include #include +#include namespace mbgl { @@ -19,6 +20,7 @@ Values makeValues(const style::SymbolPropertyValues& values, std::array extrudeScale; const float scale = values.paintSize / values.sdfScale; + if (values.pitchAlignment == AlignmentType::Map) { extrudeScale.fill(tile.id.pixelsToTileUnits(1, state.getZoom()) * scale); } else { @@ -27,7 +29,7 @@ Values makeValues(const style::SymbolPropertyValues& values, pixelsToGLUnits[1] * scale * state.getCameraToCenterDistance() }}; } - + // adjust min/max zooms for variable font sies float zoomAdjust = std::log(values.paintSize / values.layoutSize) / std::log(2); @@ -35,7 +37,6 @@ Values makeValues(const style::SymbolPropertyValues& values, uniforms::u_matrix::Value{ tile.translatedMatrix(values.translate, values.translateAnchor, state) }, - uniforms::u_opacity::Value{ values.opacity }, uniforms::u_extrude_scale::Value{ extrudeScale }, uniforms::u_texsize::Value{ std::array {{ float(texsize.width) / 4, float(texsize.height) / 4 }} }, uniforms::u_zoom::Value{ float((state.getZoom() - zoomAdjust) * 10) }, @@ -62,84 +63,37 @@ SymbolIconProgram::uniformValues(const style::SymbolPropertyValues& values, ); } -static SymbolSDFProgram::UniformValues makeSDFValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio, - Color color, - float buffer, - float gammaAdjust) -{ - // The default gamma value has to be adjust for the current pixelratio so that we're not - // drawing blurry font on retina screens. - const float gammaBase = 0.105 * values.sdfScale / values.paintSize / pixelRatio; - const float gammaScale = (values.pitchAlignment == AlignmentType::Map - ? 1.0 / std::cos(state.getPitch()) - : 1.0) / state.getCameraToCenterDistance(); - - return makeValues( - values, - texsize, - pixelsToGLUnits, - tile, - state, - uniforms::u_color::Value{ color }, - uniforms::u_buffer::Value{ buffer }, - uniforms::u_gamma::Value{ (gammaBase + gammaAdjust) * gammaScale }, - uniforms::u_pitch::Value{ state.getPitch() }, - uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, - uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, - uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map } - ); -} - -SymbolSDFProgram::UniformValues -SymbolSDFProgram::haloUniformValues(const style::SymbolPropertyValues& values, +template +typename SymbolSDFProgram::UniformValues SymbolSDFProgram::uniformValues(const style::SymbolPropertyValues& values, const Size& texsize, const std::array& pixelsToGLUnits, const RenderTile& tile, const TransformState& state, - float pixelRatio) + const SymbolSDFPart part) { const float scale = values.paintSize / values.sdfScale; - const float sdfPx = 8.0f; - const float blurOffset = 1.19f; - const float haloOffset = 6.0f; - - return makeSDFValues( + + const float gammaScale = scale * (values.pitchAlignment == AlignmentType::Map + ? std::cos(state.getPitch()) + : 1.0) * state.getCameraToCenterDistance(); + + return makeValues::UniformValues>( values, texsize, pixelsToGLUnits, tile, state, - pixelRatio, - values.haloColor, - (haloOffset - values.haloWidth / scale) / sdfPx, - values.haloBlur * blurOffset / scale / sdfPx + uniforms::u_font_scale::Value{ scale }, + uniforms::u_gamma_scale::Value{ gammaScale }, + uniforms::u_pitch::Value{ state.getPitch() }, + uniforms::u_bearing::Value{ -1.0f * state.getAngle() }, + uniforms::u_aspect_ratio::Value{ (state.getSize().width * 1.0f) / (state.getSize().height * 1.0f) }, + uniforms::u_pitch_with_map::Value{ values.pitchAlignment == AlignmentType::Map }, + uniforms::u_is_halo::Value{ part == SymbolSDFPart::Halo } ); } -SymbolSDFProgram::UniformValues -SymbolSDFProgram::foregroundUniformValues(const style::SymbolPropertyValues& values, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile& tile, - const TransformState& state, - float pixelRatio) -{ - return makeSDFValues( - values, - texsize, - pixelsToGLUnits, - tile, - state, - pixelRatio, - values.color, - (256.0f - 64.0f) / 256.0f, - 0 - ); -} +template class SymbolSDFProgram; +template class SymbolSDFProgram; } // namespace mbgl diff --git a/src/mbgl/programs/symbol_program.hpp b/src/mbgl/programs/symbol_program.hpp index e0e90f0fa4..0537c25a2c 100644 --- a/src/mbgl/programs/symbol_program.hpp +++ b/src/mbgl/programs/symbol_program.hpp @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -27,9 +28,10 @@ MBGL_DEFINE_UNIFORM_SCALAR(bool, u_rotate_with_map); MBGL_DEFINE_UNIFORM_SCALAR(bool, u_pitch_with_map); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_texture); MBGL_DEFINE_UNIFORM_SCALAR(gl::TextureUnit, u_fadetexture); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_buffer); -MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma); MBGL_DEFINE_UNIFORM_SCALAR(float, u_aspect_ratio); +MBGL_DEFINE_UNIFORM_SCALAR(bool, u_is_halo); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_font_scale); +MBGL_DEFINE_UNIFORM_SCALAR(float, u_gamma_scale); } // namespace uniforms struct SymbolLayoutAttributes : gl::Attributes< @@ -75,14 +77,13 @@ class SymbolIconProgram : public Program< SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture>, - style::SymbolPaintProperties> + style::IconPaintProperties> { public: using Program::Program; @@ -94,47 +95,73 @@ public: const TransformState&); }; +enum class SymbolSDFPart { + Fill = 1, + Halo = 0 +}; + +template class SymbolSDFProgram : public Program< shaders::symbol_sdf, gl::Triangle, SymbolLayoutAttributes, gl::Uniforms< uniforms::u_matrix, - uniforms::u_opacity, uniforms::u_extrude_scale, uniforms::u_texsize, uniforms::u_zoom, uniforms::u_rotate_with_map, uniforms::u_texture, uniforms::u_fadetexture, - uniforms::u_color, - uniforms::u_buffer, - uniforms::u_gamma, + uniforms::u_font_scale, + uniforms::u_gamma_scale, uniforms::u_pitch, uniforms::u_bearing, uniforms::u_aspect_ratio, - uniforms::u_pitch_with_map>, - style::SymbolPaintProperties> + uniforms::u_pitch_with_map, + uniforms::u_is_halo>, + PaintProperties> { public: - using Program::Program; + using BaseProgram = Program, + PaintProperties>; + + using UniformValues = typename BaseProgram::UniformValues; + - static UniformValues haloUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); - - static UniformValues foregroundUniformValues(const style::SymbolPropertyValues&, - const Size& texsize, - const std::array& pixelsToGLUnits, - const RenderTile&, - const TransformState&, - float pixelRatio); + + using BaseProgram::BaseProgram; + + static UniformValues uniformValues(const style::SymbolPropertyValues&, + const Size& texsize, + const std::array& pixelsToGLUnits, + const RenderTile&, + const TransformState&, + const SymbolSDFPart); }; +using SymbolSDFIconProgram = SymbolSDFProgram; +using SymbolSDFTextProgram = SymbolSDFProgram; + using SymbolLayoutVertex = SymbolLayoutAttributes::Vertex; -using SymbolAttributes = SymbolIconProgram::Attributes; +using SymbolIconAttributes = SymbolIconProgram::Attributes; +using SymbolTextAttributes = SymbolSDFTextProgram::Attributes; } // namespace mbgl diff --git a/src/mbgl/renderer/painter_symbol.cpp b/src/mbgl/renderer/painter_symbol.cpp index 0113c15a08..48c2e7ff66 100644 --- a/src/mbgl/renderer/painter_symbol.cpp +++ b/src/mbgl/renderer/painter_symbol.cpp @@ -33,7 +33,9 @@ void Painter::renderSymbol(PaintParameters& parameters, auto draw = [&] (auto& program, auto&& uniformValues, const auto& buffers, - const SymbolPropertyValues& values_) + const SymbolPropertyValues& values_, + const auto& binders, + const auto& paintProperties) { // We clip symbols to their tile extent in still mode. const bool needsClipping = frame.mapMode == MapMode::Still; @@ -52,14 +54,15 @@ void Painter::renderSymbol(PaintParameters& parameters, *buffers.vertexBuffer, *buffers.indexBuffer, buffers.segments, - bucket.paintPropertyBinders.at(layer.getID()), - layer.impl->paint.evaluated, + binders, + paintProperties, state.getZoom() ); }; if (bucket.hasIconData()) { auto values = layer.impl->iconPropertyValues(layout); + auto paintPropertyValues = layer.impl->iconPaintProperties(); SpriteAtlas& atlas = *layer.impl->spriteAtlas; const bool iconScaled = values.paintSize != 1.0f || frame.pixelRatio != atlas.getPixelRatio() || bucket.iconsNeedLinear; @@ -69,24 +72,30 @@ void Painter::renderSymbol(PaintParameters& parameters, const Size texsize = atlas.getSize(); if (bucket.sdfIcons) { - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolIconSDF, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } else { draw(parameters.programs.symbolIcon, SymbolIconProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state), bucket.icon, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).first, + paintPropertyValues); } } @@ -94,21 +103,26 @@ void Painter::renderSymbol(PaintParameters& parameters, glyphAtlas->bind(context, 0); auto values = layer.impl->textPropertyValues(layout); + auto paintPropertyValues = layer.impl->textPaintProperties(); const Size texsize = glyphAtlas->getSize(); - if (values.hasHalo()) { + if (values.hasHalo) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::haloUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Halo), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } - if (values.hasForeground()) { + if (values.hasFill) { draw(parameters.programs.symbolGlyph, - SymbolSDFProgram::foregroundUniformValues(values, texsize, pixelsToGLUnits, tile, state, frame.pixelRatio), + SymbolSDFTextProgram::uniformValues(values, texsize, pixelsToGLUnits, tile, state, SymbolSDFPart::Fill), bucket.text, - values); + values, + bucket.paintPropertyBinders.at(layer.getID()).second, + paintPropertyValues); } } diff --git a/src/mbgl/renderer/symbol_bucket.cpp b/src/mbgl/renderer/symbol_bucket.cpp index 9d4bde9d07..fa4178dda1 100644 --- a/src/mbgl/renderer/symbol_bucket.cpp +++ b/src/mbgl/renderer/symbol_bucket.cpp @@ -9,7 +9,8 @@ namespace mbgl { using namespace style; SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, - const std::unordered_map& layerPaintProperties, + const std::unordered_map>& layerPaintProperties, float zoom, bool sdfIcons_, bool iconsNeedLinear_) @@ -17,8 +18,10 @@ SymbolBucket::SymbolBucket(style::SymbolLayoutProperties::Evaluated layout_, sdfIcons(sdfIcons_), iconsNeedLinear(iconsNeedLinear_) { for (const auto& pair : layerPaintProperties) { - paintPropertyBinders.emplace(pair.first, - SymbolIconProgram::PaintPropertyBinders(pair.second, zoom)); + paintPropertyBinders.emplace(pair.first, std::make_pair( + SymbolIconProgram::PaintPropertyBinders(pair.second.first, zoom), + SymbolSDFTextProgram::PaintPropertyBinders(pair.second.second, zoom) + )); } } @@ -39,7 +42,8 @@ void SymbolBucket::upload(gl::Context& context) { } for (auto& pair : paintPropertyBinders) { - pair.second.upload(context); + pair.second.first.upload(context); + pair.second.second.upload(context); } uploaded = true; diff --git a/src/mbgl/renderer/symbol_bucket.hpp b/src/mbgl/renderer/symbol_bucket.hpp index 0b40bb34ae..dcf3f5f495 100644 --- a/src/mbgl/renderer/symbol_bucket.hpp +++ b/src/mbgl/renderer/symbol_bucket.hpp @@ -17,7 +17,7 @@ namespace mbgl { class SymbolBucket : public Bucket { public: SymbolBucket(style::SymbolLayoutProperties::Evaluated, - const std::unordered_map&, + const std::unordered_map>&, float zoom, bool sdfIcons, bool iconsNeedLinear); @@ -33,12 +33,14 @@ public: const bool sdfIcons; const bool iconsNeedLinear; - std::unordered_map paintPropertyBinders; + std::unordered_map> paintPropertyBinders; struct TextBuffer { gl::VertexVector vertices; gl::IndexVector triangles; - gl::SegmentVector segments; + gl::SegmentVector segments; optional> vertexBuffer; optional> indexBuffer; @@ -47,7 +49,7 @@ public: struct IconBuffer { gl::VertexVector vertices; gl::IndexVector triangles; - gl::SegmentVector segments; + gl::SegmentVector segments; optional> vertexBuffer; optional> indexBuffer; diff --git a/src/mbgl/shaders/symbol_icon.cpp b/src/mbgl/shaders/symbol_icon.cpp index eca9342d54..e6728e15de 100644 --- a/src/mbgl/shaders/symbol_icon.cpp +++ b/src/mbgl/shaders/symbol_icon.cpp @@ -66,6 +66,10 @@ attribute vec2 a_offset; attribute vec2 a_texture_pos; attribute vec4 a_data; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; // matrix is for the vertex position. uniform mat4 u_matrix; @@ -80,6 +84,8 @@ varying vec2 v_tex; varying vec2 v_fade_tex; void main() { + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + vec2 a_tex = a_texture_pos.xy; mediump float a_labelminzoom = a_data[0]; mediump vec2 a_zoom = a_data.pq; @@ -122,13 +128,16 @@ precision mediump float; #endif uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp float u_opacity; + +varying lowp float opacity; varying vec2 v_tex; varying vec2 v_fade_tex; void main() { - lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * u_opacity; + + + lowp float alpha = texture2D(u_fadetexture, v_fade_tex).a * opacity; gl_FragColor = texture2D(u_texture, v_tex) * alpha; #ifdef OVERDRAW_INSPECTOR diff --git a/src/mbgl/shaders/symbol_sdf.cpp b/src/mbgl/shaders/symbol_sdf.cpp index 7554597893..e087242bf8 100644 --- a/src/mbgl/shaders/symbol_sdf.cpp +++ b/src/mbgl/shaders/symbol_sdf.cpp @@ -68,6 +68,26 @@ attribute vec2 a_offset; attribute vec2 a_texture_pos; attribute vec4 a_data; +uniform lowp float a_fill_color_t; +attribute lowp vec4 a_fill_color_min; +attribute lowp vec4 a_fill_color_max; +varying lowp vec4 fill_color; +uniform lowp float a_halo_color_t; +attribute lowp vec4 a_halo_color_min; +attribute lowp vec4 a_halo_color_max; +varying lowp vec4 halo_color; +uniform lowp float a_opacity_t; +attribute lowp float a_opacity_min; +attribute lowp float a_opacity_max; +varying lowp float opacity; +uniform lowp float a_halo_width_t; +attribute lowp float a_halo_width_min; +attribute lowp float a_halo_width_max; +varying lowp float halo_width; +uniform lowp float a_halo_blur_t; +attribute lowp float a_halo_blur_min; +attribute lowp float a_halo_blur_max; +varying lowp float halo_blur; // matrix is for the vertex position. uniform mat4 u_matrix; @@ -87,6 +107,12 @@ varying vec2 v_fade_tex; varying float v_gamma_scale; void main() { + fill_color = mix(a_fill_color_min, a_fill_color_max, a_fill_color_t); + halo_color = mix(a_halo_color_min, a_halo_color_max, a_halo_color_t); + opacity = mix(a_opacity_min, a_opacity_max, a_opacity_t); + halo_width = mix(a_halo_width_min, a_halo_width_max, a_halo_width_t); + halo_blur = mix(a_halo_blur_min, a_halo_blur_max, a_halo_blur_t); + vec2 a_tex = a_texture_pos.xy; mediump float a_labelminzoom = a_data[0]; mediump vec2 a_zoom = a_data.pq; @@ -163,24 +189,47 @@ precision mediump float; #endif #endif +#define SDF_PX 8.0 +#define EDGE_GAMMA 0.105/DEVICE_PIXEL_RATIO + +uniform bool u_is_halo; +varying lowp vec4 fill_color; +varying lowp vec4 halo_color; +varying lowp float opacity; +varying lowp float halo_width; +varying lowp float halo_blur; + uniform sampler2D u_texture; uniform sampler2D u_fadetexture; -uniform lowp vec4 u_color; -uniform lowp float u_opacity; -uniform lowp float u_buffer; -uniform highp float u_gamma; +uniform lowp float u_font_scale; +uniform highp float u_gamma_scale; varying vec2 v_tex; varying vec2 v_fade_tex; varying float v_gamma_scale; void main() { + + + + + + + lowp vec4 color = fill_color; + lowp float gamma = EDGE_GAMMA / u_gamma_scale; + lowp float buff = (256.0 - 64.0) / 256.0; + if (u_is_halo) { + color = halo_color; + gamma = (halo_blur * 1.19 / SDF_PX + EDGE_GAMMA) / u_gamma_scale; + buff = (6.0 - halo_width / u_font_scale) / SDF_PX; + } + lowp float dist = texture2D(u_texture, v_tex).a; lowp float fade_alpha = texture2D(u_fadetexture, v_fade_tex).a; - highp float gamma = u_gamma * v_gamma_scale; - highp float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * fade_alpha; + highp float gamma_scaled = gamma * v_gamma_scale; + highp float alpha = smoothstep(buff - gamma_scaled, buff + gamma_scaled, dist) * fade_alpha; - gl_FragColor = u_color * (alpha * u_opacity); + gl_FragColor = color * (alpha * opacity); #ifdef OVERDRAW_INSPECTOR gl_FragColor = vec4(1.0); diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp index 6364091207..d85b8c00e6 100644 --- a/src/mbgl/style/layers/symbol_layer.cpp +++ b/src/mbgl/style/layers/symbol_layer.cpp @@ -542,95 +542,115 @@ void SymbolLayer::setTextOptional(PropertyValue value) { // Paint properties -PropertyValue SymbolLayer::getDefaultIconOpacity() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconOpacity() { return { 1 }; } -PropertyValue SymbolLayer::getIconOpacity(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconOpacity(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconOpacity(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconOpacity(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconOpacity(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconOpacityTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconColor() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconColor() { return { Color::black() }; } -PropertyValue SymbolLayer::getIconColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconHaloColor() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconHaloColor() { return { {} }; } -PropertyValue SymbolLayer::getIconHaloColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconHaloColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconHaloColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconHaloColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconHaloColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconHaloWidth() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconHaloWidth() { return { 0 }; } -PropertyValue SymbolLayer::getIconHaloWidth(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconHaloWidth(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconHaloWidth(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconHaloWidth(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconHaloWidth(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloWidthTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultIconHaloBlur() { +DataDrivenPropertyValue SymbolLayer::getDefaultIconHaloBlur() { return { 0 }; } -PropertyValue SymbolLayer::getIconHaloBlur(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getIconHaloBlur(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setIconHaloBlur(PropertyValue value, const optional& klass) { +void SymbolLayer::setIconHaloBlur(DataDrivenPropertyValue value, const optional& klass) { if (value == getIconHaloBlur(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setIconHaloBlurTransition(const TransitionOptions& value, const optional& klass) { @@ -671,99 +691,119 @@ void SymbolLayer::setIconTranslateAnchor(PropertyValue valu impl->observer->onLayerPaintPropertyChanged(*this); } -PropertyValue SymbolLayer::getDefaultTextOpacity() { - return { 1 }; -} - void SymbolLayer::setIconTranslateAnchorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getTextOpacity(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getDefaultTextOpacity() { + return { 1 }; +} + +DataDrivenPropertyValue SymbolLayer::getTextOpacity(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextOpacity(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextOpacity(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextOpacity(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); -} - -PropertyValue SymbolLayer::getDefaultTextColor() { - return { Color::black() }; + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextOpacityTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getTextColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getDefaultTextColor() { + return { Color::black() }; +} + +DataDrivenPropertyValue SymbolLayer::getTextColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultTextHaloColor() { +DataDrivenPropertyValue SymbolLayer::getDefaultTextHaloColor() { return { {} }; } -PropertyValue SymbolLayer::getTextHaloColor(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getTextHaloColor(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextHaloColor(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextHaloColor(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextHaloColor(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloColorTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultTextHaloWidth() { +DataDrivenPropertyValue SymbolLayer::getDefaultTextHaloWidth() { return { 0 }; } -PropertyValue SymbolLayer::getTextHaloWidth(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getTextHaloWidth(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextHaloWidth(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextHaloWidth(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextHaloWidth(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloWidthTransition(const TransitionOptions& value, const optional& klass) { impl->paint.setTransition(value, klass); } -PropertyValue SymbolLayer::getDefaultTextHaloBlur() { +DataDrivenPropertyValue SymbolLayer::getDefaultTextHaloBlur() { return { 0 }; } -PropertyValue SymbolLayer::getTextHaloBlur(const optional& klass) const { +DataDrivenPropertyValue SymbolLayer::getTextHaloBlur(const optional& klass) const { return impl->paint.get(klass); } -void SymbolLayer::setTextHaloBlur(PropertyValue value, const optional& klass) { +void SymbolLayer::setTextHaloBlur(DataDrivenPropertyValue value, const optional& klass) { if (value == getTextHaloBlur(klass)) return; impl->paint.set(value, klass); - impl->observer->onLayerPaintPropertyChanged(*this); + if (value.isDataDriven()) { + impl->observer->onLayerDataDrivenPaintPropertyChanged(*this); + } else { + impl->observer->onLayerPaintPropertyChanged(*this); + } } void SymbolLayer::setTextHaloBlurTransition(const TransitionOptions& value, const optional& klass) { diff --git a/src/mbgl/style/layers/symbol_layer_impl.cpp b/src/mbgl/style/layers/symbol_layer_impl.cpp index 32547e465a..ff59b14d65 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.cpp +++ b/src/mbgl/style/layers/symbol_layer_impl.cpp @@ -16,9 +16,14 @@ bool SymbolLayer::Impl::evaluate(const PropertyEvaluationParameters& parameters) // text-size and icon-size are layout properties but they also need to be evaluated as paint properties: iconSize = layout.evaluate(parameters); textSize = layout.evaluate(parameters); - - passes = ((paint.evaluated.get() > 0 && (paint.evaluated.get().a > 0 || paint.evaluated.get().a > 0) && iconSize > 0) - || (paint.evaluated.get() > 0 && (paint.evaluated.get().a > 0 || paint.evaluated.get().a > 0) && textSize > 0)) + + auto hasIconOpacity = paint.evaluated.get().constantOr(Color::black()).a > 0 || + paint.evaluated.get().constantOr(Color::black()).a > 0; + auto hasTextOpacity = paint.evaluated.get().constantOr(Color::black()).a > 0 || + paint.evaluated.get().constantOr(Color::black()).a > 0; + + passes = ((paint.evaluated.get().constantOr(1) > 0 && hasIconOpacity && iconSize > 0) + || (paint.evaluated.get().constantOr(1) > 0 && hasTextOpacity && textSize > 0)) ? RenderPass::Translucent : RenderPass::None; return paint.hasTransition(); @@ -38,20 +43,43 @@ std::unique_ptr SymbolLayer::Impl::createLayout(const BucketParame *spriteAtlas); } -SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { - return SymbolPropertyValues { - layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment - layout_.get(), - layout_.get(), +IconPaintProperties::Evaluated SymbolLayer::Impl::iconPaintProperties() const { + return IconPaintProperties::Evaluated { paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), + paint.evaluated.get() + }; +} + +TextPaintProperties::Evaluated SymbolLayer::Impl::textPaintProperties() const { + return TextPaintProperties::Evaluated { + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get(), + paint.evaluated.get() + }; +} + + +SymbolPropertyValues SymbolLayer::Impl::iconPropertyValues(const SymbolLayoutProperties::Evaluated& layout_) const { + return SymbolPropertyValues { + layout_.get(), // icon-pitch-alignment is not yet implemented; inherit the rotation alignment + layout_.get(), + layout_.get(), + paint.evaluated.get(), paint.evaluated.get(), iconSize, - 1.0f + 1.0f, + paint.evaluated.get().constantOr(Color::black()).a > 0 && + paint.evaluated.get().constantOr(1), + paint.evaluated.get().constantOr(Color::black()).a > 0 }; } @@ -60,15 +88,13 @@ SymbolPropertyValues SymbolLayer::Impl::textPropertyValues(const SymbolLayoutPro layout_.get(), layout_.get(), layout_.get(), - paint.evaluated.get(), - paint.evaluated.get(), - paint.evaluated.get(), - paint.evaluated.get(), - paint.evaluated.get(), paint.evaluated.get(), paint.evaluated.get(), textSize, - 24.0f + 24.0f, + paint.evaluated.get().constantOr(Color::black()).a > 0 && + paint.evaluated.get().constantOr(1), + paint.evaluated.get().constantOr(Color::black()).a > 0 }; } diff --git a/src/mbgl/style/layers/symbol_layer_impl.hpp b/src/mbgl/style/layers/symbol_layer_impl.hpp index c00c2b0bba..1e9f05e4c7 100644 --- a/src/mbgl/style/layers/symbol_layer_impl.hpp +++ b/src/mbgl/style/layers/symbol_layer_impl.hpp @@ -1,5 +1,6 @@ #pragma once +#include #include #include #include @@ -10,6 +11,30 @@ class SpriteAtlas; class SymbolLayout; namespace style { + + +// {icon,text}-specific paint-property packs for use in the symbol Programs. +// Since each program deals either with icons or text, using a smaller property set +// lets us avoid unnecessarily binding attributes for properties the program wouldn't use. +class IconPaintProperties : public PaintProperties< + IconOpacity, + IconColor, + IconHaloColor, + IconHaloWidth, + IconHaloBlur, + IconTranslate, + IconTranslateAnchor +> {}; + +class TextPaintProperties : public PaintProperties< + TextOpacity, + TextColor, + TextHaloColor, + TextHaloWidth, + TextHaloBlur, + TextTranslate, + TextTranslateAnchor +> {}; // Repackaging evaluated values from SymbolLayoutProperties + SymbolPaintProperties // for genericity over icons vs. text. @@ -21,24 +46,14 @@ public: float layoutSize; // Paint - float opacity; - Color color; - Color haloColor; - float haloWidth; - float haloBlur; std::array translate; TranslateAnchorType translateAnchor; float paintSize; float sdfScale; // Constant (1.0 or 24.0) - - bool hasHalo() const { - return haloColor.a > 0.0f && haloWidth > 0.0f; - } - - bool hasForeground() const { - return color.a > 0.0f; - } + + bool hasHalo; + bool hasFill; }; class SymbolLayer::Impl : public Layer::Impl { @@ -54,6 +69,9 @@ public: std::unique_ptr createLayout(const BucketParameters&, const std::vector&, const GeometryTileLayer&) const; + IconPaintProperties::Evaluated iconPaintProperties() const; + TextPaintProperties::Evaluated textPaintProperties() const; + SymbolPropertyValues iconPropertyValues(const SymbolLayoutProperties::Evaluated&) const; SymbolPropertyValues textPropertyValues(const SymbolLayoutProperties::Evaluated&) const; diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp index 3bdae377ea..f2b7bfa00f 100644 --- a/src/mbgl/style/layers/symbol_layer_properties.hpp +++ b/src/mbgl/style/layers/symbol_layer_properties.hpp @@ -180,23 +180,23 @@ struct TextOptional : LayoutProperty { static bool defaultValue() { return false; } }; -struct IconOpacity : PaintProperty { +struct IconOpacity : DataDrivenPaintProperty { static float defaultValue() { return 1; } }; -struct IconColor : PaintProperty { +struct IconColor : DataDrivenPaintProperty { static Color defaultValue() { return Color::black(); } }; -struct IconHaloColor : PaintProperty { +struct IconHaloColor : DataDrivenPaintProperty { static Color defaultValue() { return {}; } }; -struct IconHaloWidth : PaintProperty { +struct IconHaloWidth : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; -struct IconHaloBlur : PaintProperty { +struct IconHaloBlur : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; @@ -208,23 +208,23 @@ struct IconTranslateAnchor : PaintProperty { static TranslateAnchorType defaultValue() { return TranslateAnchorType::Map; } }; -struct TextOpacity : PaintProperty { +struct TextOpacity : DataDrivenPaintProperty { static float defaultValue() { return 1; } }; -struct TextColor : PaintProperty { +struct TextColor : DataDrivenPaintProperty { static Color defaultValue() { return Color::black(); } }; -struct TextHaloColor : PaintProperty { +struct TextHaloColor : DataDrivenPaintProperty { static Color defaultValue() { return {}; } }; -struct TextHaloWidth : PaintProperty { +struct TextHaloWidth : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; -struct TextHaloBlur : PaintProperty { +struct TextHaloBlur : DataDrivenPaintProperty { static float defaultValue() { return 0; } }; diff --git a/src/mbgl/text/shaping.cpp b/src/mbgl/text/shaping.cpp index b43ba0220c..e68566d419 100644 --- a/src/mbgl/text/shaping.cpp +++ b/src/mbgl/text/shaping.cpp @@ -3,16 +3,15 @@ namespace mbgl { -PositionedIcon shapeIcon(const SpriteAtlasElement& image, - const SymbolFeature& feature) { - float dx = feature.iconOffset[0]; - float dy = feature.iconOffset[1]; +PositionedIcon shapeIcon(const SpriteAtlasElement& image, const std::array& iconOffset, const float iconRotation) { + float dx = iconOffset[0]; + float dy = iconOffset[1]; float x1 = dx - image.spriteImage->getWidth() / 2.0f; float x2 = x1 + image.spriteImage->getWidth(); float y1 = dy - image.spriteImage->getHeight() / 2.0f; float y2 = y1 + image.spriteImage->getHeight(); - return PositionedIcon(image, y1, y2, x1, x2, feature.iconRotation); + return PositionedIcon(image, y1, y2, x1, x2, iconRotation); } } // namespace mbgl diff --git a/src/mbgl/text/shaping.hpp b/src/mbgl/text/shaping.hpp index b0e6ae3b1d..1b7b8b2733 100644 --- a/src/mbgl/text/shaping.hpp +++ b/src/mbgl/text/shaping.hpp @@ -36,7 +36,6 @@ public: explicit operator bool() const { return image && (*image).pos.hasArea(); } }; -PositionedIcon shapeIcon(const SpriteAtlasElement&, - const SymbolFeature&); +PositionedIcon shapeIcon(const SpriteAtlasElement&, const std::array& iconOffset, const float iconRotation); } // namespace mbgl diff --git a/src/mbgl/tile/vector_tile.cpp b/src/mbgl/tile/vector_tile.cpp index a195885415..68f48e81fd 100644 --- a/src/mbgl/tile/vector_tile.cpp +++ b/src/mbgl/tile/vector_tile.cpp @@ -15,9 +15,23 @@ class VectorTileLayer; using packed_iter_type = protozero::iterator_range; +struct VectorTileLayerData { + VectorTileLayerData(std::shared_ptr); + + // Hold a reference to the underlying pbf data that backs the lazily-built + // components of the owning VectorTileLayer and VectorTileFeature objects + std::shared_ptr data; + + uint32_t version = 1; + uint32_t extent = 4096; + std::unordered_map keysMap; + std::vector> keys; + std::vector values; +}; + class VectorTileFeature : public GeometryTileFeature { public: - VectorTileFeature(protozero::pbf_reader, const VectorTileLayer&); + VectorTileFeature(protozero::pbf_reader, std::shared_ptr layerData); FeatureType getType() const override { return type; } optional getValue(const std::string&) const override; @@ -26,16 +40,16 @@ public: GeometryCollection getGeometries() const override; private: - const VectorTileLayer& layer; + std::shared_ptr layerData; optional id; FeatureType type = FeatureType::Unknown; packed_iter_type tags_iter; packed_iter_type geometry_iter; }; - + class VectorTileLayer : public GeometryTileLayer { public: - VectorTileLayer(protozero::pbf_reader); + VectorTileLayer(protozero::pbf_reader, std::shared_ptr); std::size_t featureCount() const override { return features.size(); } std::unique_ptr getFeature(std::size_t) const override; @@ -46,12 +60,8 @@ private: friend class VectorTileFeature; std::string name; - uint32_t version = 1; - uint32_t extent = 4096; - std::unordered_map keysMap; - std::vector> keys; - std::vector values; std::vector features; + std::shared_ptr data; }; class VectorTileData : public GeometryTileData { @@ -117,8 +127,8 @@ Value parseValue(protozero::pbf_reader data) { return false; } -VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const VectorTileLayer& layer_) - : layer(layer_) { +VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, std::shared_ptr layerData_) + : layerData(std::move(layerData_)) { while (feature_pbf.next()) { switch (feature_pbf.tag()) { case 1: // id @@ -141,8 +151,8 @@ VectorTileFeature::VectorTileFeature(protozero::pbf_reader feature_pbf, const Ve } optional VectorTileFeature::getValue(const std::string& key) const { - auto keyIter = layer.keysMap.find(key); - if (keyIter == layer.keysMap.end()) { + auto keyIter = layerData->keysMap.find(key); + if (keyIter == layerData->keysMap.end()) { return optional(); } @@ -151,7 +161,7 @@ optional VectorTileFeature::getValue(const std::string& key) const { while (start_itr != end_itr) { uint32_t tag_key = static_cast(*start_itr++); - if (layer.keysMap.size() <= tag_key) { + if (layerData->keysMap.size() <= tag_key) { throw std::runtime_error("feature referenced out of range key"); } @@ -160,12 +170,12 @@ optional VectorTileFeature::getValue(const std::string& key) const { } uint32_t tag_val = static_cast(*start_itr++);; - if (layer.values.size() <= tag_val) { + if (layerData->values.size() <= tag_val) { throw std::runtime_error("feature referenced out of range value"); } if (tag_key == keyIter->second) { - return layer.values[tag_val]; + return layerData->values[tag_val]; } } @@ -182,7 +192,7 @@ std::unordered_map VectorTileFeature::getProperties() const { throw std::runtime_error("uneven number of feature tag ids"); } uint32_t tag_val = static_cast(*start_itr++); - properties[layer.keys.at(tag_key)] = layer.values.at(tag_val); + properties[layerData->keys.at(tag_key)] = layerData->values.at(tag_val); } return properties; } @@ -196,7 +206,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { uint32_t length = 0; int32_t x = 0; int32_t y = 0; - const float scale = float(util::EXTENT) / layer.extent; + const float scale = float(util::EXTENT) / layerData->extent; GeometryCollection lines; @@ -234,7 +244,7 @@ GeometryCollection VectorTileFeature::getGeometries() const { } } - if (layer.version >= 2 || type != FeatureType::Polygon) { + if (layerData->version >= 2 || type != FeatureType::Polygon) { return lines; } @@ -250,7 +260,7 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const parsed = true; protozero::pbf_reader tile_pbf(*data); while (tile_pbf.next(3)) { - VectorTileLayer layer(tile_pbf.get_message()); + VectorTileLayer layer(tile_pbf.get_message(), data); layers.emplace(layer.name, std::move(layer)); } } @@ -262,7 +272,13 @@ const GeometryTileLayer* VectorTileData::getLayer(const std::string& name) const return nullptr; } -VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { +VectorTileLayerData::VectorTileLayerData(std::shared_ptr pbfData) : + data(std::move(pbfData)) +{} + +VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf, std::shared_ptr pbfData) + : data(std::make_shared(std::move(pbfData))) +{ while (layer_pbf.next()) { switch (layer_pbf.tag()) { case 1: // name @@ -273,18 +289,18 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { break; case 3: // keys { - auto iter = keysMap.emplace(layer_pbf.get_string(), keysMap.size()); - keys.emplace_back(std::reference_wrapper(iter.first->first)); + auto iter = data->keysMap.emplace(layer_pbf.get_string(), data->keysMap.size()); + data->keys.emplace_back(std::reference_wrapper(iter.first->first)); } break; case 4: // values - values.emplace_back(parseValue(layer_pbf.get_message())); + data->values.emplace_back(parseValue(layer_pbf.get_message())); break; case 5: // extent - extent = layer_pbf.get_uint32(); + data->extent = layer_pbf.get_uint32(); break; case 15: // version - version = layer_pbf.get_uint32(); + data->version = layer_pbf.get_uint32(); break; default: layer_pbf.skip(); @@ -294,7 +310,7 @@ VectorTileLayer::VectorTileLayer(protozero::pbf_reader layer_pbf) { } std::unique_ptr VectorTileLayer::getFeature(std::size_t i) const { - return std::make_unique(features.at(i), *this); + return std::make_unique(features.at(i), data); } std::string VectorTileLayer::getName() const { diff --git a/test/tile/vector_tile.test.cpp b/test/tile/vector_tile.test.cpp index 2fe2586ec2..49fdcbd9f8 100644 --- a/test/tile/vector_tile.test.cpp +++ b/test/tile/vector_tile.test.cpp @@ -61,9 +61,11 @@ TEST(VectorTile, Issue7615) { style::SymbolLayer symbolLayer("symbol", "source"); auto symbolBucket = std::make_shared( style::SymbolLayoutProperties::Evaluated(), - std::unordered_map(), + std::unordered_map< + std::string, + std::pair>(), 0.0f, false, false); - + // Simulate placement of a symbol layer. tile.onPlacement(GeometryTile::PlacementResult { {{ diff --git a/test/util/merge_lines.test.cpp b/test/util/merge_lines.test.cpp index 0a39419b70..8a3a400887 100644 --- a/test/util/merge_lines.test.cpp +++ b/test/util/merge_lines.test.cpp @@ -8,95 +8,131 @@ const std::u16string bbb = u"b"; using namespace mbgl; -TEST(MergeLines, SameText) { - // merges lines with the same text - std::vector input1 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{8, 0}, {9, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{5, 0}, {6, 0}}}, aaa, {}, {{0, 0}}, 0, 0 } +class GeometryTileFeatureStub : public GeometryTileFeature { +public: + GeometryTileFeatureStub(optional id_, FeatureType type_, GeometryCollection geometry_, + std::unordered_map properties_) : + id(id_), + type(type_), + geometry(geometry_), + properties(properties_) + {} + + FeatureType getType() const override { return type; } + optional getValue(const std::string& key) const override { + auto it = properties.find(key); + if (it != properties.end()) { + return it->second; + } + return {}; }; + std::unordered_map getProperties() const override { return properties; }; + optional getID() const override { return id; }; + GeometryCollection getGeometries() const override { return geometry; }; + + optional id; + FeatureType type; + GeometryCollection geometry; + std::unordered_map properties; +}; + +class SymbolFeatureStub : public SymbolFeature { +public: + SymbolFeatureStub(optional id_, FeatureType type_, GeometryCollection geometry_, + std::unordered_map properties_, optional text_, + optional icon_, std::size_t index_) : + SymbolFeature(std::make_unique(id_, type_, geometry_, properties_)) + { + text = text_; + icon = icon_; + index = index_; + } +}; - const std::vector expected1 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, bbb, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 } +TEST(MergeLines, SameText) { + // merges lines with the same text + std::vector input1; + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, bbb, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{8, 0}, {9, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{6, 0}, {7, 0}, {8, 0}}}, {}, aaa, {}, 0)); + input1.push_back(SymbolFeatureStub({}, FeatureType::LineString, {{{5, 0}, {6, 0}}}, {}, aaa, {}, 0)); + + const std::vector expected1 = { + { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}}}, {} }, + { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {} }, + { {}, FeatureType::LineString, {{{5, 0}, {6, 0}, {7, 0}, {8, 0}, {9, 0}}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} } }; - + mbgl::util::mergeLines(input1); for (int i = 0; i < 6; i++) { - EXPECT_TRUE(input1[i].geometry == expected1[i].geometry); + EXPECT_TRUE(input1[i].geometry == expected1[i].getGeometries()); } } TEST(MergeLines, BothEnds) { // mergeLines handles merge from both ends - std::vector input2 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 } - }; - - const std::vector expected2 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 } + std::vector input2; + input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 }); + input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {5, 0}, {6, 0}}}, {}, aaa, {}, 0 }); + input2.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 }); + + const std::vector expected2 = { + { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {5, 0}, {6, 0}}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} } }; mbgl::util::mergeLines(input2); for (int i = 0; i < 3; i++) { - EXPECT_TRUE(input2[i].geometry == expected2[i].geometry); + EXPECT_TRUE(input2[i].geometry == expected2[i].getGeometries()); } } TEST(MergeLines, CircularLines) { // mergeLines handles circular lines - std::vector input3 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{{4, 0}, {0, 0}}}, aaa, {}, {{0, 0}}, 0, 0 } - }; - - const std::vector expected3 = { - { FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 } + std::vector input3; + input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}}}, {}, aaa, {}, 0 }); + input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{2, 0}, {3, 0}, {4, 0}}}, {}, aaa, {}, 0 }); + input3.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{{4, 0}, {0, 0}}}, {}, aaa, {}, 0 }); + + const std::vector expected3 = { + { {}, FeatureType::LineString, {{{0, 0}, {1, 0}, {2, 0}, {3, 0}, {4, 0}, {0, 0}}}, {} }, + { {}, FeatureType::LineString, {{}}, {} }, + { {}, FeatureType::LineString, {{}}, {} } }; mbgl::util::mergeLines(input3); for (int i = 0; i < 3; i++) { - EXPECT_TRUE(input3[i].geometry == expected3[i].geometry); + EXPECT_TRUE(input3[i].geometry == expected3[i].getGeometries()); } } TEST(MergeLines, EmptyOuterGeometry) { - std::vector input = { - { FeatureType::LineString, {}, aaa, {}, {{0, 0}}, 0, 0 }, - }; + std::vector input; + input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {}, {}, aaa, {}, 0 }); - const std::vector expected = input; + const std::vector expected = { { {}, FeatureType::LineString, {}, {} } }; mbgl::util::mergeLines(input); - EXPECT_EQ(expected[0].geometry, input[0].geometry); + EXPECT_EQ(input[0].geometry, expected[0].getGeometries()); } TEST(MergeLines, EmptyInnerGeometry) { - std::vector input = { - { FeatureType::LineString, {{}}, aaa, {}, {{0, 0}}, 0, 0 }, - }; + std::vector input; + input.push_back(SymbolFeatureStub { {}, FeatureType::LineString, {{}}, {}, aaa, {}, 0 }); - const std::vector expected = input; + const std::vector expected = { { {}, FeatureType::LineString, {{}}, {} } }; mbgl::util::mergeLines(input); - EXPECT_EQ(expected[0].geometry, input[0].geometry); + EXPECT_EQ(input[0].geometry, expected[0].getGeometries()); } -- cgit v1.2.1