summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnand Thakker <github@anandthakker.net>2017-07-28 16:32:25 -0400
committerAnand Thakker <github@anandthakker.net>2017-08-11 21:54:51 -0400
commit29a9f50287ba95463a829c096ef7b12fa5be80ee (patch)
tree8d5e34b9e3a533c55e1eb251a50c53d4aab512d5
parent0925a779e866b64899cdd6c7cf2dc042e865eac7 (diff)
downloadqtlocation-mapboxgl-29a9f50287ba95463a829c096ef7b12fa5be80ee.tar.gz
Implement Camera and Source functions using expressions
-rw-r--r--cmake/core-files.cmake4
-rw-r--r--include/mbgl/style/expression/array_assertion.hpp128
-rw-r--r--include/mbgl/style/expression/case.hpp174
-rw-r--r--include/mbgl/style/expression/curve.hpp2
-rw-r--r--include/mbgl/style/expression/expression.hpp79
-rw-r--r--include/mbgl/style/expression/literal.hpp79
-rw-r--r--include/mbgl/style/expression/match.hpp16
-rw-r--r--include/mbgl/style/expression/parse.hpp19
-rw-r--r--include/mbgl/style/expression/value.hpp27
-rw-r--r--include/mbgl/style/function/camera_function.hpp21
-rw-r--r--include/mbgl/style/function/convert.hpp217
-rw-r--r--include/mbgl/style/function/source_function.hpp26
-rw-r--r--include/mbgl/util/enum.hpp1
-rw-r--r--src/mbgl/style/expression/compound_expression.cpp17
-rw-r--r--src/mbgl/style/expression/match.cpp33
-rw-r--r--src/mbgl/style/expression/value.cpp222
-rw-r--r--src/mbgl/style/function/expression.cpp4
17 files changed, 926 insertions, 143 deletions
diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake
index 24d7fb7013..a8ece9f6cd 100644
--- a/cmake/core-files.cmake
+++ b/cmake/core-files.cmake
@@ -390,18 +390,21 @@ set(MBGL_CORE_FILES
src/mbgl/style/conversion/stringify.hpp
# style/expression
+ include/mbgl/style/expression/array_assertion.hpp
include/mbgl/style/expression/case.hpp
include/mbgl/style/expression/coalesce.hpp
include/mbgl/style/expression/compound_expression.hpp
include/mbgl/style/expression/curve.hpp
include/mbgl/style/expression/expression.hpp
include/mbgl/style/expression/let.hpp
+ include/mbgl/style/expression/literal.hpp
include/mbgl/style/expression/match.hpp
include/mbgl/style/expression/parse.hpp
include/mbgl/style/expression/parsing_context.hpp
include/mbgl/style/expression/type.hpp
include/mbgl/style/expression/value.hpp
src/mbgl/style/expression/compound_expression.cpp
+ src/mbgl/style/expression/match.cpp
src/mbgl/style/expression/type.cpp
src/mbgl/style/expression/value.cpp
@@ -412,6 +415,7 @@ set(MBGL_CORE_FILES
include/mbgl/style/function/composite_exponential_stops.hpp
include/mbgl/style/function/composite_function.hpp
include/mbgl/style/function/composite_interval_stops.hpp
+ include/mbgl/style/function/convert.hpp
include/mbgl/style/function/exponential_stops.hpp
include/mbgl/style/function/identity_stops.hpp
include/mbgl/style/function/interval_stops.hpp
diff --git a/include/mbgl/style/expression/array_assertion.hpp b/include/mbgl/style/expression/array_assertion.hpp
new file mode 100644
index 0000000000..710e16f00b
--- /dev/null
+++ b/include/mbgl/style/expression/array_assertion.hpp
@@ -0,0 +1,128 @@
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+
+class TypedArrayAssertion : public TypedExpression {
+public:
+ TypedArrayAssertion(type::Array type, std::unique_ptr<TypedExpression> input_) :
+ TypedExpression(type),
+ input(std::move(input_))
+ {}
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override {
+ auto result = input->evaluate(params);
+ if (!result) {
+ return result.error();
+ }
+ auto error = matchType(getType(), typeOf(*result));
+ if (error) {
+ return EvaluationError { *error };
+ }
+ return *result;
+ }
+private:
+ std::unique_ptr<TypedExpression> input;
+};
+
+class UntypedArrayAssertion : public UntypedExpression {
+public:
+ UntypedArrayAssertion(std::string key_,
+ type::Type itemType_,
+ optional<std::size_t> length_,
+ std::unique_ptr<UntypedExpression> input_
+ ) : UntypedExpression(key_),
+ itemType(itemType_),
+ length(length_),
+ input(std::move(input_))
+ {}
+
+ TypecheckResult typecheck(std::vector<CompileError>& errors) const override {
+ auto checkedInput = input->typecheck(errors);
+ if (!checkedInput) {
+ return TypecheckResult();
+ }
+ return TypecheckResult(std::make_unique<TypedArrayAssertion>(
+ type::Array(itemType, length), std::move(*checkedInput))
+ );
+ }
+
+ template <class V>
+ static ParseResult parse(const V& value, const ParsingContext& ctx) {
+ using namespace mbgl::style::conversion;
+
+ static std::unordered_map<std::string, type::Type> itemTypes {
+ {"string", type::String},
+ {"number", type::Number},
+ {"boolean", type::Boolean}
+ };
+
+ auto length = arrayLength(value);
+ if (length < 2 || length > 4) {
+ return CompileError {
+ "Expected 1, 2, or 3 arguments, but found " + std::to_string(length - 1) + " instead.",
+ ctx.key()
+ };
+ }
+
+ auto input = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, {length - 1}, {"array"}));
+ if (input.template is<CompileError>()) {
+ return input;
+ }
+
+ optional<type::Type> itemType;
+ optional<std::size_t> N;
+ if (length > 2) {
+ auto type = toString(arrayMember(value, 2));
+ if (!type || itemTypes.find(*type) == itemTypes.end()) {
+ return CompileError {
+ "The item type argument of \"array\" must be one of string, number, boolean",
+ ctx.key(2)
+ };
+ }
+ itemType = itemTypes.at(*type);
+ } else {
+ itemType = {type::Value};
+ }
+
+ if (length > 3) {
+ auto n = toNumber(arrayMember(value, 3));
+ if (!n || *n != ceilf(*n)) {
+ return CompileError {
+ "The length argument to \"array\" must be a positive integer literal.",
+ ctx.key(3)
+ };
+ }
+ N = optional<std::size_t>(*n);
+ }
+
+ return std::make_unique<UntypedArrayAssertion>(
+ ctx.key(),
+ *itemType,
+ N,
+ std::move(input.template get<std::unique_ptr<UntypedExpression>>())
+ );
+ }
+
+private:
+
+ type::Type itemType;
+ optional<std::size_t> length;
+ std::unique_ptr<UntypedExpression> input;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/case.hpp b/include/mbgl/style/expression/case.hpp
index e69de29bb2..005bb475c6 100644
--- a/include/mbgl/style/expression/case.hpp
+++ b/include/mbgl/style/expression/case.hpp
@@ -0,0 +1,174 @@
+#pragma once
+
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class TypedCase : public TypedExpression {
+public:
+ using Case = std::pair<std::unique_ptr<TypedExpression>, std::unique_ptr<TypedExpression>>;
+
+ TypedCase(std::vector<Case> cases_,
+ std::unique_ptr<TypedExpression> otherwise_
+ ) : TypedExpression(otherwise_->getType()),
+ cases(std::move(cases_)),
+ otherwise(std::move(otherwise_))
+ {}
+
+ bool isFeatureConstant() const override {
+ if (!otherwise->isFeatureConstant()) { return false; }
+ for (const auto& pair : cases) {
+ if (!pair.first->isFeatureConstant() || !pair.second->isFeatureConstant()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ bool isZoomConstant() const override {
+ if (!otherwise->isZoomConstant()) { return false; }
+ for (const auto& pair : cases) {
+ if (!pair.first->isFeatureConstant() || !pair.second->isZoomConstant()) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ EvaluationResult evaluate(const EvaluationParameters& params) const override {
+ for (const auto& casePair : cases) {
+ const auto& condition = casePair.first->template evaluate<bool>(params);
+ if (!condition) {
+ return condition.error();
+ }
+ if (*condition) {
+ return casePair.second->evaluate(params);
+ }
+ }
+
+ return otherwise->evaluate(params);
+ }
+
+private:
+ std::unique_ptr<TypedExpression> input;
+ std::vector<Case> cases;
+ std::unique_ptr<TypedExpression> otherwise;
+};
+
+class UntypedCase : public UntypedExpression {
+public:
+ using Cases = std::vector<std::pair<std::unique_ptr<UntypedExpression>, std::unique_ptr<UntypedExpression>>>;
+
+ UntypedCase(std::string key,
+ Cases cases_,
+ std::unique_ptr<UntypedExpression> otherwise_)
+ : UntypedExpression(key),
+ cases(std::move(cases_)),
+ otherwise(std::move(otherwise_))
+ {}
+
+ template <class V>
+ static ParseResult parse(const V& value, const ParsingContext& ctx) {
+ using namespace mbgl::style::conversion;
+
+ assert(isArray(value));
+ auto length = arrayLength(value);
+ if (length < 4) {
+ return CompileError {
+ "Expected at least 3 arguments, but found only " + std::to_string(length - 1) + ".",
+ ctx.key()
+ };
+ }
+
+ // Expect even-length array: ["case", 2 * (n pairs)..., otherwise]
+ if (length % 2 != 0) {
+ return CompileError {
+ "Missing final output value for \"case\" expression.",
+ ctx.key()
+ };
+ }
+
+ auto otherwise = parseExpression(arrayMember(value, length - 1), ParsingContext(ctx, {length - 1}, {"case"}));
+ if (otherwise.template is<CompileError>()) {
+ return otherwise;
+ }
+
+ Cases cases;
+ for (size_t i = 1; i + 1 < length; i += 2) {
+ auto condition = parseExpression(arrayMember(value, i), ParsingContext(ctx, {i}, {"case"}));
+ if (condition.template is<CompileError>()) {
+ return condition.template get<CompileError>();
+ }
+
+ auto output = parseExpression(arrayMember(value, i + 1), ParsingContext(ctx, {i + 1}, {"case"}));
+ if (output.template is<CompileError>()) {
+ return output.template get<CompileError>();
+ }
+
+ cases.push_back(std::make_pair(
+ std::move(condition.template get<std::unique_ptr<UntypedExpression>>()),
+ std::move(output.template get<std::unique_ptr<UntypedExpression>>()))
+ );
+ }
+
+ return std::make_unique<UntypedCase>(ctx.key(),
+ std::move(cases),
+ std::move(otherwise.template get<std::unique_ptr<UntypedExpression>>()));
+ }
+
+ TypecheckResult typecheck(std::vector<CompileError>& errors) const override {
+ auto checkedOtherwise = otherwise->typecheck(errors);
+ if (!checkedOtherwise) {
+ return TypecheckResult();
+ }
+
+ optional<type::Type> outputType;
+ std::vector<TypedCase::Case> checkedCases;
+ for (const auto& pair : cases) {
+ auto condition = pair.first->typecheck(errors);
+ if (!condition) continue;
+ auto error = matchType(type::Boolean, (*condition)->getType());
+ if (error) {
+ errors.push_back({ *error, pair.first->getKey() });
+ }
+
+ auto output = pair.second->typecheck(errors);
+ if (!output) continue;
+ if (!outputType) {
+ outputType = (*output)->getType();
+ } else {
+ error = matchType(*outputType, (*output)->getType());
+ if (error) {
+ errors.push_back({ *error, pair.second->getKey() });
+ }
+ }
+
+ if (errors.size() == 0) {
+ checkedCases.emplace_back(std::move(*condition), std::move(*output));
+ }
+ }
+
+ auto error = matchType(*outputType, (*checkedOtherwise)->getType());
+ if (error) {
+ errors.push_back({ *error, otherwise->getKey() });
+ }
+
+ if (errors.size() > 0) {
+ return TypecheckResult();
+ }
+
+ return TypecheckResult(std::make_unique<TypedCase>(std::move(checkedCases), std::move(*checkedOtherwise)));
+ };
+
+private:
+ Cases cases;
+ std::unique_ptr<UntypedExpression> otherwise;
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/curve.hpp b/include/mbgl/style/expression/curve.hpp
index 00ea15eb46..e0c9fae534 100644
--- a/include/mbgl/style/expression/curve.hpp
+++ b/include/mbgl/style/expression/curve.hpp
@@ -284,7 +284,7 @@ public:
checkedStops.emplace(stop.first, std::move(*checkedOutput));
}
- if (stops.size() > 0) return TypecheckResult();
+ if (errors.size() > 0) return TypecheckResult();
return interpolation.match(
[&](const StepInterpolation&) -> TypecheckResult {
diff --git a/include/mbgl/style/expression/expression.hpp b/include/mbgl/style/expression/expression.hpp
index 8b2c5c7e98..94f62054a8 100644
--- a/include/mbgl/style/expression/expression.hpp
+++ b/include/mbgl/style/expression/expression.hpp
@@ -24,8 +24,8 @@ struct EvaluationError {
};
struct EvaluationParameters {
- float zoom;
- const GeometryTileFeature& feature;
+ optional<float> zoom;
+ GeometryTileFeature const * feature = nullptr;
};
template<typename T>
@@ -90,22 +90,22 @@ public:
virtual EvaluationResult evaluate(const EvaluationParameters& params) const = 0;
/*
- Evaluate this expression to a particular value type T. (See expression/value.hpp for
- possible types T.)
+ Evaluate this expression to a particular type T.
+ (See expression/value.xpp for possible types T.)
*/
template <typename T>
Result<T> evaluate(const EvaluationParameters& params) {
const auto& result = evaluate(params);
if (!result) { return result.error(); }
- return result->match(
- [&] (const T& v) -> variant<EvaluationError, T> { return v; },
- [&] (const auto& v) -> variant<EvaluationError, T> {
- return EvaluationError {
- "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
- ", but found " + toString(typeOf(v)) + " instead."
- };
- }
- );
+ optional<T> converted = fromExpressionValue<T>(*result);
+ if (converted) {
+ return *converted;
+ } else {
+ return EvaluationError {
+ "Expected value to be of type " + toString(valueTypeToExpressionType<T>()) +
+ ", but found " + toString(typeOf(*result)) + " instead."
+ };
+ }
}
EvaluationResult evaluate(float z, const Feature& feature) const;
@@ -133,59 +133,6 @@ using ParseResult = variant<CompileError, std::unique_ptr<UntypedExpression>>;
template <class V>
ParseResult parseExpression(const V& value, const ParsingContext& context);
-class TypedLiteral : public TypedExpression {
-public:
- TypedLiteral(Value value_) : TypedExpression(typeOf(value_)), value(value_) {}
- EvaluationResult evaluate(const EvaluationParameters&) const override {
- return value;
- }
-private:
- Value value;
-};
-
-class UntypedLiteral : public UntypedExpression {
-public:
- UntypedLiteral(std::string key_, Value value_) : UntypedExpression(key_), value(value_) {}
-
- TypecheckResult typecheck(std::vector<CompileError>&) const override {
- return {std::make_unique<TypedLiteral>(value)};
- }
-
- template <class V>
- static ParseResult parse(const V& value, const ParsingContext& ctx) {
- const Value& parsedValue = parseValue(value);
- return std::make_unique<UntypedLiteral>(ctx.key(), parsedValue);
- }
-
-private:
- template <class V>
- static Value parseValue(const V& value) {
- using namespace mbgl::style::conversion;
- if (isUndefined(value)) return Null;
- if (isObject(value)) {
- std::unordered_map<std::string, Value> result;
- eachMember(value, [&] (const std::string& k, const V& v) -> optional<conversion::Error> {
- result.emplace(k, parseValue(v));
- return {};
- });
- return result;
- }
- if (isArray(value)) {
- std::vector<Value> result;
- const auto length = arrayLength(value);
- for(std::size_t i = 0; i < length; i++) {
- result.emplace_back(parseValue(arrayMember(value, i)));
- }
- return result;
- }
-
- optional<mbgl::Value> v = toValue(value);
- assert(v);
- return convertValue(*v);
- }
-
- Value value;
-};
} // namespace expression
} // namespace style
diff --git a/include/mbgl/style/expression/literal.hpp b/include/mbgl/style/expression/literal.hpp
new file mode 100644
index 0000000000..a7390c279c
--- /dev/null
+++ b/include/mbgl/style/expression/literal.hpp
@@ -0,0 +1,79 @@
+#pragma once
+
+#include <vector>
+#include <memory>
+#include <mbgl/util/optional.hpp>
+#include <mbgl/util/variant.hpp>
+#include <mbgl/util/color.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/expression/value.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
+#include <mbgl/style/conversion.hpp>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+class TypedLiteral : public TypedExpression {
+public:
+ TypedLiteral(Value value_) : TypedExpression(typeOf(value_)), value(value_) {}
+ EvaluationResult evaluate(const EvaluationParameters&) const override {
+ return value;
+ }
+private:
+ Value value;
+};
+
+class UntypedLiteral : public UntypedExpression {
+public:
+ UntypedLiteral(std::string key_, Value value_) : UntypedExpression(key_), value(value_) {}
+
+ TypecheckResult typecheck(std::vector<CompileError>&) const override {
+ return {std::make_unique<TypedLiteral>(value)};
+ }
+
+ Value getValue() const { return value; }
+
+ template <class V>
+ static ParseResult parse(const V& value, const ParsingContext& ctx) {
+ const Value& parsedValue = parseValue(value);
+ return std::make_unique<UntypedLiteral>(ctx.key(), parsedValue);
+ }
+
+private:
+ template <class V>
+ static Value parseValue(const V& value) {
+ using namespace mbgl::style::conversion;
+ if (isUndefined(value)) return Null;
+ if (isObject(value)) {
+ std::unordered_map<std::string, Value> result;
+ eachMember(value, [&] (const std::string& k, const V& v) -> optional<conversion::Error> {
+ result.emplace(k, parseValue(v));
+ return {};
+ });
+ return result;
+ }
+ if (isArray(value)) {
+ std::vector<Value> result;
+ const auto length = arrayLength(value);
+ for(std::size_t i = 0; i < length; i++) {
+ result.emplace_back(parseValue(arrayMember(value, i)));
+ }
+ return result;
+ }
+
+ optional<mbgl::Value> v = toValue(value);
+ assert(v);
+
+ return Value(toExpressionValue(*v));
+ }
+
+ Value value;
+};
+
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/expression/match.hpp b/include/mbgl/style/expression/match.hpp
index 8bd2e1d238..bd0096e044 100644
--- a/include/mbgl/style/expression/match.hpp
+++ b/include/mbgl/style/expression/match.hpp
@@ -55,7 +55,7 @@ public:
}
EvaluationResult evaluate(const EvaluationParameters& params) const override {
- const auto& inputValue = input->evaluate<T>(params);
+ const auto& inputValue = evaluateInput(params);
if (!inputValue) {
return inputValue.error();
}
@@ -66,23 +66,13 @@ public:
}
private:
+ Result<T> evaluateInput(const EvaluationParameters& params) const;
+
std::unique_ptr<TypedExpression> input;
std::unordered_map<T, std::shared_ptr<TypedExpression>> cases;
std::unique_ptr<TypedExpression> otherwise;
};
-template <> EvaluationResult TypedMatch<int64_t>::evaluate(const EvaluationParameters& params) const {
- const auto& inputValue = input->evaluate<float>(params);
- if (!inputValue) {
- return inputValue.error();
- }
- int64_t rounded = ceilf(*inputValue);
- if (*inputValue != rounded || cases.find(rounded) == cases.end()) {
- return otherwise->evaluate(params);
- }
- return cases.at(rounded)->evaluate(params);
-}
-
class UntypedMatch : public UntypedExpression {
public:
using Cases = std::vector<std::pair<MatchKey, std::unique_ptr<UntypedExpression>>>;
diff --git a/include/mbgl/style/expression/parse.hpp b/include/mbgl/style/expression/parse.hpp
index 97ad880ae5..89d4354d76 100644
--- a/include/mbgl/style/expression/parse.hpp
+++ b/include/mbgl/style/expression/parse.hpp
@@ -1,13 +1,16 @@
#pragma once
#include <memory>
-#include <mbgl/style/expression/parsing_context.hpp>
-#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
#include <mbgl/style/expression/compound_expression.hpp>
-#include <mbgl/style/expression/match.hpp>
#include <mbgl/style/expression/curve.hpp>
-#include <mbgl/style/expression/coalesce.hpp>
-#include <mbgl/style/conversion.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+#include <mbgl/style/expression/parsing_context.hpp>
namespace mbgl {
namespace style {
@@ -24,7 +27,7 @@ std::string getJSType(const V& value) {
if (isArray(value) || isObject(value)) {
return "object";
}
- optional<mbgl::Value> v = toValue(value);
+ optional<mbgl::Value> v = conversion::toValue(value);
assert(v);
return v->match(
[&] (std::string) { return "string"; },
@@ -74,6 +77,10 @@ ParseResult parseExpression(const V& value, const ParsingContext& context)
return UntypedCurve::parse(value, context);
} else if (*op == "coalesce") {
return UntypedCoalesce::parse(value, context);
+ } else if (*op == "case") {
+ return UntypedCase::parse(value, context);
+ } else if (*op == "array") {
+ return UntypedArrayAssertion::parse(value, context);
}
return UntypedCompoundExpression::parse(value, context);
diff --git a/include/mbgl/style/expression/value.hpp b/include/mbgl/style/expression/value.hpp
index ffc31856e0..8a9cafcec1 100644
--- a/include/mbgl/style/expression/value.hpp
+++ b/include/mbgl/style/expression/value.hpp
@@ -1,9 +1,13 @@
#pragma once
+#include <array>
+#include <mbgl/style/expression/type.hpp>
+#include <mbgl/style/position.hpp>
+#include <mbgl/style/types.hpp>
#include <mbgl/util/color.hpp>
-#include <mbgl/util/variant.hpp>
+#include <mbgl/util/enum.hpp>
#include <mbgl/util/feature.hpp>
-#include <mbgl/style/expression/type.hpp>
+#include <mbgl/util/variant.hpp>
namespace mbgl {
@@ -11,6 +15,7 @@ namespace style {
namespace expression {
struct Value;
+
using ValueBase = variant<
NullValue,
bool,
@@ -24,9 +29,24 @@ struct Value : ValueBase {
using ValueBase::ValueBase;
};
+Value toExpressionValue(const Value&);
+
+template <typename T, typename Enable = std::enable_if_t< !std::is_convertible<T, Value>::value >>
+Value toExpressionValue(const T& value);
+
+template <typename T>
+std::enable_if_t< !std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v);
+
+template <typename T>
+std::enable_if_t< std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return v.template is<T>() ? v.template get<T>() : optional<T>();
+}
+
constexpr NullValue Null = NullValue();
-Value convertValue(const mbgl::Value&);
type::Type typeOf(const Value& value);
std::string stringify(const Value& value);
@@ -38,6 +58,7 @@ std::string stringify(const Value& value);
template <typename T>
type::Type valueTypeToExpressionType();
+
} // 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 7fde365b3d..3d33c814cb 100644
--- a/include/mbgl/style/function/camera_function.hpp
+++ b/include/mbgl/style/function/camera_function.hpp
@@ -1,5 +1,6 @@
#pragma once
+#include <mbgl/style/function/convert.hpp>
#include <mbgl/style/function/exponential_stops.hpp>
#include <mbgl/style/function/interval_stops.hpp>
#include <mbgl/util/interpolate.hpp>
@@ -20,13 +21,16 @@ public:
IntervalStops<T>>>;
CameraFunction(Stops stops_)
- : stops(std::move(stops_)) {
- }
+ : stops(std::move(stops_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(s);
+ }))
+ {}
T evaluate(float zoom) const {
- return stops.match([&] (const auto& s) {
- return s.evaluate(zoom).value_or(T());
- });
+ auto result = expression->evaluate<T>(expression::EvaluationParameters { zoom });
+ if (!result) return T();
+ return *result;
}
friend bool operator==(const CameraFunction& lhs,
@@ -34,8 +38,13 @@ public:
return lhs.stops == rhs.stops;
}
- Stops stops;
bool useIntegerZoom = false;
+
+ // retained for compatibility with pre-expression function API
+ Stops stops;
+
+private:
+ std::shared_ptr<expression::TypedExpression> expression;
};
} // namespace style
diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp
new file mode 100644
index 0000000000..ae439faa54
--- /dev/null
+++ b/include/mbgl/style/function/convert.hpp
@@ -0,0 +1,217 @@
+#pragma once
+
+#include <mbgl/util/enum.hpp>
+#include <mbgl/style/types.hpp>
+#include <mbgl/style/expression/array_assertion.hpp>
+#include <mbgl/style/expression/case.hpp>
+#include <mbgl/style/expression/coalesce.hpp>
+#include <mbgl/style/expression/compound_expression.hpp>
+#include <mbgl/style/expression/curve.hpp>
+#include <mbgl/style/expression/expression.hpp>
+#include <mbgl/style/expression/literal.hpp>
+#include <mbgl/style/expression/match.hpp>
+
+#include <mbgl/style/function/exponential_stops.hpp>
+#include <mbgl/style/function/interval_stops.hpp>
+#include <mbgl/style/function/categorical_stops.hpp>
+#include <mbgl/style/function/identity_stops.hpp>
+
+#include <string>
+
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+// Create expressions representing 'classic' (i.e. stop-based) style functions
+
+struct Convert {
+ // TODO: Organize. Where should each of these actually go?
+
+ template <typename T>
+ static std::unique_ptr<UntypedLiteral> makeLiteral(const T& value) {
+ return std::make_unique<UntypedLiteral>("", Value(toExpressionValue(value)));
+ }
+
+ static std::unique_ptr<UntypedExpression> makeGet(const std::string& type, const std::string& property) {
+ UntypedCompoundExpression::Args getArgs;
+ getArgs.push_back(makeLiteral(property));
+ auto get = std::make_unique<UntypedCompoundExpression>("", "get", std::move(getArgs));
+ UntypedCompoundExpression::Args assertionArgs;
+ assertionArgs.push_back(std::move(get));
+ return std::make_unique<UntypedCompoundExpression>("", type, std::move(assertionArgs));
+ }
+
+ static std::unique_ptr<UntypedExpression> makeZoom() {
+ return std::make_unique<UntypedCompoundExpression>("", "zoom", UntypedCompoundExpression::Args());
+ }
+
+ template <typename T>
+ static std::unique_ptr<UntypedExpression> makeCoalesceToDefault(std::unique_ptr<UntypedExpression> main, optional<T> defaultValue) {
+ if (!defaultValue) {
+ return main;
+ }
+
+ UntypedCoalesce::Args args;
+ args.push_back(std::move(main));
+ args.push_back(makeLiteral(*defaultValue));
+ return(std::make_unique<UntypedCoalesce>("", std::move(args)));
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> makeCurve(std::unique_ptr<UntypedExpression> input,
+ UntypedCurve::Interpolation interpolation,
+ const std::map<float, T>& stops,
+ optional<T> defaultValue)
+ {
+ UntypedCurve::Stops convertedStops;
+ for(const auto& stop : stops) {
+ convertedStops.push_back(std::make_pair(
+ stop.first,
+ makeLiteral(stop.second)
+ ));
+ }
+
+ std::unique_ptr<UntypedExpression> curve = std::make_unique<UntypedCurve>("", std::move(input), std::move(convertedStops), interpolation);
+
+ std::vector<CompileError> errors;
+ auto checked = makeCoalesceToDefault(std::move(curve), defaultValue)->typecheck(errors);
+ assert(checked);
+ return std::move(*checked);
+ }
+
+ template <typename Key, typename T>
+ static std::unique_ptr<UntypedExpression> makeMatch(std::unique_ptr<UntypedExpression> input,
+ const CategoricalStops<T>& stops) {
+ // match expression
+ UntypedMatch::Cases cases;
+ optional<type::Type> inputType;
+ for(const auto& stop : stops.stops) {
+ if (!inputType) {
+ inputType = stop.first.template is<std::string>() ?
+ type::Type(type::String) : type::Type(type::Number);
+ }
+ assert(stop.first.template is<Key>());
+ auto key = stop.first.template get<Key>();
+ cases.push_back(std::make_pair(
+ std::move(key),
+ makeLiteral(stop.second)
+ ));
+ }
+
+ return std::make_unique<UntypedMatch>("",
+ std::move(input),
+ std::move(cases),
+ makeLiteral(Null),
+ *inputType);
+ }
+
+ template <typename T>
+ static std::unique_ptr<UntypedExpression> makeCase(std::unique_ptr<UntypedExpression> input,
+ const CategoricalStops<T>& stops) {
+ // case expression
+ UntypedCase::Cases cases;
+ auto true_case = stops.stops.find(true) == stops.stops.end() ?
+ makeLiteral(Null) :
+ makeLiteral(stops.stops.at(true));
+ auto false_case = stops.stops.find(false) == stops.stops.end() ?
+ makeLiteral(Null) :
+ makeLiteral(stops.stops.at(false));
+ cases.push_back(std::make_pair(std::move(input), std::move(true_case)));
+ return std::make_unique<UntypedCase>("", std::move(cases), std::move(false_case));
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> toExpression(const ExponentialStops<T>& stops)
+ {
+ return makeCurve(makeZoom(), ExponentialInterpolation{stops.base}, stops.stops, {});
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> toExpression(const IntervalStops<T>& stops)
+ {
+ return makeCurve(makeZoom(), StepInterpolation(), stops.stops, {});
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> toExpression(const std::string& property,
+ const ExponentialStops<T>& stops,
+ optional<T> defaultValue)
+ {
+ return makeCurve(makeGet("number", property), ExponentialInterpolation{stops.base}, stops.stops, defaultValue);
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> toExpression(const std::string& property,
+ const IntervalStops<T>& stops,
+ optional<T> defaultValue)
+ {
+ return makeCurve(makeGet("number", property), StepInterpolation(), stops.stops, defaultValue);
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> toExpression(const std::string& property,
+ const CategoricalStops<T>& stops,
+ optional<T> defaultValue)
+ {
+ assert(stops.stops.size() > 0);
+
+ const auto& firstKey = stops.stops.begin()->first;
+ auto expr = firstKey.match(
+ [&](bool) {
+ auto input = makeGet("boolean", property);
+ return makeCase(std::move(input), stops);
+ },
+ [&](const std::string&) {
+ auto input = makeGet("string", property);
+ return makeMatch<std::string>(std::move(input), stops);
+ },
+ [&](int64_t) {
+ auto input = makeGet("number", property);
+ return makeMatch<int64_t>(std::move(input), stops);
+ }
+
+ );
+
+ std::vector<CompileError> errors;
+ auto checked = makeCoalesceToDefault(std::move(expr), defaultValue)->typecheck(errors);
+ assert(checked);
+ return std::move(*checked);
+ }
+
+ template <typename T>
+ static std::unique_ptr<TypedExpression> toExpression(const std::string& property,
+ const IdentityStops<T>&,
+ optional<T> defaultValue)
+ {
+ auto input = valueTypeToExpressionType<T>().match(
+ [&] (const type::StringType&) {
+ return makeGet("string", property);
+ },
+ [&] (const type::NumberType&) {
+ return makeGet("number", property);
+ },
+ [&] (const type::BooleanType&) {
+ return makeGet("boolean", property);
+ },
+ [&] (const type::Array& arr) {
+ UntypedCompoundExpression::Args getArgs;
+ getArgs.push_back(makeLiteral(property));
+ auto get = std::make_unique<UntypedCompoundExpression>("", "get", std::move(getArgs));
+ return std::make_unique<UntypedArrayAssertion>("", arr.itemType, arr.N, std::move(get));
+ },
+ [&] (const auto&) -> std::unique_ptr<UntypedExpression> {
+ return makeLiteral(Null);
+ }
+ );
+
+ std::vector<CompileError> errors;
+ auto checked = makeCoalesceToDefault(std::move(input), defaultValue)->typecheck(errors);
+ assert(checked);
+ return std::move(*checked);
+ }
+};
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/include/mbgl/style/function/source_function.hpp b/include/mbgl/style/function/source_function.hpp
index 9c2ad101ec..a25fda0a91 100644
--- a/include/mbgl/style/function/source_function.hpp
+++ b/include/mbgl/style/function/source_function.hpp
@@ -1,5 +1,7 @@
#pragma once
+#include <mbgl/style/function/convert.hpp>
+
#include <mbgl/style/function/exponential_stops.hpp>
#include <mbgl/style/function/interval_stops.hpp>
#include <mbgl/style/function/categorical_stops.hpp>
@@ -30,18 +32,19 @@ public:
SourceFunction(std::string property_, Stops stops_, optional<T> defaultValue_ = {})
: property(std::move(property_)),
stops(std::move(stops_)),
- defaultValue(std::move(defaultValue_)) {
- }
+ defaultValue(std::move(defaultValue_)),
+ expression(stops.match([&] (const auto& s) {
+ return expression::Convert::toExpression(property, s, defaultValue);
+ }))
+ {}
template <class Feature>
T evaluate(const Feature& feature, T finalDefaultValue) const {
- optional<Value> v = feature.getValue(property);
- if (!v) {
- return defaultValue.value_or(finalDefaultValue);
+ auto result = expression->evaluate<T>(expression::EvaluationParameters { optional<float>(), &feature });
+ if (!result) {
+ return finalDefaultValue;
}
- return stops.match([&] (const auto& s) -> T {
- return s.evaluate(*v).value_or(defaultValue.value_or(finalDefaultValue));
- });
+ return *result;
}
friend bool operator==(const SourceFunction& lhs,
@@ -50,10 +53,15 @@ public:
== std::tie(rhs.property, rhs.stops, rhs.defaultValue);
}
+ bool useIntegerZoom = false;
+
+ // retained for compatibility with pre-expression function API
std::string property;
Stops stops;
optional<T> defaultValue;
- bool useIntegerZoom = false;
+
+private:
+ std::shared_ptr<expression::TypedExpression> expression;
};
} // namespace style
diff --git a/include/mbgl/util/enum.hpp b/include/mbgl/util/enum.hpp
index 369ca86bfd..608befd3c4 100644
--- a/include/mbgl/util/enum.hpp
+++ b/include/mbgl/util/enum.hpp
@@ -11,6 +11,7 @@ namespace mbgl {
template <typename T>
class Enum {
public:
+ using Type = T;
static const char * toString(T);
static optional<T> toEnum(const std::string&);
};
diff --git a/src/mbgl/style/expression/compound_expression.cpp b/src/mbgl/style/expression/compound_expression.cpp
index 6767842b9a..9134b769f9 100644
--- a/src/mbgl/style/expression/compound_expression.cpp
+++ b/src/mbgl/style/expression/compound_expression.cpp
@@ -71,13 +71,16 @@ static Definition defineGet() {
definition.push_back(
std::make_unique<Signature<Result<Value> (const EvaluationParameters&, const std::string&)>>(
[](const EvaluationParameters& params, const std::string& key) -> Result<Value> {
- const auto propertyValue = params.feature.getValue(key);
+ optional<mbgl::Value> propertyValue;
+ if (params.feature) {
+ propertyValue = params.feature->getValue(key);
+ }
if (!propertyValue) {
return EvaluationError {
"Property '" + key + "' not found in feature.properties"
};
}
- return convertValue(*propertyValue);
+ return Value(toExpressionValue(*propertyValue));
},
false
)
@@ -117,7 +120,6 @@ std::unordered_map<std::string, CompoundExpression::Definition> CompoundExpressi
define("number", assertion<float>),
define("string", assertion<std::string>),
define("boolean", assertion<bool>),
- define("array", assertion<std::vector<Value>>), // TODO: [array, type, value], [array, type, length, value]
define("to_string", [](const Value& v) -> Result<std::string> { return stringify(v); }),
define("to_number", [](const Value& v) -> Result<float> {
@@ -160,6 +162,15 @@ std::unordered_map<std::string, CompoundExpression::Definition> CompoundExpressi
};
}),
+ define("zoom", [](const EvaluationParameters& params) -> Result<float> {
+ if (!params.zoom) {
+ return EvaluationError {
+ "The 'zoom' expression is unavailable in the current evaluation context."
+ };
+ }
+ return *(params.zoom);
+ }),
+
std::pair<std::string, Definition>("get", defineGet()),
define("+", [](const Varargs<float>& args) -> Result<float> {
diff --git a/src/mbgl/style/expression/match.cpp b/src/mbgl/style/expression/match.cpp
new file mode 100644
index 0000000000..9286e96d50
--- /dev/null
+++ b/src/mbgl/style/expression/match.cpp
@@ -0,0 +1,33 @@
+#include <mbgl/style/expression/match.hpp>
+
+namespace mbgl {
+namespace style {
+namespace expression {
+
+template<> Result<std::string> TypedMatch<std::string>::evaluateInput(const EvaluationParameters& params) const {
+ const auto& inputValue = input->evaluate<std::string>(params);
+ if (!inputValue) {
+ return inputValue.error();
+ }
+ return *inputValue;
+}
+
+template<> Result<int64_t> TypedMatch<int64_t>::evaluateInput(const EvaluationParameters& params) const {
+ const auto& inputValue = input->evaluate<float>(params);
+ if (!inputValue) {
+ return inputValue.error();
+ }
+ int64_t rounded = ceilf(*inputValue);
+ if (*inputValue == rounded) {
+ return rounded;
+ } else {
+ return EvaluationError {
+ "Input to \"match\" must be an integer value; found " +
+ std::to_string(*inputValue) + " instead ."
+ };
+ }
+}
+
+} // namespace expression
+} // namespace style
+} // namespace mbgl
diff --git a/src/mbgl/style/expression/value.cpp b/src/mbgl/style/expression/value.cpp
index 4e670ec5dd..324fc06f17 100644
--- a/src/mbgl/style/expression/value.cpp
+++ b/src/mbgl/style/expression/value.cpp
@@ -4,38 +4,6 @@ namespace mbgl {
namespace style {
namespace expression {
-struct ConvertValue {
-// null_value_t, bool, uint64_t, int64_t, double, std::string,
-// mapbox::util::recursive_wrapper<std::vector<value>>,
-// mapbox::util::recursive_wrapper<std::unordered_map<std::string, value>>
- Value operator()(const std::vector<mbgl::Value>& v) {
- std::vector<Value> result;
- for(const auto& item : v) {
- result.emplace_back(convertValue(item));
- }
- return result;
- }
-
- Value operator()(const std::unordered_map<std::string, mbgl::Value>& v) {
- std::unordered_map<std::string, Value> result;
- for(const auto& entry : v) {
- result.emplace(entry.first, convertValue(entry.second));
- }
- return result;
- }
-
- Value operator()(const std::string& s) { return s; }
- Value operator()(const bool& b) { return b; }
- Value operator()(const mbgl::NullValue) { return Null; }
-
- template <typename T>
- Value operator()(const T& v) { return *numericValue<float>(v); }
-};
-
-Value convertValue(const mbgl::Value& value) {
- return mbgl::Value::visit(value, ConvertValue());
-}
-
type::Type typeOf(const Value& value) {
return value.match(
[&](bool) -> type::Type { return type::Boolean; },
@@ -92,6 +60,165 @@ std::string stringify(const Value& value) {
);
}
+
+template <class T, class Enable = void>
+struct Converter {
+ static Value toExpressionValue(const T& value) {
+ return Value(value);
+ }
+ static optional<T> fromExpressionValue(const Value& value) {
+ return value.template is<T>() ? value.template get<T>() : optional<T>();
+ }
+};
+
+template<>
+struct Converter<mbgl::Value> {
+ static Value toExpressionValue(const mbgl::Value& value) {
+ return mbgl::Value::visit(value, Converter<mbgl::Value>());
+ }
+
+
+ // Double duty as a variant visitor for mbgl::Value:
+ Value operator()(const std::vector<mbgl::Value>& v) {
+ std::vector<Value> result;
+ for(const auto& item : v) {
+ result.emplace_back(toExpressionValue(item));
+ }
+ return result;
+ }
+
+ Value operator()(const std::unordered_map<std::string, mbgl::Value>& v) {
+ std::unordered_map<std::string, Value> result;
+ for(const auto& entry : v) {
+ result.emplace(entry.first, toExpressionValue(entry.second));
+ }
+ return result;
+ }
+
+ Value operator()(const std::string& s) { return s; }
+ Value operator()(const bool& b) { return b; }
+ Value operator()(const mbgl::NullValue) { return Null; }
+
+ template <typename T>
+ Value operator()(const T& v) { return *numericValue<float>(v); }
+};
+
+template <typename T, std::size_t N>
+struct Converter<std::array<T, N>> {
+ static Value toExpressionValue(const std::array<T, N>& value) {
+ std::vector<Value> result;
+ std::copy_n(value.begin(), N, result.begin());
+ return result;
+ }
+
+ static optional<std::array<T, N>> fromExpressionValue(const Value& v) {
+ return v.match(
+ [&] (const std::vector<Value>& v) -> optional<std::array<T, N>> {
+ if (v.size() != N) return optional<std::array<T, N>>();
+ std::array<T, N> result;
+ auto it = result.begin();
+ for(const auto& item : v) {
+ if (!item.template is<T>()) {
+ return optional<std::array<T, N>>();
+ }
+ *it = item.template get<T>();
+ it = std::next(it);
+ }
+ return result;
+ },
+ [&] (const auto&) { return optional<std::array<T, N>>(); }
+ );
+ }
+
+ static type::Type expressionType() {
+ return type::Array(valueTypeToExpressionType<T>(), N);
+ }
+};
+
+template <typename T>
+struct Converter<std::vector<T>> {
+ static Value toExpressionValue(const std::vector<T>& value) {
+ std::vector<Value> v;
+ std::copy(value.begin(), value.end(), v.begin());
+ return v;
+ }
+
+ static optional<std::vector<T>> fromExpressionValue(const Value& v) {
+ return v.match(
+ [&] (const std::vector<Value>& v) -> optional<std::vector<T>> {
+ std::vector<T> result;
+ for(const auto& item : v) {
+ if (!item.template is<T>()) {
+ return optional<std::vector<T>>();
+ }
+ result.push_back(item.template get<T>());
+ }
+ return result;
+ },
+ [&] (const auto&) { return optional<std::vector<T>>(); }
+ );
+ }
+
+ static type::Type expressionType() {
+ return type::Array(valueTypeToExpressionType<T>());
+ }
+};
+
+template <>
+struct Converter<Position> {
+ static Value toExpressionValue(const mbgl::style::Position& value) {
+ return Converter<std::array<float, 3>>::toExpressionValue(value.getSpherical());
+ }
+
+ static optional<Position> fromExpressionValue(const Value& v) {
+ auto pos = Converter<std::array<float, 3>>::fromExpressionValue(v);
+ return pos ? optional<Position>(Position(*pos)) : optional<Position>();
+ }
+
+ static type::Type expressionType() {
+ return type::Array(type::Number, 3);
+ }
+};
+
+template <typename T>
+struct Converter<T, std::enable_if_t< std::is_enum<T>::value >> {
+ static Value toExpressionValue(const T& value) {
+ return std::string(Enum<T>::toString(value));
+ }
+
+ static optional<T> fromExpressionValue(const Value& v) {
+ return v.match(
+ [&] (const std::string& v) { return Enum<T>::toEnum(v); },
+ [&] (const auto&) { return optional<T>(); }
+ );
+ }
+
+ static type::Type expressionType() {
+ return type::String;
+ }
+};
+
+Value toExpressionValue(const Value& v) {
+ return v;
+}
+
+template <typename T, typename Enable>
+Value toExpressionValue(const T& value) {
+ return Converter<T>::toExpressionValue(value);
+}
+
+template <typename T>
+std::enable_if_t< !std::is_convertible<T, Value>::value,
+optional<T>> fromExpressionValue(const Value& v)
+{
+ return Converter<T>::fromExpressionValue(v);
+}
+
+template <typename T>
+type::Type valueTypeToExpressionType() {
+ return Converter<T>::expressionType();
+}
+
template <> type::Type valueTypeToExpressionType<Value>() { return type::Value; }
template <> type::Type valueTypeToExpressionType<NullValue>() { return type::Null; }
template <> type::Type valueTypeToExpressionType<bool>() { return type::Boolean; }
@@ -99,10 +226,37 @@ template <> type::Type valueTypeToExpressionType<float>() { return type::Number;
template <> type::Type valueTypeToExpressionType<std::string>() { return type::String; }
template <> type::Type valueTypeToExpressionType<mbgl::Color>() { return type::Color; }
template <> type::Type valueTypeToExpressionType<std::unordered_map<std::string, Value>>() { return type::Object; }
-template <> type::Type valueTypeToExpressionType<std::array<float, 2>>() { return type::Array(type::Number, 2); }
-template <> type::Type valueTypeToExpressionType<std::array<float, 4>>() { return type::Array(type::Number, 4); }
template <> type::Type valueTypeToExpressionType<std::vector<Value>>() { return type::Array(type::Value); }
+
+template Value toExpressionValue(const mbgl::Value&);
+
+// instantiate templates fromExpressionValue<T>, toExpressionValue<T>, and valueTypeToExpressionType<T>
+template <typename T>
+struct instantiate {
+ void noop(const T& t) {
+ fromExpressionValue<T>(toExpressionValue(t));
+ valueTypeToExpressionType<T>();
+ }
+};
+
+template struct instantiate<std::array<float, 2>>;
+template struct instantiate<std::array<float, 4>>;
+template struct instantiate<std::vector<float>>;
+template struct instantiate<std::vector<std::string>>;
+template struct instantiate<AlignmentType>;
+template struct instantiate<CirclePitchScaleType>;
+template struct instantiate<IconTextFitType>;
+template struct instantiate<LineCapType>;
+template struct instantiate<LineJoinType>;
+template struct instantiate<SymbolPlacementType>;
+template struct instantiate<TextAnchorType>;
+template struct instantiate<TextJustifyType>;
+template struct instantiate<TextTransformType>;
+template struct instantiate<TranslateAnchorType>;
+template struct instantiate<LightAnchorType>;
+template struct instantiate<Position>;
+
} // namespace expression
} // namespace style
} // namespace mbgl
diff --git a/src/mbgl/style/function/expression.cpp b/src/mbgl/style/function/expression.cpp
index 76d4b3cd1f..4fef6e45d9 100644
--- a/src/mbgl/style/function/expression.cpp
+++ b/src/mbgl/style/function/expression.cpp
@@ -28,8 +28,8 @@ public:
EvaluationResult TypedExpression::evaluate(float z, const Feature& feature) const {
- std::unique_ptr<const GeometryTileFeature> f = std::make_unique<const GeoJSONFeature>(feature);
- return this->evaluate(EvaluationParameters {z, *f});
+ GeoJSONFeature f(feature);
+ return this->evaluate(EvaluationParameters {z, &f});
}
} // namespace expression