summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn Firebaugh <john.firebaugh@gmail.com>2018-01-05 12:40:54 -0800
committerJohn Firebaugh <john.firebaugh@gmail.com>2018-01-10 12:41:51 -0800
commit5f61dd81ff128761f46adcbe407b9a0df702e5dd (patch)
treef14a4191fb36d9a94365ca6394bbc62f7b170bbf
parent06033885b78eee2c4bb2cba476594f793cac5979 (diff)
downloadqtlocation-mapboxgl-upstream/dds-text-font.tar.gz
[core, ios, macos, android] Add data-driven-styling support for `text-font`upstream/dds-text-font
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp4
-rw-r--r--include/mbgl/style/expression/assertion.hpp5
-rw-r--r--include/mbgl/style/expression/at.hpp4
-rw-r--r--include/mbgl/style/expression/boolean_operator.hpp5
-rw-r--r--include/mbgl/style/expression/case.hpp2
-rw-r--r--include/mbgl/style/expression/coalesce.hpp4
-rw-r--r--include/mbgl/style/expression/coercion.hpp5
-rw-r--r--include/mbgl/style/expression/compound_expression.hpp8
-rw-r--r--include/mbgl/style/expression/equals.hpp1
-rw-r--r--include/mbgl/style/expression/expression.hpp18
-rw-r--r--include/mbgl/style/expression/interpolate.hpp2
-rw-r--r--include/mbgl/style/expression/let.hpp6
-rw-r--r--include/mbgl/style/expression/literal.hpp6
-rw-r--r--include/mbgl/style/expression/match.hpp7
-rw-r--r--include/mbgl/style/expression/step.hpp2
-rw-r--r--include/mbgl/style/expression/value.hpp9
-rw-r--r--include/mbgl/style/function/camera_function.hpp7
-rw-r--r--include/mbgl/style/function/composite_function.hpp4
-rw-r--r--include/mbgl/style/function/convert.hpp4
-rw-r--r--include/mbgl/style/function/source_function.hpp4
-rw-r--r--include/mbgl/style/layers/symbol_layer.hpp6
-rw-r--r--include/mbgl/util/optional.hpp3
-rw-r--r--platform/android/MapboxGLAndroidSDK/src/main/java/com/mapbox/mapboxsdk/style/layers/PropertyFactory.java6
-rw-r--r--platform/android/MapboxGLAndroidSDKTestApp/src/androidTest/java/com/mapbox/mapboxsdk/testapp/style/SymbolLayerTest.java57
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.h14
-rw-r--r--platform/darwin/src/MGLSymbolStyleLayer.mm6
-rw-r--r--platform/darwin/test/MGLSymbolStyleLayerTests.mm7
-rw-r--r--platform/node/test/ignores.json1
-rw-r--r--src/mbgl/layout/symbol_layout.cpp29
-rw-r--r--src/mbgl/style/conversion/make_property_setters.hpp2
-rw-r--r--src/mbgl/style/expression/assertion.cpp10
-rw-r--r--src/mbgl/style/expression/boolean_operator.cpp8
-rw-r--r--src/mbgl/style/expression/case.cpp13
-rw-r--r--src/mbgl/style/expression/coalesce.cpp10
-rw-r--r--src/mbgl/style/expression/coercion.cpp10
-rw-r--r--src/mbgl/style/expression/equals.cpp4
-rw-r--r--src/mbgl/style/expression/interpolate.cpp10
-rw-r--r--src/mbgl/style/expression/let.cpp8
-rw-r--r--src/mbgl/style/expression/match.cpp14
-rw-r--r--src/mbgl/style/expression/step.cpp10
-rw-r--r--src/mbgl/style/layers/symbol_layer.cpp6
-rw-r--r--src/mbgl/style/layers/symbol_layer_properties.hpp2
-rw-r--r--src/mbgl/style/parser.cpp29
-rw-r--r--test/style/style_parser.test.cpp75
44 files changed, 379 insertions, 68 deletions
diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp
index 2516eea024..7f36f8aac2 100644
--- a/include/mbgl/style/expression/array_assertion.hpp
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -30,6 +30,10 @@ public:
return false;
}
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return input->possibleOutputs();
+ }
+
private:
std::unique_ptr<Expression> input;
};
diff --git a/include/mbgl/style/expression/assertion.hpp b/include/mbgl/style/expression/assertion.hpp
index 504d49f4e5..43ea73f2ba 100644
--- a/include/mbgl/style/expression/assertion.hpp
+++ b/include/mbgl/style/expression/assertion.hpp
@@ -1,7 +1,9 @@
#pragma once
+
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/expression/parsing_context.hpp>
+
#include <memory>
#include <vector>
@@ -23,6 +25,8 @@ public:
bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
private:
std::vector<std::unique_ptr<Expression>> inputs;
};
@@ -30,4 +34,3 @@ private:
} // namespace expression
} // namespace style
} // namespace mbgl
-
diff --git a/include/mbgl/style/expression/at.hpp b/include/mbgl/style/expression/at.hpp
index e3eefa4fe8..27fccc761f 100644
--- a/include/mbgl/style/expression/at.hpp
+++ b/include/mbgl/style/expression/at.hpp
@@ -28,6 +28,10 @@ public:
return false;
}
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return { nullopt };
+ }
+
private:
std::unique_ptr<Expression> index;
std::unique_ptr<Expression> input;
diff --git a/include/mbgl/style/expression/boolean_operator.hpp b/include/mbgl/style/expression/boolean_operator.hpp
index 01231d706b..115a096665 100644
--- a/include/mbgl/style/expression/boolean_operator.hpp
+++ b/include/mbgl/style/expression/boolean_operator.hpp
@@ -2,6 +2,7 @@
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/conversion.hpp>
+
#include <memory>
namespace mbgl {
@@ -20,6 +21,7 @@ public:
EvaluationResult evaluate(const EvaluationContext& params) const override;
void eachChild(const std::function<void(const Expression&)>& visit) const override;
bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
private:
std::vector<std::unique_ptr<Expression>> inputs;
@@ -36,8 +38,8 @@ public:
EvaluationResult evaluate(const EvaluationContext& params) const override;
void eachChild(const std::function<void(const Expression&)>& visit) const override;
-
bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
private:
std::vector<std::unique_ptr<Expression>> inputs;
@@ -46,4 +48,3 @@ private:
} // namespace expression
} // namespace style
} // namespace mbgl
-
diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp
index ece2fe0329..e61a55fc6d 100644
--- a/include/mbgl/style/expression/case.hpp
+++ b/include/mbgl/style/expression/case.hpp
@@ -26,6 +26,8 @@ public:
bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
private:
std::vector<Branch> branches;
std::unique_ptr<Expression> otherwise;
diff --git a/include/mbgl/style/expression/coalesce.hpp b/include/mbgl/style/expression/coalesce.hpp
index 4e6a9b3793..52d9498cbd 100644
--- a/include/mbgl/style/expression/coalesce.hpp
+++ b/include/mbgl/style/expression/coalesce.hpp
@@ -27,7 +27,9 @@ public:
void eachChild(const std::function<void(const Expression&)>& visit) const override;
bool operator==(const Expression& e) const override;
-
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
std::size_t getLength() const {
return args.size();
}
diff --git a/include/mbgl/style/expression/coercion.hpp b/include/mbgl/style/expression/coercion.hpp
index 665bb7ce7c..40d2490186 100644
--- a/include/mbgl/style/expression/coercion.hpp
+++ b/include/mbgl/style/expression/coercion.hpp
@@ -1,6 +1,8 @@
#pragma once
+
#include <mbgl/style/expression/expression.hpp>
#include <mbgl/style/conversion.hpp>
+
#include <memory>
#include <vector>
@@ -23,6 +25,9 @@ public:
void eachChild(const std::function<void(const Expression&)>& visit) const override;
bool operator==(const Expression& e) const override;
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
private:
EvaluationResult (*coerceSingleValue) (const Value& v);
std::vector<std::unique_ptr<Expression>> inputs;
diff --git a/include/mbgl/style/expression/compound_expression.hpp b/include/mbgl/style/expression/compound_expression.hpp
index fc3edbfd4a..8b74027578 100644
--- a/include/mbgl/style/expression/compound_expression.hpp
+++ b/include/mbgl/style/expression/compound_expression.hpp
@@ -72,7 +72,11 @@ public:
[&](const std::vector<type::Type>& p) -> optional<std::size_t> { return p.size(); }
);
}
-
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return { nullopt };
+ }
+
private:
std::string name;
variant<std::vector<type::Type>, VarargsType> params;
@@ -107,7 +111,7 @@ public:
}
return false;
}
-
+
private:
Signature signature;
typename Signature::Args args;
diff --git a/include/mbgl/style/expression/equals.hpp b/include/mbgl/style/expression/equals.hpp
index 3c0c024294..80550bd59d 100644
--- a/include/mbgl/style/expression/equals.hpp
+++ b/include/mbgl/style/expression/equals.hpp
@@ -19,6 +19,7 @@ public:
void eachChild(const std::function<void(const Expression&)>& visit) const override;
bool operator==(const Expression&) const override;
EvaluationResult evaluate(const EvaluationContext&) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
private:
std::unique_ptr<Expression> lhs;
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index a22fc28724..cf9fa0cb21 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -1,8 +1,5 @@
#pragma once
-#include <array>
-#include <vector>
-#include <memory>
#include <mbgl/util/optional.hpp>
#include <mbgl/util/variant.hpp>
#include <mbgl/util/color.hpp>
@@ -10,6 +7,10 @@
#include <mbgl/style/expression/value.hpp>
#include <mbgl/style/expression/parsing_context.hpp>
+#include <array>
+#include <vector>
+#include <memory>
+
namespace mbgl {
class GeometryTileFeature;
@@ -38,7 +39,7 @@ public:
optional<double> heatmapDensity;
};
-template<typename T>
+template <typename T>
class Result : private variant<EvaluationError, T> {
public:
using variant<EvaluationError, T>::variant;
@@ -128,6 +129,13 @@ public:
EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> heatmapDensity) const;
+ /**
+ * Statically analyze the expression, attempting to enumerate possible outputs. Returns
+ * an array of values plus the sentinel null optional value, used to indicate that the
+ * complete set of outputs is statically undecidable.
+ */
+ virtual std::vector<optional<Value>> possibleOutputs() const = 0;
+
protected:
template <typename T>
static bool childrenEqual(const T& lhs, const T& rhs) {
@@ -161,8 +169,6 @@ protected:
const std::pair<std::unique_ptr<Expression>, std::unique_ptr<Expression>>& rhs) {
return *(lhs.first) == *(rhs.first) && *(lhs.second) == *(rhs.second);
}
-
-
private:
type::Type type;
diff --git a/include/mbgl/style/expression/interpolate.hpp b/include/mbgl/style/expression/interpolate.hpp
index 439122f91c..9cc7c87957 100644
--- a/include/mbgl/style/expression/interpolate.hpp
+++ b/include/mbgl/style/expression/interpolate.hpp
@@ -89,6 +89,8 @@ public:
);
}
+ std::vector<optional<Value>> possibleOutputs() const override;
+
protected:
const Interpolator interpolator;
const std::unique_ptr<Expression> input;
diff --git a/include/mbgl/style/expression/let.hpp b/include/mbgl/style/expression/let.hpp
index aaa16ca0c2..6829ded9b8 100644
--- a/include/mbgl/style/expression/let.hpp
+++ b/include/mbgl/style/expression/let.hpp
@@ -33,6 +33,8 @@ public:
return false;
}
+ std::vector<optional<Value>> possibleOutputs() const override;
+
Expression* getResult() const {
return result.get();
}
@@ -61,7 +63,9 @@ public:
}
return false;
}
-
+
+ std::vector<optional<Value>> possibleOutputs() const override;
+
private:
std::string name;
std::shared_ptr<Expression> value;
diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp
index a0819c7e73..82983d78af 100644
--- a/include/mbgl/style/expression/literal.hpp
+++ b/include/mbgl/style/expression/literal.hpp
@@ -28,7 +28,11 @@ public:
}
return false;
}
-
+
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return {{ value }};
+ }
+
private:
Value value;
};
diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp
index e17fe96bfe..682d784b0f 100644
--- a/include/mbgl/style/expression/match.hpp
+++ b/include/mbgl/style/expression/match.hpp
@@ -25,14 +25,15 @@ public:
otherwise(std::move(otherwise_))
{}
+ EvaluationResult evaluate(const EvaluationContext& params) const override;
+
void eachChild(const std::function<void(const Expression&)>& visit) const override;
bool operator==(const Expression& e) const override;
- EvaluationResult evaluate(const EvaluationContext& params) const override;
-
+ std::vector<optional<Value>> possibleOutputs() const override;
+
private:
-
std::unique_ptr<Expression> input;
Branches branches;
std::unique_ptr<Expression> otherwise;
diff --git a/include/mbgl/style/expression/step.hpp b/include/mbgl/style/expression/step.hpp
index e3c49bc609..4a0a724d7c 100644
--- a/include/mbgl/style/expression/step.hpp
+++ b/include/mbgl/style/expression/step.hpp
@@ -33,6 +33,8 @@ public:
bool operator==(const Expression& e) const override;
+ std::vector<optional<Value>> possibleOutputs() const override;
+
static ParseResult parse(const mbgl::style::conversion::Convertible& value, ParsingContext& ctx);
private:
diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp
index 8baa9d2dba..be5be64752 100644
--- a/include/mbgl/style/expression/value.hpp
+++ b/include/mbgl/style/expression/value.hpp
@@ -148,6 +148,15 @@ struct ValueConverter<T, std::enable_if_t< std::is_enum<T>::value >> {
static optional<T> fromExpressionValue(const Value& value);
};
+template <typename T>
+std::vector<optional<T>> fromExpressionValues(const std::vector<optional<Value>>& values) {
+ std::vector<optional<T>> result;
+ for (const auto& value : values) {
+ result.push_back(value ? fromExpressionValue<T>(*value) : nullopt);
+ }
+ return result;
+}
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/include/mbgl/style/function/camera_function.hpp b/include/mbgl/style/function/camera_function.hpp
index 25b38e3616..015abd3e62 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -12,7 +12,6 @@
#include <mbgl/util/interpolate.hpp>
#include <mbgl/util/variant.hpp>
-
namespace mbgl {
namespace style {
@@ -66,7 +65,11 @@ public:
[&](auto z) { return z->getCoveringStops(lower, upper); }
);
}
-
+
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
+ }
+
friend bool operator==(const CameraFunction& lhs,
const CameraFunction& rhs) {
return *lhs.expression == *rhs.expression;
diff --git a/include/mbgl/style/function/composite_function.hpp b/include/mbgl/style/function/composite_function.hpp
index b44bf8e6fe..24578f599c 100644
--- a/include/mbgl/style/function/composite_function.hpp
+++ b/include/mbgl/style/function/composite_function.hpp
@@ -102,6 +102,10 @@ public:
);
}
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
+ }
+
friend bool operator==(const CompositeFunction& lhs,
const CompositeFunction& rhs) {
return *lhs.expression == *rhs.expression;
diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp
index 9f7b7ed1f8..8e544d3ad5 100644
--- a/include/mbgl/style/function/convert.hpp
+++ b/include/mbgl/style/function/convert.hpp
@@ -45,6 +45,10 @@ public:
return EvaluationError{message};
}
+ std::vector<optional<Value>> possibleOutputs() const override {
+ return {};
+ }
+
private:
std::string message;
};
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index 02e4b604e2..bd7b109fd8 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -57,6 +57,10 @@ public:
return defaultValue ? *defaultValue : finalDefaultValue;
}
+ std::vector<optional<T>> possibleOutputs() const {
+ return expression::fromExpressionValues<T>(expression->possibleOutputs());
+ }
+
friend bool operator==(const SourceFunction& lhs,
const SourceFunction& rhs) {
return *lhs.expression == *rhs.expression;
diff --git a/include/mbgl/style/layers/symbol_layer.hpp b/include/mbgl/style/layers/symbol_layer.hpp
index a72baa0b4e..f068e2d060 100644
--- a/include/mbgl/style/layers/symbol_layer.hpp
+++ b/include/mbgl/style/layers/symbol_layer.hpp
@@ -118,9 +118,9 @@ public:
DataDrivenPropertyValue<std::string> getTextField() const;
void setTextField(DataDrivenPropertyValue<std::string>);
- static PropertyValue<std::vector<std::string>> getDefaultTextFont();
- PropertyValue<std::vector<std::string>> getTextFont() const;
- void setTextFont(PropertyValue<std::vector<std::string>>);
+ static DataDrivenPropertyValue<std::vector<std::string>> getDefaultTextFont();
+ DataDrivenPropertyValue<std::vector<std::string>> getTextFont() const;
+ void setTextFont(DataDrivenPropertyValue<std::vector<std::string>>);
static DataDrivenPropertyValue<float> getDefaultTextSize();
DataDrivenPropertyValue<float> getTextSize() const;
diff --git a/include/mbgl/util/optional.hpp b/include/mbgl/util/optional.hpp
index a9374a1b53..abec02dca9 100644
--- a/include/mbgl/util/optional.hpp
+++ b/include/mbgl/util/optional.hpp
@@ -7,4 +7,7 @@ namespace mbgl {
template <typename T>
using optional = std::experimental::optional<T>;
+using nullopt_t = std::experimental::nullopt_t;
+constexpr nullopt_t nullopt = std::experimental::nullopt;
+
} // namespace mbgl
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 8247577bd4..67596fb428 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
@@ -2888,11 +2888,11 @@ public class PropertyFactory {
/**
* Font stack to use for displaying text.
*
- * @param <Z> the zoom parameter type
- * @param function a wrapper {@link CameraFunction} for String[]
+ * @param <T> the function input type
+ * @param function a wrapper function for String[]
* @return property wrapper around a String[] function
*/
- public static <Z extends Number> PropertyValue<CameraFunction<Z, String[]>> textFont(CameraFunction<Z, String[]> function) {
+ public static <T> PropertyValue<Function<T, String[]>> textFont(Function<T, String[]> function) {
return new LayoutPropertyValue<>("text-font", function);
}
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 f8248ae4a7..781307862d 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
@@ -1616,6 +1616,63 @@ public class SymbolLayerTest extends BaseActivityTest {
}
@Test
+ public void testTextFontAsIdentitySourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-font");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textFont(property("FeaturePropertyA", Stops.<String[]>identity()))
+ );
+
+ // Verify
+ assertNotNull(layer.getTextFont());
+ assertNotNull(layer.getTextFont().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextFont().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextFont().getFunction()).getProperty());
+ assertEquals(IdentityStops.class, layer.getTextFont().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
+ public void testTextFontAsIntervalSourceFunction() {
+ validateTestSetup();
+ setupLayer();
+ Timber.i("text-font");
+ invoke(mapboxMap, new MapboxMapAction.OnInvokeActionListener() {
+ @Override
+ public void onInvokeAction(UiController uiController, MapboxMap mapboxMap) {
+ assertNotNull(layer);
+
+ // Set
+ layer.setProperties(
+ textFont(
+ property(
+ "FeaturePropertyA",
+ interval(
+ stop(1, textFont(new String[]{"Open Sans Regular", "Arial Unicode MS Regular"}))
+ )
+ )
+ )
+ );
+
+ // Verify
+ assertNotNull(layer.getTextFont());
+ assertNotNull(layer.getTextFont().getFunction());
+ assertEquals(SourceFunction.class, layer.getTextFont().getFunction().getClass());
+ assertEquals("FeaturePropertyA", ((SourceFunction) layer.getTextFont().getFunction()).getProperty());
+ assertEquals(IntervalStops.class, layer.getTextFont().getFunction().getStops().getClass());
+ }
+ });
+ }
+
+ @Test
public void testTextSizeAsConstant() {
validateTestSetup();
setupLayer();
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.h b/platform/darwin/src/MGLSymbolStyleLayer.h
index f6d5323e22..84d32cd0b1 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.h
+++ b/platform/darwin/src/MGLSymbolStyleLayer.h
@@ -1054,8 +1054,18 @@ MGL_EXPORT
You can set this property to an instance of:
* `MGLConstantStyleValue`
- * `MGLCameraStyleFunction` with an interpolation mode of
- `MGLInterpolationModeInterval`
+ * `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<NSArray<NSString *> *> *textFontNames;
diff --git a/platform/darwin/src/MGLSymbolStyleLayer.mm b/platform/darwin/src/MGLSymbolStyleLayer.mm
index 1990c82669..3f206aac19 100644
--- a/platform/darwin/src/MGLSymbolStyleLayer.mm
+++ b/platform/darwin/src/MGLSymbolStyleLayer.mm
@@ -632,7 +632,7 @@ namespace mbgl {
- (void)setTextFontNames:(MGLStyleValue<NSArray<NSString *> *> *)textFontNames {
MGLAssertStyleLayerIsValid();
- auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toPropertyValue(textFontNames);
+ auto mbglValue = MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toDataDrivenPropertyValue(textFontNames);
self.rawLayer->setTextFont(mbglValue);
}
@@ -641,9 +641,9 @@ namespace mbgl {
auto propertyValue = self.rawLayer->getTextFont();
if (propertyValue.isUndefined()) {
- return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toStyleValue(self.rawLayer->getDefaultTextFont());
+ return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toDataDrivenStyleValue(self.rawLayer->getDefaultTextFont());
}
- return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toStyleValue(propertyValue);
+ return MGLStyleValueTransformer<std::vector<std::string>, NSArray<NSString *> *, std::string>().toDataDrivenStyleValue(propertyValue);
}
- (void)setTextFont:(MGLStyleValue<NSArray<NSString *> *> *)textFont {
diff --git a/platform/darwin/test/MGLSymbolStyleLayerTests.mm b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
index 1ac86dd402..60d674f2ed 100644
--- a/platform/darwin/test/MGLSymbolStyleLayerTests.mm
+++ b/platform/darwin/test/MGLSymbolStyleLayerTests.mm
@@ -1017,7 +1017,7 @@
MGLStyleValue<NSArray<NSString *> *> *constantStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithRawValue:@[@"Text Font", @"Tnof Txet"]];
layer.textFontNames = constantStyleValue;
- mbgl::style::PropertyValue<std::vector<std::string>> propertyValue = { { "Text Font", "Tnof Txet" } };
+ mbgl::style::DataDrivenPropertyValue<std::vector<std::string>> propertyValue = { { "Text Font", "Tnof Txet" } };
XCTAssertEqual(rawLayer->getTextFont(), propertyValue,
@"Setting textFontNames to a constant value should update text-font.");
XCTAssertEqualObjects(layer.textFontNames, constantStyleValue,
@@ -1041,11 +1041,6 @@
@"Unsetting textFontNames should return text-font to the default value.");
XCTAssertEqualObjects(layer.textFontNames, defaultStyleValue,
@"textFontNames should return the default value after being unset.");
-
- functionStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithInterpolationMode:MGLInterpolationModeIdentity sourceStops:nil attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textFontNames = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
- functionStyleValue = [MGLStyleValue<NSArray<NSString *> *> valueWithInterpolationMode:MGLInterpolationModeInterval compositeStops:@{@18: constantStyleValue} attributeName:@"" options:nil];
- XCTAssertThrowsSpecificNamed(layer.textFontNames = functionStyleValue, NSException, NSInvalidArgumentException, @"MGLStyleValue should raise an exception if it is applied to a property that cannot support it");
}
// text-size
diff --git a/platform/node/test/ignores.json b/platform/node/test/ignores.json
index 7cb3560f4d..0c287212a4 100644
--- a/platform/node/test/ignores.json
+++ b/platform/node/test/ignores.json
@@ -60,7 +60,6 @@
"render-tests/runtime-styling/set-style-paint-property-fill-flat-to-extrude": "https://github.com/mapbox/mapbox-gl-native/issues/6745",
"render-tests/symbol-placement/line-overscaled": "https://github.com/mapbox/mapbox-gl-js/issues/5654",
"render-tests/symbol-visibility/visible": "https://github.com/mapbox/mapbox-gl-native/issues/10409",
- "render-tests/text-font/data-expression": "https://github.com/mapbox/mapbox-gl-native/issues/10535",
"render-tests/text-pitch-alignment/auto-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
"render-tests/text-pitch-alignment/map-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
"render-tests/text-pitch-alignment/viewport-text-rotation-alignment-map": "https://github.com/mapbox/mapbox-gl-native/issues/9732",
diff --git a/src/mbgl/layout/symbol_layout.cpp b/src/mbgl/layout/symbol_layout.cpp
index fc1ede56ff..a41a98fcaf 100644
--- a/src/mbgl/layout/symbol_layout.cpp
+++ b/src/mbgl/layout/symbol_layout.cpp
@@ -32,7 +32,7 @@ using namespace style;
template <class Property>
static bool has(const style::SymbolLayoutProperties::PossiblyEvaluated& layout) {
return layout.get<Property>().match(
- [&] (const std::string& s) { return !s.empty(); },
+ [&] (const typename Property::Type& t) { return !t.empty(); },
[&] (const auto&) { return true; }
);
}
@@ -82,7 +82,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
layout.get<IconPitchAlignment>() = layout.get<IconRotationAlignment>();
}
- const bool hasText = has<TextField>(layout) && !layout.get<TextFont>().empty();
+ const bool hasText = has<TextField>(layout) && has<TextFont>(layout);
const bool hasIcon = has<IconImage>(layout);
if (!hasText && !hasIcon) {
@@ -143,12 +143,15 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
&& layout.get<SymbolPlacement>() == SymbolPlacementType::Line
&& util::i18n::allowsVerticalWritingMode(*ft.text);
+ FontStack fontStack = layout.evaluate<TextFont>(zoom, ft);
+ GlyphIDs& dependencies = glyphDependencies[fontStack];
+
// Loop through all characters of this text and collect unique codepoints.
for (char16_t chr : *ft.text) {
- glyphDependencies[layout.get<TextFont>()].insert(chr);
+ dependencies.insert(chr);
if (canVerticalizeText) {
if (char16_t verticalChr = util::i18n::verticalizePunctuation(chr)) {
- glyphDependencies[layout.get<TextFont>()].insert(verticalChr);
+ dependencies.insert(verticalChr);
}
}
}
@@ -183,18 +186,20 @@ void SymbolLayout::prepare(const GlyphMap& glyphMap, const GlyphPositions& glyph
const bool textAlongLine = layout.get<TextRotationAlignment>() == AlignmentType::Map &&
layout.get<SymbolPlacement>() == SymbolPlacementType::Line;
- auto glyphMapIt = glyphMap.find(layout.get<TextFont>());
- const Glyphs& glyphs = glyphMapIt != glyphMap.end()
- ? glyphMapIt->second : Glyphs();
-
- auto glyphPositionsIt = glyphPositions.find(layout.get<TextFont>());
- const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end()
- ? glyphPositionsIt->second : GlyphPositionMap();
-
for (auto it = features.begin(); it != features.end(); ++it) {
auto& feature = *it;
if (feature.geometry.empty()) continue;
+ FontStack fontStack = layout.evaluate<TextFont>(zoom, feature);
+
+ auto glyphMapIt = glyphMap.find(fontStack);
+ const Glyphs& glyphs = glyphMapIt != glyphMap.end()
+ ? glyphMapIt->second : Glyphs();
+
+ auto glyphPositionsIt = glyphPositions.find(fontStack);
+ const GlyphPositionMap& glyphPositionMap = glyphPositionsIt != glyphPositions.end()
+ ? glyphPositionsIt->second : GlyphPositionMap();
+
std::pair<Shaping, Shaping> shapedTextOrientations;
optional<PositionedIcon> shapedIcon;
diff --git a/src/mbgl/style/conversion/make_property_setters.hpp b/src/mbgl/style/conversion/make_property_setters.hpp
index 074d7eb730..18370df636 100644
--- a/src/mbgl/style/conversion/make_property_setters.hpp
+++ b/src/mbgl/style/conversion/make_property_setters.hpp
@@ -49,7 +49,7 @@ inline auto makeLayoutPropertySetters() {
result["text-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
result["text-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
result["text-field"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
- result["text-font"] = &setProperty<SymbolLayer, PropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
+ result["text-font"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
result["text-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
result["text-max-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
result["text-line-height"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setTextLineHeight>;
diff --git a/src/mbgl/style/expression/assertion.cpp b/src/mbgl/style/expression/assertion.cpp
index a17c53cf54..0187921af9 100644
--- a/src/mbgl/style/expression/assertion.cpp
+++ b/src/mbgl/style/expression/assertion.cpp
@@ -66,6 +66,16 @@ bool Assertion::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Assertion::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& input : inputs) {
+ for (auto& output : input->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/expression/boolean_operator.cpp b/src/mbgl/style/expression/boolean_operator.cpp
index 88797f965a..8d277450ba 100644
--- a/src/mbgl/style/expression/boolean_operator.cpp
+++ b/src/mbgl/style/expression/boolean_operator.cpp
@@ -26,6 +26,10 @@ bool Any::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Any::possibleOutputs() const {
+ return {{ true }, { false }};
+}
+
EvaluationResult All::evaluate(const EvaluationContext& params) const {
for (auto it = inputs.begin(); it != inputs.end(); it++) {
@@ -49,6 +53,10 @@ bool All::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> All::possibleOutputs() const {
+ return {{ true }, { false }};
+}
+
using namespace mbgl::style::conversion;
template <class T>
diff --git a/src/mbgl/style/expression/case.cpp b/src/mbgl/style/expression/case.cpp
index 049f258606..295e694189 100644
--- a/src/mbgl/style/expression/case.cpp
+++ b/src/mbgl/style/expression/case.cpp
@@ -34,6 +34,19 @@ bool Case::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Case::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& branch : branches) {
+ for (auto& output : branch.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ for (auto& output : otherwise->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ return result;
+}
+
using namespace mbgl::style::conversion;
ParseResult Case::parse(const Convertible& value, ParsingContext& ctx) {
assert(isArray(value));
diff --git a/src/mbgl/style/expression/coalesce.cpp b/src/mbgl/style/expression/coalesce.cpp
index 0373c9626c..872a9abbef 100644
--- a/src/mbgl/style/expression/coalesce.cpp
+++ b/src/mbgl/style/expression/coalesce.cpp
@@ -27,6 +27,16 @@ bool Coalesce::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Coalesce::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& arg : args) {
+ for (auto& output : arg->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
using namespace mbgl::style::conversion;
ParseResult Coalesce::parse(const Convertible& value, ParsingContext& ctx) {
assert(isArray(value));
diff --git a/src/mbgl/style/expression/coercion.cpp b/src/mbgl/style/expression/coercion.cpp
index 8ed8e160dd..56ab33fcfd 100644
--- a/src/mbgl/style/expression/coercion.cpp
+++ b/src/mbgl/style/expression/coercion.cpp
@@ -136,6 +136,16 @@ bool Coercion::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Coercion::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& input : inputs) {
+ for (auto& output : input->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/expression/equals.cpp b/src/mbgl/style/expression/equals.cpp
index 08ef85e92b..6d963cc1d8 100644
--- a/src/mbgl/style/expression/equals.cpp
+++ b/src/mbgl/style/expression/equals.cpp
@@ -37,6 +37,10 @@ bool Equals::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Equals::possibleOutputs() const {
+ return {{ true }, { false }};
+}
+
static bool isComparableType(const type::Type& type) {
return type == type::String ||
type == type::Number ||
diff --git a/src/mbgl/style/expression/interpolate.cpp b/src/mbgl/style/expression/interpolate.cpp
index 5ddfca8e9f..4cb22a3e4f 100644
--- a/src/mbgl/style/expression/interpolate.cpp
+++ b/src/mbgl/style/expression/interpolate.cpp
@@ -206,6 +206,16 @@ ParseResult parseInterpolate(const Convertible& value, ParsingContext& ctx) {
);
}
+std::vector<optional<Value>> InterpolateBase::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& stop : stops) {
+ for (auto& output : stop.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/expression/let.cpp b/src/mbgl/style/expression/let.cpp
index 5c08248eef..fe48138ac3 100644
--- a/src/mbgl/style/expression/let.cpp
+++ b/src/mbgl/style/expression/let.cpp
@@ -17,6 +17,10 @@ void Let::eachChild(const std::function<void(const Expression&)>& visit) const {
visit(*result);
}
+std::vector<optional<Value>> Let::possibleOutputs() const {
+ return result->possibleOutputs();
+}
+
using namespace mbgl::style::conversion;
ParseResult Let::parse(const Convertible& value, ParsingContext& ctx) {
@@ -67,6 +71,10 @@ EvaluationResult Var::evaluate(const EvaluationContext& params) const {
void Var::eachChild(const std::function<void(const Expression&)>&) const {}
+std::vector<optional<Value>> Var::possibleOutputs() const {
+ return { nullopt };
+}
+
ParseResult Var::parse(const Convertible& value_, ParsingContext& ctx) {
assert(isArray(value_));
diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp
index 35356747c9..0b2790b688 100644
--- a/src/mbgl/style/expression/match.cpp
+++ b/src/mbgl/style/expression/match.cpp
@@ -26,6 +26,20 @@ bool Match<T>::operator==(const Expression& e) const {
return false;
}
+template <typename T>
+std::vector<optional<Value>> Match<T>::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& branch : branches) {
+ for (auto& output : branch.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ for (auto& output : otherwise->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ return result;
+}
+
template<> EvaluationResult Match<std::string>::evaluate(const EvaluationContext& params) const {
const EvaluationResult inputValue = input->evaluate(params);
diff --git a/src/mbgl/style/expression/step.cpp b/src/mbgl/style/expression/step.cpp
index bfcb6fd270..1fb3bd73cc 100644
--- a/src/mbgl/style/expression/step.cpp
+++ b/src/mbgl/style/expression/step.cpp
@@ -40,6 +40,16 @@ bool Step::operator==(const Expression& e) const {
return false;
}
+std::vector<optional<Value>> Step::possibleOutputs() const {
+ std::vector<optional<Value>> result;
+ for (const auto& stop : stops) {
+ for (auto& output : stop.second->possibleOutputs()) {
+ result.push_back(std::move(output));
+ }
+ }
+ return result;
+}
+
Range<float> Step::getCoveringStops(const double lower, const double upper) const {
return ::mbgl::style::expression::getCoveringStops(stops, lower, upper);
}
diff --git a/src/mbgl/style/layers/symbol_layer.cpp b/src/mbgl/style/layers/symbol_layer.cpp
index 9a944657ca..d1a1ba246e 100644
--- a/src/mbgl/style/layers/symbol_layer.cpp
+++ b/src/mbgl/style/layers/symbol_layer.cpp
@@ -412,15 +412,15 @@ void SymbolLayer::setTextField(DataDrivenPropertyValue<std::string> value) {
baseImpl = std::move(impl_);
observer->onLayerChanged(*this);
}
-PropertyValue<std::vector<std::string>> SymbolLayer::getDefaultTextFont() {
+DataDrivenPropertyValue<std::vector<std::string>> SymbolLayer::getDefaultTextFont() {
return TextFont::defaultValue();
}
-PropertyValue<std::vector<std::string>> SymbolLayer::getTextFont() const {
+DataDrivenPropertyValue<std::vector<std::string>> SymbolLayer::getTextFont() const {
return impl().layout.get<TextFont>();
}
-void SymbolLayer::setTextFont(PropertyValue<std::vector<std::string>> value) {
+void SymbolLayer::setTextFont(DataDrivenPropertyValue<std::vector<std::string>> value) {
if (value == getTextFont())
return;
auto impl_ = mutableImpl();
diff --git a/src/mbgl/style/layers/symbol_layer_properties.hpp b/src/mbgl/style/layers/symbol_layer_properties.hpp
index 436b5cbd4f..e70ac28d59 100644
--- a/src/mbgl/style/layers/symbol_layer_properties.hpp
+++ b/src/mbgl/style/layers/symbol_layer_properties.hpp
@@ -112,7 +112,7 @@ struct TextField : DataDrivenLayoutProperty<std::string> {
static std::string defaultValue() { return ""; }
};
-struct TextFont : LayoutProperty<std::vector<std::string>> {
+struct TextFont : DataDrivenLayoutProperty<std::vector<std::string>> {
static constexpr const char * key = "text-font";
static std::vector<std::string> defaultValue() { return { "Open Sans Regular", "Arial Unicode MS Regular" }; }
};
diff --git a/src/mbgl/style/parser.cpp b/src/mbgl/style/parser.cpp
index 10fce33986..52d788b3a1 100644
--- a/src/mbgl/style/parser.cpp
+++ b/src/mbgl/style/parser.cpp
@@ -271,28 +271,29 @@ void Parser::parseLayer(const std::string& id, const JSValue& value, std::unique
}
std::vector<FontStack> Parser::fontStacks() const {
- std::set<FontStack> optional;
+ std::set<FontStack> result;
for (const auto& layer : layers) {
if (layer->is<SymbolLayer>()) {
- PropertyValue<FontStack> textFont = layer->as<SymbolLayer>()->getTextFont();
- if (textFont.isUndefined()) {
- optional.insert({"Open Sans Regular", "Arial Unicode MS Regular"});
- } else if (textFont.isConstant()) {
- optional.insert(textFont.asConstant());
- } else if (textFont.isCameraFunction()) {
- textFont.asCameraFunction().stops.match(
- [&] (const auto& stops) {
- for (const auto& stop : stops.stops) {
- optional.insert(stop.second);
+ layer->as<SymbolLayer>()->getTextFont().match(
+ [&] (Undefined) {
+ result.insert({"Open Sans Regular", "Arial Unicode MS Regular"});
+ },
+ [&] (const FontStack& constant) {
+ result.insert(constant);
+ },
+ [&] (const auto& function) {
+ for (const auto& value : function.possibleOutputs()) {
+ if (value) {
+ result.insert(*value);
}
}
- );
- }
+ }
+ );
}
}
- return std::vector<FontStack>(optional.begin(), optional.end());
+ return std::vector<FontStack>(result.begin(), result.end());
}
} // namespace style
diff --git a/test/style/style_parser.test.cpp b/test/style/style_parser.test.cpp
index 5fa81b47e9..7f7c06f4c5 100644
--- a/test/style/style_parser.test.cpp
+++ b/test/style/style_parser.test.cpp
@@ -102,3 +102,78 @@ TEST(StyleParser, FontStacks) {
ASSERT_EQ(FontStack({"a", "b"}), result[1]);
ASSERT_EQ(FontStack({"a", "b", "c"}), result[2]);
}
+
+TEST(StyleParser, FontStacksCaseExpression) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-font": ["case", ["==", "a", ["string", ["get", "text-font"]]], ["literal", ["Arial"]], ["literal", ["Helvetica"]]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(FontStack({"Arial"}), result[0]);
+ ASSERT_EQ(FontStack({"Helvetica"}), result[1]);
+}
+
+TEST(StyleParser, FontStacksMatchExpression) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-font": ["match", ["get", "text-font"], "a", ["literal", ["Arial"]], ["literal", ["Helvetica"]]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(FontStack({"Arial"}), result[0]);
+ ASSERT_EQ(FontStack({"Helvetica"}), result[1]);
+}
+
+TEST(StyleParser, FontStacksStepExpression) {
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-font": ["array", "string", ["step", ["get", "text-font"], ["literal", ["Arial"]], 0, ["literal", ["Helvetica"]]]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(2u, result.size());
+ ASSERT_EQ(FontStack({"Arial"}), result[0]);
+ ASSERT_EQ(FontStack({"Helvetica"}), result[1]);
+}
+
+TEST(StyleParser, FontStacksGetExpression) {
+ // Invalid style, but not currently validated.
+ style::Parser parser;
+ parser.parse(R"({
+ "version": 8,
+ "layers": [{
+ "id": "symbol",
+ "type": "symbol",
+ "source": "vector",
+ "layout": {
+ "text-font": ["array", "string", ["get", "text-font"]]
+ }
+ }]
+ })");
+ auto result = parser.fontStacks();
+ ASSERT_EQ(0u, result.size());
+}