diff options
author | John Firebaugh <john.firebaugh@gmail.com> | 2018-06-29 12:52:08 -0700 |
---|---|---|
committer | John Firebaugh <john.firebaugh@gmail.com> | 2018-07-13 11:53:27 -0700 |
commit | 8696db551b82601971676ab892afc2a479342f77 (patch) | |
tree | 3d856db5fc934256962a75808ec1a821c457aa02 | |
parent | 1031e7c8bf936f05dd86e4570d4e86db5582ef16 (diff) | |
download | qtlocation-mapboxgl-upstream/function-conversion.tar.gz |
[core] Convert legacy functions directly to expressionsupstream/function-conversion
22 files changed, 774 insertions, 1190 deletions
diff --git a/benchmark/function/composite_function.benchmark.cpp b/benchmark/function/composite_function.benchmark.cpp index e2545e6349..8442e0aa15 100644 --- a/benchmark/function/composite_function.benchmark.cpp +++ b/benchmark/function/composite_function.benchmark.cpp @@ -2,7 +2,6 @@ #include <mbgl/benchmark/stub_geometry_tile_feature.hpp> -#include <mbgl/style/function/composite_exponential_stops.hpp> #include <mbgl/style/function/composite_function.hpp> #include <mbgl/style/conversion.hpp> diff --git a/cmake/core-files.cmake b/cmake/core-files.cmake index 9a26ac70b0..7d7e887a76 100644 --- a/cmake/core-files.cmake +++ b/cmake/core-files.cmake @@ -426,6 +426,7 @@ set(MBGL_CORE_FILES src/mbgl/style/conversion/constant.cpp src/mbgl/style/conversion/coordinate.cpp src/mbgl/style/conversion/filter.cpp + src/mbgl/style/conversion/function.cpp src/mbgl/style/conversion/geojson.cpp src/mbgl/style/conversion/geojson_options.cpp src/mbgl/style/conversion/get_json_type.cpp @@ -454,6 +455,7 @@ set(MBGL_CORE_FILES include/mbgl/style/expression/compound_expression.hpp include/mbgl/style/expression/dsl.hpp include/mbgl/style/expression/equals.hpp + include/mbgl/style/expression/error.hpp include/mbgl/style/expression/expression.hpp include/mbgl/style/expression/find_zoom_curve.hpp include/mbgl/style/expression/get_covering_stops.hpp @@ -498,20 +500,9 @@ set(MBGL_CORE_FILES # style/function include/mbgl/style/function/camera_function.hpp - include/mbgl/style/function/categorical_stops.hpp - include/mbgl/style/function/composite_categorical_stops.hpp - 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 include/mbgl/style/function/source_function.hpp - src/mbgl/style/function/categorical_stops.cpp - src/mbgl/style/function/convert.cpp src/mbgl/style/function/expression.cpp - src/mbgl/style/function/identity_stops.cpp # style/layers include/mbgl/style/layers/background_layer.hpp diff --git a/cmake/test-files.cmake b/cmake/test-files.cmake index b2c0172d73..5d76ffcade 100644 --- a/cmake/test-files.cmake +++ b/cmake/test-files.cmake @@ -90,8 +90,6 @@ set(MBGL_TEST_FILES # style/function test/style/function/camera_function.test.cpp test/style/function/composite_function.test.cpp - test/style/function/exponential_stops.test.cpp - test/style/function/interval_stops.test.cpp test/style/function/source_function.test.cpp # test diff --git a/include/mbgl/style/conversion/function.hpp b/include/mbgl/style/conversion/function.hpp index 932925c53c..5ddede324b 100644 --- a/include/mbgl/style/conversion/function.hpp +++ b/include/mbgl/style/conversion/function.hpp @@ -3,224 +3,15 @@ #include <mbgl/style/function/camera_function.hpp> #include <mbgl/style/function/source_function.hpp> #include <mbgl/style/function/composite_function.hpp> -#include <mbgl/style/function/convert.hpp> -#include <mbgl/style/function/categorical_stops.hpp> -#include <mbgl/style/function/exponential_stops.hpp> -#include <mbgl/style/function/interval_stops.hpp> -#include <mbgl/style/function/identity_stops.hpp> -#include <mbgl/style/function/composite_exponential_stops.hpp> -#include <mbgl/style/function/composite_interval_stops.hpp> -#include <mbgl/style/function/composite_categorical_stops.hpp> #include <mbgl/style/conversion.hpp> #include <mbgl/style/conversion/constant.hpp> -#include <mbgl/util/ignore.hpp> -#include <mbgl/util/variant.hpp> +#include <mbgl/style/expression/expression.hpp> +#include <mbgl/style/expression/value.hpp> namespace mbgl { namespace style { namespace conversion { -template <class D, class R> -optional<std::map<D, R>> convertStops(const Convertible& value, Error& error) { - auto stopsValue = objectMember(value, "stops"); - if (!stopsValue) { - error = { "function value must specify stops" }; - return {}; - } - - if (!isArray(*stopsValue)) { - error = { "function stops must be an array" }; - return {}; - } - - if (arrayLength(*stopsValue) == 0) { - error = { "function must have at least one stop" }; - return {}; - } - - std::map<D, R> stops; - for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { - const auto& stopValue = arrayMember(*stopsValue, i); - - if (!isArray(stopValue)) { - error = { "function stop must be an array" }; - return {}; - } - - if (arrayLength(stopValue) != 2) { - error = { "function stop must have two elements" }; - return {}; - } - - optional<D> d = convert<D>(arrayMember(stopValue, 0), error); - if (!d) { - return {}; - } - - optional<R> r = convert<R>(arrayMember(stopValue, 1), error); - if (!r) { - return {}; - } - - stops.emplace(*d, *r); - } - - return stops; -} - -template <class T> -struct Converter<ExponentialStops<T>> { - static constexpr const char * type = "exponential"; - - optional<ExponentialStops<T>> operator()(const Convertible& value, Error& error) const { - auto stops = convertStops<float, T>(value, error); - if (!stops) { - return {}; - } - - auto baseValue = objectMember(value, "base"); - if (!baseValue) { - return ExponentialStops<T>(*stops); - } - - optional<float> base = toNumber(*baseValue); - if (!base) { - error = { "function base must be a number"}; - return {}; - } - - return ExponentialStops<T>(*stops, *base); - } -}; - -template <class T> -struct Converter<IntervalStops<T>> { - static constexpr const char * type = "interval"; - - optional<IntervalStops<T>> operator()(const Convertible& value, Error& error) const { - auto stops = convertStops<float, T>(value, error); - if (!stops) { - return {}; - } - return IntervalStops<T>(*stops); - } -}; - -template <> -struct Converter<CategoricalValue> { - optional<CategoricalValue> operator()(const Convertible& value, Error& error) const { - auto b = toBool(value); - if (b) { - return { *b }; - } - - auto n = toNumber(value); - if (n) { - return { int64_t(*n) }; - } - - auto s = toString(value); - if (s) { - return { *s }; - } - - error = { "stop domain value must be a number, string, or boolean" }; - return {}; - } -}; - -template <class T> -struct Converter<CategoricalStops<T>> { - static constexpr const char * type = "categorical"; - - optional<CategoricalStops<T>> operator()(const Convertible& value, Error& error) const { - auto stops = convertStops<CategoricalValue, T>(value, error); - if (!stops) { - return {}; - } - return CategoricalStops<T>( - std::map<CategoricalValue, T>((*stops).begin(), (*stops).end())); - } -}; - -template <class T> -struct Converter<IdentityStops<T>> { - static constexpr const char * type = "identity"; - - optional<IdentityStops<T>> operator()(const Convertible&, Error&) const { - return IdentityStops<T>(); - } -}; - -template <class, class> -struct StopsConverter; - -template <class T, class... Ts> -struct StopsConverter<T, variant<Ts...>> { -public: - optional<variant<Ts...>> operator()(const Convertible& value, Error& error) const { - std::string type = util::Interpolatable<T>::value ? "exponential" : "interval"; - - auto typeValue = objectMember(value, "type"); - if (typeValue && toString(*typeValue)) { - type = *toString(*typeValue); - } - - bool matched = false; - optional<variant<Ts...>> result; - - // Workaround for https://gcc.gnu.org/bugzilla/show_bug.cgi?id=47226 - auto tryConvert = [&] (auto* tp) { - using Stops = std::decay_t<decltype(*tp)>; - if (type == Converter<Stops>::type) { - matched = true; - optional<Stops> stops = convert<Stops>(value, error); - if (stops) { - result = variant<Ts...>(*stops); - } - } - }; - - util::ignore({ - (tryConvert((Ts*)nullptr), 0)... - }); - - if (!matched) { - error = { "unsupported function type" }; - return {}; - } - - return result; - } -}; - -template <class T> -struct Converter<CameraFunction<T>> { - optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const { - if (!isObject(value)) { - error = { "function must be an object" }; - return {}; - } - - using Stops = std::conditional_t< - util::Interpolatable<T>::value, - variant< - ExponentialStops<T>, - IntervalStops<T>>, - variant< - IntervalStops<T>>>; - - auto stops = StopsConverter<T, Stops>()(value, error); - if (!stops) { - return {}; - } - - return CameraFunction<T>((*stops).match([&] (const auto& s) { - return expression::Convert::toExpression(s); - }), false); - } -}; - template <class T> optional<optional<T>> convertDefaultValue(const Convertible& value, Error& error) { auto defaultValueValue = objectMember(value, "default"); @@ -237,199 +28,48 @@ optional<optional<T>> convertDefaultValue(const Convertible& value, Error& error return { *defaultValue }; } -template <class T> -struct Converter<SourceFunction<T>> { - optional<SourceFunction<T>> operator()(const Convertible& value, Error& error) const { - if (!isObject(value)) { - error = { "function must be an object" }; - return {}; - } - - auto propertyValue = objectMember(value, "property"); - if (!propertyValue) { - error = { "function must specify property" }; - return {}; - } - - auto propertyString = toString(*propertyValue); - if (!propertyString) { - error = { "function property must be a string" }; - return {}; - } - - using Stops = std::conditional_t< - util::Interpolatable<T>::value, - variant< - ExponentialStops<T>, - IntervalStops<T>, - CategoricalStops<T>, - IdentityStops<T>>, - variant< - IntervalStops<T>, - CategoricalStops<T>, - IdentityStops<T>>>; - - auto stops = StopsConverter<T, Stops>()(value, error); - if (!stops) { - return {}; - } - - auto defaultValue = convertDefaultValue<T>(value, error); - if (!defaultValue) { - return {}; - } - - return SourceFunction<T>((*stops).match([&] (const auto& s) { - return expression::Convert::toExpression(*propertyString, s); - }), *defaultValue); - } -}; - -template <class S> -struct CompositeValue : std::pair<float, S> { - using std::pair<float, S>::pair; -}; - -template <class S> -struct Converter<CompositeValue<S>> { - optional<CompositeValue<S>> operator()(const Convertible& value, Error& error) const { - if (!isObject(value)) { - error = { "stop must be an object" }; - return {}; - } - - auto zoomValue = objectMember(value, "zoom"); - if (!zoomValue) { - error = { "stop must specify zoom" }; - return {}; - } - - auto propertyValue = objectMember(value, "value"); - if (!propertyValue) { - error = { "stop must specify value" }; - return {}; - } - - optional<float> z = convert<float>(*zoomValue, error); - if (!z) { - return {}; - } - - optional<S> s = convert<S>(*propertyValue, error); - if (!s) { - return {}; - } - - return CompositeValue<S> { *z, *s }; - } -}; +optional<std::unique_ptr<expression::Expression>> convertCameraFunctionToExpression(expression::type::Type, const Convertible&, Error&); +optional<std::unique_ptr<expression::Expression>> convertSourceFunctionToExpression(expression::type::Type, const Convertible&, Error&); +optional<std::unique_ptr<expression::Expression>> convertCompositeFunctionToExpression(expression::type::Type, const Convertible&, Error&); template <class T> -struct Converter<CompositeExponentialStops<T>> { - static constexpr const char * type = "exponential"; - - optional<CompositeExponentialStops<T>> operator()(const Convertible& value, Error& error) const { - auto stops = convertStops<CompositeValue<float>, T>(value, error); - if (!stops) { +struct Converter<CameraFunction<T>> { + optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const { + auto expression = convertCameraFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error); + if (!expression) { return {}; } - - auto base = 1.0f; - auto baseValue = objectMember(value, "base"); - if (baseValue && toNumber(*baseValue)) { - base = *toNumber(*baseValue); - } - - std::map<float, std::map<float, T>> convertedStops; - for (const auto& stop : *stops) { - convertedStops[stop.first.first].emplace(stop.first.second, stop.second); - } - - return CompositeExponentialStops<T>(convertedStops, base); + return CameraFunction<T>(std::move(*expression), false); } }; template <class T> -struct Converter<CompositeIntervalStops<T>> { - static constexpr const char * type = "interval"; - - optional<CompositeIntervalStops<T>> operator()(const Convertible& value, Error& error) const { - auto stops = convertStops<CompositeValue<float>, T>(value, error); - if (!stops) { +struct Converter<SourceFunction<T>> { + optional<SourceFunction<T>> operator()(const Convertible& value, Error& error) const { + auto expression = convertSourceFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error); + if (!expression) { return {}; } - - std::map<float, std::map<float, T>> convertedStops; - for (const auto& stop : *stops) { - convertedStops[stop.first.first].emplace(stop.first.second, stop.second); - } - - return CompositeIntervalStops<T>(convertedStops); - } -}; - -template <class T> -struct Converter<CompositeCategoricalStops<T>> { - static constexpr const char * type = "categorical"; - - optional<CompositeCategoricalStops<T>> operator()(const Convertible& value, Error& error) const { - auto stops = convertStops<CompositeValue<CategoricalValue>, T>(value, error); - if (!stops) { + auto defaultValue = convertDefaultValue<T>(value, error); + if (!defaultValue) { return {}; } - - std::map<float, std::map<CategoricalValue, T>> convertedStops; - for (const auto& stop : *stops) { - convertedStops[stop.first.first].emplace(stop.first.second, stop.second); - } - - return CompositeCategoricalStops<T>(convertedStops); + return SourceFunction<T>(std::move(*expression), *defaultValue); } }; template <class T> struct Converter<CompositeFunction<T>> { optional<CompositeFunction<T>> operator()(const Convertible& value, Error& error) const { - if (!isObject(value)) { - error = { "function must be an object" }; + auto expression = convertCompositeFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error); + if (!expression) { return {}; } - - auto propertyValue = objectMember(value, "property"); - if (!propertyValue) { - error = { "function must specify property" }; - return {}; - } - - auto propertyString = toString(*propertyValue); - if (!propertyString) { - error = { "function property must be a string" }; - return {}; - } - - using Stops = std::conditional_t< - util::Interpolatable<T>::value, - variant< - CompositeExponentialStops<T>, - CompositeIntervalStops<T>, - CompositeCategoricalStops<T>>, - variant< - CompositeIntervalStops<T>, - CompositeCategoricalStops<T>>>; - - auto stops = StopsConverter<T, Stops>()(value, error); - if (!stops) { - return {}; - } - auto defaultValue = convertDefaultValue<T>(value, error); if (!defaultValue) { return {}; } - - return CompositeFunction<T>((*stops).match([&] (const auto& s) { - return expression::Convert::toExpression(*propertyString, s); - }), *defaultValue); + return CompositeFunction<T>(std::move(*expression), *defaultValue); } }; diff --git a/include/mbgl/style/conversion/property_value.hpp b/include/mbgl/style/conversion/property_value.hpp index dc1c32830f..e52c63d023 100644 --- a/include/mbgl/style/conversion/property_value.hpp +++ b/include/mbgl/style/conversion/property_value.hpp @@ -9,6 +9,7 @@ #include <mbgl/style/expression/is_expression.hpp> #include <mbgl/style/expression/find_zoom_curve.hpp> #include <mbgl/style/expression/parsing_context.hpp> +#include <mbgl/style/expression/literal.hpp> namespace mbgl { namespace style { diff --git a/include/mbgl/style/expression/dsl.hpp b/include/mbgl/style/expression/dsl.hpp index 8a5d1d84f5..22278b0975 100644 --- a/include/mbgl/style/expression/dsl.hpp +++ b/include/mbgl/style/expression/dsl.hpp @@ -5,6 +5,7 @@ #include <mbgl/style/expression/interpolator.hpp> #include <memory> +#include <string> #include <initializer_list> namespace mbgl { @@ -15,6 +16,8 @@ namespace dsl { // This convenience API does little to no expression validation or type-checking, and is intended for // use only by test and other non-production code. +std::unique_ptr<Expression> error(std::string); + std::unique_ptr<Expression> literal(const char* value); std::unique_ptr<Expression> literal(Value value); std::unique_ptr<Expression> literal(std::initializer_list<double> value); @@ -22,6 +25,7 @@ std::unique_ptr<Expression> literal(std::initializer_list<const char *> value); std::unique_ptr<Expression> number(std::unique_ptr<Expression>); std::unique_ptr<Expression> string(std::unique_ptr<Expression>); +std::unique_ptr<Expression> boolean(std::unique_ptr<Expression>); std::unique_ptr<Expression> toColor(const char* value); std::unique_ptr<Expression> toColor(std::unique_ptr<Expression>); diff --git a/include/mbgl/style/expression/error.hpp b/include/mbgl/style/expression/error.hpp new file mode 100644 index 0000000000..bfb9247d11 --- /dev/null +++ b/include/mbgl/style/expression/error.hpp @@ -0,0 +1,39 @@ +#pragma once + +#include <mbgl/style/expression/expression.hpp> + +#include <string> + +namespace mbgl { +namespace style { +namespace expression { + +class Error : public Expression { +public: + Error(std::string message_) + : Expression(type::Error), + message(std::move(message_)) {} + + void eachChild(const std::function<void(const Expression&)>&) const override {} + + bool operator==(const Expression& e) const override { + return dynamic_cast<const Error*>(&e); + } + + EvaluationResult evaluate(const EvaluationContext&) const override { + return EvaluationError{message}; + } + + std::vector<optional<Value>> possibleOutputs() const override { + return {}; + } + + std::string getOperator() const override { return "error"; } + +private: + std::string message; +}; + +} // namespace expression +} // namespace style +} // namespace mbgl diff --git a/include/mbgl/style/function/categorical_stops.hpp b/include/mbgl/style/function/categorical_stops.hpp deleted file mode 100644 index c8505115ab..0000000000 --- a/include/mbgl/style/function/categorical_stops.hpp +++ /dev/null @@ -1,40 +0,0 @@ -#pragma once - -#include <mbgl/util/feature.hpp> -#include <mbgl/util/variant.hpp> - -#include <cassert> -#include <utility> -#include <map> - -namespace mbgl { -namespace style { - -class CategoricalValue : public variant<bool, int64_t, std::string> { -public: - using variant<bool, int64_t, std::string>::variant; -}; - -template <class T> -class CategoricalStops { -public: - using Stops = std::map<CategoricalValue, T>; - - Stops stops; - - CategoricalStops() = default; - CategoricalStops(Stops stops_) - : stops(std::move(stops_)) { - assert(stops.size() > 0); - } - - optional<T> evaluate(const Value&) const; - - friend bool operator==(const CategoricalStops& lhs, - const CategoricalStops& rhs) { - return lhs.stops == rhs.stops; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/composite_categorical_stops.hpp b/include/mbgl/style/function/composite_categorical_stops.hpp deleted file mode 100644 index b796621d1a..0000000000 --- a/include/mbgl/style/function/composite_categorical_stops.hpp +++ /dev/null @@ -1,30 +0,0 @@ -#pragma once - -#include <mbgl/style/function/categorical_stops.hpp> - -namespace mbgl { -namespace style { - -template <class T> -class CompositeCategoricalStops { -public: - using Stops = std::map<float, std::map<CategoricalValue, T>>; - Stops stops; - - CompositeCategoricalStops() = default; - CompositeCategoricalStops(Stops stops_) - : stops(std::move(stops_)) { - } - - CategoricalStops<T> innerStops(const std::map<CategoricalValue, T>& stops_) const { - return CategoricalStops<T>(stops_); - } - - friend bool operator==(const CompositeCategoricalStops& lhs, - const CompositeCategoricalStops& rhs) { - return lhs.stops == rhs.stops; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/composite_exponential_stops.hpp b/include/mbgl/style/function/composite_exponential_stops.hpp deleted file mode 100644 index f1ad32a04d..0000000000 --- a/include/mbgl/style/function/composite_exponential_stops.hpp +++ /dev/null @@ -1,35 +0,0 @@ -#pragma once - -#include <mbgl/style/function/exponential_stops.hpp> - -#include <map> - -namespace mbgl { -namespace style { - -template <class T> -class CompositeExponentialStops { -public: - using Stops = std::map<float, std::map<float, T>>; - - Stops stops; - float base = 1.0f; - - CompositeExponentialStops() = default; - CompositeExponentialStops(Stops stops_, float base_ = 1.0f) - : stops(std::move(stops_)), - base(base_) { - } - - ExponentialStops<T> innerStops(const std::map<float, T>& stops_) const { - return ExponentialStops<T>(stops_, base); - } - - friend bool operator==(const CompositeExponentialStops& lhs, - const CompositeExponentialStops& rhs) { - return lhs.stops == rhs.stops && lhs.base == rhs.base; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/composite_interval_stops.hpp b/include/mbgl/style/function/composite_interval_stops.hpp deleted file mode 100644 index 3c495f2a7f..0000000000 --- a/include/mbgl/style/function/composite_interval_stops.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include <mbgl/style/function/interval_stops.hpp> - -#include <map> - -namespace mbgl { -namespace style { - -template <class T> -class CompositeIntervalStops { -public: - using Stops = std::map<float, std::map<float, T>>; - Stops stops; - - CompositeIntervalStops() = default; - CompositeIntervalStops(Stops stops_) - : stops(std::move(stops_)) { - } - - IntervalStops<T> innerStops(const std::map<float, T>& stops_) const { - return IntervalStops<T>(stops_); - } - - friend bool operator==(const CompositeIntervalStops& lhs, - const CompositeIntervalStops& rhs) { - return lhs.stops == rhs.stops; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/convert.hpp b/include/mbgl/style/function/convert.hpp deleted file mode 100644 index a0dae992fa..0000000000 --- a/include/mbgl/style/function/convert.hpp +++ /dev/null @@ -1,322 +0,0 @@ -#pragma once - -#include <mbgl/style/expression/array_assertion.hpp> -#include <mbgl/style/expression/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/coercion.hpp> -#include <mbgl/style/expression/interpolate.hpp> -#include <mbgl/style/expression/expression.hpp> -#include <mbgl/style/expression/literal.hpp> -#include <mbgl/style/expression/match.hpp> -#include <mbgl/style/expression/step.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/composite_exponential_stops.hpp> -#include <mbgl/style/function/composite_interval_stops.hpp> -#include <mbgl/style/function/composite_categorical_stops.hpp> -#include <mbgl/style/function/identity_stops.hpp> - -#include <mbgl/util/enum.hpp> -#include <mbgl/style/types.hpp> - -#include <string> - - -namespace mbgl { -namespace style { -namespace expression { - -namespace detail { - -class ErrorExpression : public Expression { -public: - ErrorExpression(std::string message_) : Expression(type::Error), message(std::move(message_)) {} - void eachChild(const std::function<void(const Expression&)>&) const override {} - - bool operator==(const Expression& e) const override { - return dynamic_cast<const ErrorExpression*>(&e); - } - - EvaluationResult evaluate(const EvaluationContext&) const override { - return EvaluationError{message}; - } - - std::vector<optional<Value>> possibleOutputs() const override { - return {}; - } - - std::string getOperator() const override { return "error"; } -private: - std::string message; -}; - -} // namespace detail - - -// Create expressions representing 'classic' (i.e. stop-based) style functions - -struct Convert { - template <typename T> - static std::unique_ptr<Literal> makeLiteral(const T& value) { - return std::make_unique<Literal>(Value(toExpressionValue(value))); - } - - static std::unique_ptr<Expression> makeGet(type::Type type, const std::string& property) { - ParsingContext ctx; - std::vector<std::unique_ptr<Expression>> getArgs; - getArgs.push_back(makeLiteral(property)); - ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx); - assert(get); - assert(ctx.getErrors().size() == 0); - - std::vector<std::unique_ptr<Expression>> assertionArgs; - assertionArgs.push_back(std::move(*get)); - - return std::make_unique<Assertion>(type, std::move(assertionArgs)); - } - - static std::unique_ptr<Expression> makeZoom() { - ParsingContext ctx; - ParseResult zoom = createCompoundExpression("zoom", std::vector<std::unique_ptr<Expression>>(), ctx); - assert(zoom); - assert(ctx.getErrors().size() == 0); - return std::move(*zoom); - } - - static std::unique_ptr<Expression> makeError(std::string message) { - return std::make_unique<detail::ErrorExpression>(message); - } - - template <typename Key> - static ParseResult makeMatch(type::Type type, - std::unique_ptr<Expression> input, - std::map<CategoricalValue, std::unique_ptr<Expression>> stops) { - // match expression - typename Match<Key>::Branches branches; - for(auto it = stops.begin(); it != stops.end(); it++) { - assert(it->first.template is<Key>()); - Key key = it->first.template get<Key>(); - branches.emplace( - std::move(key), - std::move(it->second) - ); - } - - return ParseResult(std::make_unique<Match<Key>>(std::move(type), - std::move(input), - std::move(branches), - makeError("No matching label"))); - } - - static ParseResult makeCase(type::Type type, - std::unique_ptr<Expression> input, - std::map<CategoricalValue, std::unique_ptr<Expression>> stops) { - // case expression - std::vector<typename Case::Branch> branches; - - auto it = stops.find(true); - std::unique_ptr<Expression> true_case = it == stops.end() ? - makeError("No matching label") : - std::move(it->second); - - it = stops.find(false); - std::unique_ptr<Expression> false_case = it == stops.end() ? - makeError("No matching label") : - std::move(it->second); - - branches.push_back(std::make_pair(std::move(input), std::move(true_case))); - return ParseResult(std::make_unique<Case>(std::move(type), std::move(branches), std::move(false_case))); - } - - template <typename T> - static ParseResult fromCategoricalStops(std::map<CategoricalValue, T> stops, const std::string& property) { - assert(stops.size() > 0); - - std::map<CategoricalValue, std::unique_ptr<Expression>> convertedStops; - for(const std::pair<CategoricalValue, T>& stop : stops) { - convertedStops.emplace( - stop.first, - makeLiteral(stop.second) - ); - } - - type::Type type = valueTypeToExpressionType<T>(); - - const CategoricalValue& firstKey = stops.begin()->first; - return firstKey.match( - [&](bool) { - return makeCase(type, makeGet(type::Boolean, property), std::move(convertedStops)); - }, - [&](const std::string&) { - return makeMatch<std::string>(type, makeGet(type::String, property), std::move(convertedStops)); - }, - [&](int64_t) { - return makeMatch<int64_t>(type, makeGet(type::Number, property), std::move(convertedStops)); - } - ); - } - - template <typename T> - static std::map<double, std::unique_ptr<Expression>> convertStops(const std::map<float, T>& stops) { - std::map<double, std::unique_ptr<Expression>> convertedStops; - for(const auto& stop : stops) { - convertedStops.emplace( - stop.first, - makeLiteral(stop.second) - ); - } - return convertedStops; - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const ExponentialStops<T>& stops) - { - ParsingContext ctx; - ParseResult e = createInterpolate(valueTypeToExpressionType<T>(), - ExponentialInterpolator(stops.base), - makeZoom(), - convertStops(stops.stops), - ctx); - assert(e); - return std::move(*e); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const IntervalStops<T>& stops) - { - ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(), - makeZoom(), - convertStops(stops.stops))); - assert(e); - return std::move(*e); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const ExponentialStops<T>& stops) - { - ParsingContext ctx; - ParseResult e = createInterpolate(valueTypeToExpressionType<T>(), - ExponentialInterpolator(stops.base), - makeGet(type::Number, property), - convertStops(stops.stops), - ctx); - assert(e); - return std::move(*e); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const IntervalStops<T>& stops) - { - std::unique_ptr<Expression> get = makeGet(type::Number, property); - ParseResult e(std::make_unique<Step>(valueTypeToExpressionType<T>(), - std::move(get), - convertStops(stops.stops))); - assert(e); - return std::move(*e); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const CategoricalStops<T>& stops) - { - ParseResult expr = fromCategoricalStops(stops.stops, property); - assert(expr); - return std::move(*expr); - } - - // interpolatable zoom curve - template <typename T> - static typename std::enable_if_t<util::Interpolatable<T>::value, - ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) { - ParsingContext ctx; - return createInterpolate(valueTypeToExpressionType<T>(), - ExponentialInterpolator(1.0), - makeZoom(), - std::move(stops), - ctx); - } - - // non-interpolatable zoom curve - template <typename T> - static typename std::enable_if_t<!util::Interpolatable<T>::value, - ParseResult> makeZoomCurve(std::map<double, std::unique_ptr<Expression>> stops) { - return ParseResult(std::make_unique<Step>(valueTypeToExpressionType<T>(), makeZoom(), std::move(stops))); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const CompositeExponentialStops<T>& stops) - { - ParsingContext ctx; - - std::map<double, std::unique_ptr<Expression>> outerStops; - for (const std::pair<float, std::map<float, T>>& stop : stops.stops) { - ParseResult innerInterpolate = createInterpolate(valueTypeToExpressionType<T>(), - ExponentialInterpolator(stops.base), - makeGet(type::Number, property), - convertStops(stop.second), - ctx); - assert(innerInterpolate); - outerStops.emplace(stop.first, std::move(*innerInterpolate)); - } - - ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops)); - assert(zoomCurve); - return std::move(*zoomCurve); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const CompositeIntervalStops<T>& stops) - { - std::map<double, std::unique_ptr<Expression>> outerStops; - for (const std::pair<float, std::map<float, T>>& stop : stops.stops) { - std::unique_ptr<Expression> get = makeGet(type::Number, property); - ParseResult innerInterpolate(std::make_unique<Step>(valueTypeToExpressionType<T>(), - std::move(get), - convertStops(stop.second))); - assert(innerInterpolate); - outerStops.emplace(stop.first, std::move(*innerInterpolate)); - } - - ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops)); - assert(zoomCurve); - return std::move(*zoomCurve); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const CompositeCategoricalStops<T>& stops) - { - std::map<double, std::unique_ptr<Expression>> outerStops; - for (const std::pair<float, std::map<CategoricalValue, T>>& stop : stops.stops) { - ParseResult innerInterpolate = fromCategoricalStops(stop.second, property); - assert(innerInterpolate); - outerStops.emplace(stop.first, std::move(*innerInterpolate)); - } - - ParseResult zoomCurve = makeZoomCurve<T>(std::move(outerStops)); - assert(zoomCurve); - return std::move(*zoomCurve); - } - - template <typename T> - static std::unique_ptr<Expression> toExpression(const std::string& property, - const IdentityStops<T>&) - { - return fromIdentityFunction(property, expression::valueTypeToExpressionType<T>()); - } - -private: - static std::unique_ptr<Expression> fromIdentityFunction(const std::string& property, type::Type type); -}; - -} // namespace expression -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/exponential_stops.hpp b/include/mbgl/style/function/exponential_stops.hpp deleted file mode 100644 index b3866c4059..0000000000 --- a/include/mbgl/style/function/exponential_stops.hpp +++ /dev/null @@ -1,56 +0,0 @@ -#pragma once - -#include <mbgl/util/feature.hpp> -#include <mbgl/util/interpolate.hpp> - -#include <map> - -namespace mbgl { -namespace style { - -template <class T> -class ExponentialStops { -public: - using Stops = std::map<float, T>; - - Stops stops; - float base = 1.0f; - - ExponentialStops() = default; - ExponentialStops(Stops stops_, float base_ = 1.0f) - : stops(std::move(stops_)), - base(base_) { - } - - optional<T> evaluate(float z) const { - if (stops.empty()) { - return {}; - } - - auto it = stops.upper_bound(z); - if (it == stops.end()) { - return stops.rbegin()->second; - } else if (it == stops.begin()) { - return stops.begin()->second; - } else { - return util::interpolate(std::prev(it)->second, it->second, - util::interpolationFactor(base, { std::prev(it)->first, it->first }, z)); - } - } - - optional<T> evaluate(const Value& value) const { - optional<float> z = numericValue<float>(value); - if (!z) { - return {}; - } - return evaluate(*z); - } - - friend bool operator==(const ExponentialStops& lhs, - const ExponentialStops& rhs) { - return lhs.stops == rhs.stops && lhs.base == rhs.base; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/identity_stops.hpp b/include/mbgl/style/function/identity_stops.hpp deleted file mode 100644 index 741ebbbe0c..0000000000 --- a/include/mbgl/style/function/identity_stops.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once - -#include <mbgl/util/feature.hpp> - -namespace mbgl { -namespace style { - -template <class T> -class IdentityStops { -public: - optional<T> evaluate(const Value&) const; - - friend bool operator==(const IdentityStops&, - const IdentityStops&) { - return true; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/include/mbgl/style/function/interval_stops.hpp b/include/mbgl/style/function/interval_stops.hpp deleted file mode 100644 index 45e2dc6f2e..0000000000 --- a/include/mbgl/style/function/interval_stops.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#pragma once - -#include <mbgl/util/feature.hpp> - -#include <map> - -namespace mbgl { -namespace style { - -template <class T> -class IntervalStops { -public: - using Stops = std::map<float, T>; - Stops stops; - - IntervalStops() = default; - IntervalStops(Stops stops_) - : stops(std::move(stops_)) { - } - - optional<T> evaluate(float z) const { - if (stops.empty()) { - return {}; - } - - auto it = stops.upper_bound(z); - if (it == stops.end()) { - return stops.rbegin()->second; - } else if (it == stops.begin()) { - return stops.begin()->second; - } else { - return std::prev(it)->second; - } - } - - optional<T> evaluate(const Value& value) const { - optional<float> z = numericValue<float>(value); - if (!z) { - return {}; - } - return evaluate(*z); - } - - friend bool operator==(const IntervalStops& lhs, - const IntervalStops& rhs) { - return lhs.stops == rhs.stops; - } -}; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/conversion/function.cpp b/src/mbgl/style/conversion/function.cpp new file mode 100644 index 0000000000..64adf522a1 --- /dev/null +++ b/src/mbgl/style/conversion/function.cpp @@ -0,0 +1,699 @@ +#include <mbgl/style/conversion/function.hpp> +#include <mbgl/style/expression/dsl.hpp> +#include <mbgl/style/expression/step.hpp> +#include <mbgl/style/expression/interpolate.hpp> +#include <mbgl/style/expression/match.hpp> +#include <mbgl/style/expression/case.hpp> +#include <mbgl/style/expression/array_assertion.hpp> +#include <mbgl/util/string.hpp> + +#include <cassert> + +namespace mbgl { +namespace style { +namespace conversion { + +using namespace expression; +using namespace expression::dsl; + +// Ad-hoc Converters for double and int64_t. We should replace float with double wholesale, +// and promote the int64_t Converter to general use (and it should check that the input is +// an integer). +template <> +struct Converter<double> { + optional<double> operator()(const Convertible& value, Error& error) const { + auto converted = convert<float>(value, error); + if (!converted) { + return {}; + } + return *converted; + } +}; + +template <> +struct Converter<int64_t> { + optional<int64_t> operator()(const Convertible& value, Error& error) const { + auto converted = convert<float>(value, error); + if (!converted) { + return {}; + } + return *converted; + } +}; + +enum class FunctionType { + Interval, + Exponential, + Categorical, + Identity, + Invalid +}; + +static bool interpolatable(type::Type type) { + return type.match( + [&] (const type::NumberType&) { + return true; + }, + [&] (const type::ColorType&) { + return true; + }, + [&] (const type::Array& array) { + return array.N && array.itemType == type::Number; + }, + [&] (const auto&) { + return false; + } + ); +} + +static FunctionType functionType(type::Type type, const Convertible& value) { + auto typeValue = objectMember(value, "type"); + if (!typeValue) { + return interpolatable(type) ? FunctionType::Exponential : FunctionType::Interval; + } + + optional<std::string> string = toString(*typeValue); + if (!string) { + return FunctionType::Invalid; + } + + if (*string == "interval") + return FunctionType::Interval; + if (*string == "exponential" && interpolatable(type)) + return FunctionType::Exponential; + if (*string == "categorical") + return FunctionType::Categorical; + if (*string == "identity") + return FunctionType::Identity; + + return FunctionType::Invalid; +} + +static optional<std::unique_ptr<Expression>> convertLiteral(type::Type type, const Convertible& value, Error& error) { + return type.match( + [&] (const type::NumberType&) -> optional<std::unique_ptr<Expression>> { + auto result = convert<float>(value, error); + if (!result) { + return {}; + } + return literal(double(*result)); + }, + [&] (const type::BooleanType&) -> optional<std::unique_ptr<Expression>> { + auto result = convert<bool>(value, error); + if (!result) { + return {}; + } + return literal(*result); + }, + [&] (const type::StringType&) -> optional<std::unique_ptr<Expression>> { + auto result = convert<std::string>(value, error); + if (!result) { + return {}; + } + return literal(*result); + }, + [&] (const type::ColorType&) -> optional<std::unique_ptr<Expression>> { + auto result = convert<Color>(value, error); + if (!result) { + return {}; + } + return literal(*result); + }, + [&] (const type::Array& array) -> optional<std::unique_ptr<Expression>> { + if (!isArray(value)) { + error = { "value must be an array" }; + return {}; + } + if (array.N && arrayLength(value) != *array.N) { + error = { "value must be an array of length " + util::toString(*array.N) }; + return {}; + } + return array.itemType.match( + [&] (const type::NumberType&) -> optional<std::unique_ptr<Expression>> { + std::vector<expression::Value> result; + result.reserve(arrayLength(value)); + for (std::size_t i = 0; i < arrayLength(value); ++i) { + optional<float> number = toNumber(arrayMember(value, i)); + if (!number) { + error = { "value must be an array of numbers" }; + return {}; + } + result.push_back(double(*number)); + } + return literal(result); + }, + [&] (const type::StringType&) -> optional<std::unique_ptr<Expression>> { + std::vector<expression::Value> result; + result.reserve(arrayLength(value)); + for (std::size_t i = 0; i < arrayLength(value); ++i) { + optional<std::string> string = toString(arrayMember(value, i)); + if (!string) { + error = { "value must be an array of strings" }; + return {}; + } + result.push_back(*string); + } + return literal(result); + }, + [&] (const auto&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + } + ); + }, + [&] (const type::NullType&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + }, + [&] (const type::ObjectType&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + }, + [&] (const type::ErrorType&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + }, + [&] (const type::ValueType&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + }, + [&] (const type::CollatorType&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + } + ); +} + +static optional<std::map<double, std::unique_ptr<Expression>>> convertStops(type::Type type, + const Convertible& value, + Error& error) { + auto stopsValue = objectMember(value, "stops"); + if (!stopsValue) { + error = { "function value must specify stops" }; + return {}; + } + + if (!isArray(*stopsValue)) { + error = { "function stops must be an array" }; + return {}; + } + + if (arrayLength(*stopsValue) == 0) { + error = { "function must have at least one stop" }; + return {}; + } + + std::map<double, std::unique_ptr<Expression>> stops; + for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { + const auto& stopValue = arrayMember(*stopsValue, i); + + if (!isArray(stopValue)) { + error = { "function stop must be an array" }; + return {}; + } + + if (arrayLength(stopValue) != 2) { + error = { "function stop must have two elements" }; + return {}; + } + + optional<float> t = convert<float>(arrayMember(stopValue, 0), error); + if (!t) { + return {}; + } + + optional<std::unique_ptr<Expression>> e = convertLiteral(type, arrayMember(stopValue, 1), error); + if (!e) { + return {}; + } + + stops.emplace(*t, std::move(*e)); + } + + return { std::move(stops) }; +} + +template <class T> +optional<std::map<T, std::unique_ptr<Expression>>> convertBranches(type::Type type, + const Convertible& value, + Error& error) { + auto stopsValue = objectMember(value, "stops"); + if (!stopsValue) { + error = { "function value must specify stops" }; + return {}; + } + + if (!isArray(*stopsValue)) { + error = { "function stops must be an array" }; + return {}; + } + + if (arrayLength(*stopsValue) == 0) { + error = { "function must have at least one stop" }; + return {}; + } + + std::map<T, std::unique_ptr<Expression>> stops; + for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { + const auto& stopValue = arrayMember(*stopsValue, i); + + if (!isArray(stopValue)) { + error = { "function stop must be an array" }; + return {}; + } + + if (arrayLength(stopValue) != 2) { + error = { "function stop must have two elements" }; + return {}; + } + + optional<T> t = convert<T>(arrayMember(stopValue, 0), error); + if (!t) { + return {}; + } + + optional<std::unique_ptr<Expression>> e = convertLiteral(type, arrayMember(stopValue, 1), error); + if (!e) { + return {}; + } + + stops.emplace(*t, std::move(*e)); + } + + return { std::move(stops) }; +} + +static optional<double> convertBase(const Convertible& value, Error& error) { + auto baseValue = objectMember(value, "base"); + + if (!baseValue) { + return 1.0; + } + + auto base = toNumber(*baseValue); + if (!base) { + error = { "function base must be a number" }; + return {}; + } + + return *base; +} + +static std::unique_ptr<Expression> step(type::Type type, std::unique_ptr<Expression> input, std::map<double, std::unique_ptr<Expression>> stops) { + return std::make_unique<Step>(type, std::move(input), std::move(stops)); +} + +static std::unique_ptr<Expression> interpolate(type::Type type, Interpolator interpolator, std::unique_ptr<Expression> input, std::map<double, std::unique_ptr<Expression>> stops) { + ParsingContext ctx; + auto result = createInterpolate(type, std::move(interpolator), std::move(input), std::move(stops), ctx); + if (!result) { + assert(false); + return {}; + } + return std::move(*result); +} + +template <class T> +std::unique_ptr<Expression> categorical(type::Type type, const std::string& property, std::map<T, std::unique_ptr<Expression>> branches) { + std::unordered_map<T, std::shared_ptr<Expression>> convertedBranches; + for (auto& b : branches) { + convertedBranches[b.first] = std::move(b.second); + } + return std::make_unique<Match<T>>(type, get(literal(property)), std::move(convertedBranches), error("replaced with default")); +} + +template <> +std::unique_ptr<Expression> categorical<bool>(type::Type type, const std::string& property, std::map<bool, std::unique_ptr<Expression>> branches) { + auto it = branches.find(true); + std::unique_ptr<Expression> trueCase = it == branches.end() ? + error("replaced with default") : + std::move(it->second); + + it = branches.find(false); + std::unique_ptr<Expression> falseCase = it == branches.end() ? + error("replaced with default") : + std::move(it->second); + + std::vector<typename Case::Branch> trueBranch; + trueBranch.emplace_back(get(literal(property)), std::move(trueCase)); + + return std::make_unique<Case>(type, std::move(trueBranch), std::move(falseCase)); +} + +static optional<std::unique_ptr<Expression>> convertIntervalFunction(type::Type type, + const Convertible& value, + Error& error, + std::unique_ptr<Expression> input) { + auto stops = convertStops(type, value, error); + if (!stops) { + return {}; + } + return step(type, std::move(input), std::move(*stops)); +} + +static optional<std::unique_ptr<Expression>> convertExponentialFunction(type::Type type, + const Convertible& value, + Error& error, + std::unique_ptr<Expression> input) { + auto stops = convertStops(type, value, error); + if (!stops) { + return {}; + } + auto base = convertBase(value, error); + if (!base) { + return {}; + } + return interpolate(type, exponential(*base), std::move(input), std::move(*stops)); +} + +static optional<std::unique_ptr<Expression>> convertCategoricalFunction(type::Type type, + const Convertible& value, + Error& err, + const std::string& property) { + auto stopsValue = objectMember(value, "stops"); + if (!stopsValue) { + err = { "function value must specify stops" }; + return {}; + } + + if (!isArray(*stopsValue)) { + err = { "function stops must be an array" }; + return {}; + } + + if (arrayLength(*stopsValue) == 0) { + err = { "function must have at least one stop" }; + return {}; + } + + const auto& first = arrayMember(*stopsValue, 0); + + if (!isArray(first)) { + err = { "function stop must be an array" }; + return {}; + } + + if (arrayLength(first) != 2) { + err = { "function stop must have two elements" }; + return {}; + } + + if (toBool(arrayMember(first, 0))) { + auto branches = convertBranches<bool>(type, value, err); + if (!branches) { + return {}; + } + return categorical(type, property, std::move(*branches)); + } + + if (toNumber(arrayMember(first, 0))) { + auto branches = convertBranches<int64_t>(type, value, err); + if (!branches) { + return {}; + } + return categorical(type, property, std::move(*branches)); + } + + if (toString(arrayMember(first, 0))) { + auto branches = convertBranches<std::string>(type, value, err); + if (!branches) { + return {}; + } + return categorical(type, property, std::move(*branches)); + } + + err = { "stop domain value must be a number, string, or boolean" }; + return {}; +} + +optional<std::unique_ptr<Expression>> convertCameraFunctionToExpression(type::Type type, + const Convertible& value, + Error& error) { + if (!isObject(value)) { + error = { "function must be an object" }; + return {}; + } + + switch (functionType(type, value)) { + case FunctionType::Interval: + return convertIntervalFunction(type, value, error, zoom()); + case FunctionType::Exponential: + return convertExponentialFunction(type, value, error, zoom()); + default: + error = { "unsupported function type" }; + return {}; + } +} + +optional<std::unique_ptr<Expression>> convertSourceFunctionToExpression(type::Type type, + const Convertible& value, + Error& error) { + if (!isObject(value)) { + error = { "function must be an object" }; + return {}; + } + + auto propertyValue = objectMember(value, "property"); + if (!propertyValue) { + error = { "function must specify property" }; + return {}; + } + + auto property = toString(*propertyValue); + if (!property) { + error = { "function property must be a string" }; + return {}; + } + + switch (functionType(type, value)) { + case FunctionType::Interval: + return convertIntervalFunction(type, value, error, number(get(literal(*property)))); + case FunctionType::Exponential: + return convertExponentialFunction(type, value, error, number(get(literal(*property)))); + case FunctionType::Categorical: + return convertCategoricalFunction(type, value, error, *property); + case FunctionType::Identity: + return type.match( + [&] (const type::StringType&) -> optional<std::unique_ptr<Expression>> { + return string(get(literal(*property))); + }, + [&] (const type::NumberType&) -> optional<std::unique_ptr<Expression>> { + return number(get(literal(*property))); + }, + [&] (const type::BooleanType&) -> optional<std::unique_ptr<Expression>> { + return boolean(get(literal(*property))); + }, + [&] (const type::ColorType&) -> optional<std::unique_ptr<Expression>> { + return toColor(get(literal(*property))); + }, + [&] (const type::Array& array) -> optional<std::unique_ptr<Expression>> { + return std::unique_ptr<Expression>( + std::make_unique<ArrayAssertion>(array, get(literal(*property)))); + }, + [&] (const auto&) -> optional<std::unique_ptr<Expression>> { + assert(false); // No properties use this type. + return {}; + } + ); + default: + error = { "unsupported function type" }; + return {}; + } +} + +template <class T> +optional<std::unique_ptr<Expression>> composite(type::Type type, + const Convertible& value, + Error& error, + std::unique_ptr<Expression> (*makeInnerExpression) (type::Type type, + double base, + const std::string& property, + std::map<T, std::unique_ptr<Expression>>)) { + auto propertyValue = objectMember(value, "property"); + if (!propertyValue) { + error = { "function must specify property" }; + return {}; + } + + auto base = convertBase(value, error); + if (!base) { + return {}; + } + + auto propertyString = toString(*propertyValue); + if (!propertyString) { + error = { "function property must be a string" }; + return {}; + } + + auto stopsValue = objectMember(value, "stops"); + + // Checked by caller. + assert(stopsValue); + assert(isArray(*stopsValue)); + + std::map<float, std::map<T, std::unique_ptr<Expression>>> map; + + for (std::size_t i = 0; i < arrayLength(*stopsValue); ++i) { + const auto& stopValue = arrayMember(*stopsValue, i); + + if (!isArray(stopValue)) { + error = { "function stop must be an array" }; + return {}; + } + + if (arrayLength(stopValue) != 2) { + error = { "function stop must have two elements" }; + return {}; + } + + const auto& stopInput = arrayMember(stopValue, 0); + + if (!isObject(stopInput)) { + error = { "stop input must be an object" }; + return {}; + } + + auto zoomValue = objectMember(stopInput, "zoom"); + if (!zoomValue) { + error = { "stop input must specify zoom" }; + return {}; + } + + auto sourceValue = objectMember(stopInput, "value"); + if (!sourceValue) { + error = { "stop input must specify value" }; + return {}; + } + + optional<float> z = convert<float>(*zoomValue, error); + if (!z) { + return {}; + } + + optional<T> d = convert<T>(*sourceValue, error); + if (!d) { + return {}; + } + + optional<std::unique_ptr<Expression>> r = convertLiteral(type, arrayMember(stopValue, 1), error); + if (!r) { + return {}; + } + + map[*z].emplace(*d, std::move(*r)); + } + + std::map<double, std::unique_ptr<Expression>> stops; + + for (auto& e : map) { + stops.emplace(e.first, makeInnerExpression(type, *base, *propertyString, std::move(e.second))); + } + + if (interpolatable(type)) { + return interpolate(type, linear(), zoom(), std::move(stops)); + } else { + return step(type, zoom(), std::move(stops)); + } +} + +optional<std::unique_ptr<Expression>> convertCompositeFunctionToExpression(type::Type type, + const Convertible& value, + Error& err) { + if (!isObject(value)) { + err = { "function must be an object" }; + return {}; + } + + auto stopsValue = objectMember(value, "stops"); + if (!stopsValue) { + err = { "function value must specify stops" }; + return {}; + } + + if (!isArray(*stopsValue)) { + err = { "function stops must be an array" }; + return {}; + } + + if (arrayLength(*stopsValue) == 0) { + err = { "function must have at least one stop" }; + return {}; + } + + const auto& first = arrayMember(*stopsValue, 0); + + if (!isArray(first)) { + err = { "function stop must be an array" }; + return {}; + } + + if (arrayLength(first) != 2) { + err = { "function stop must have two elements" }; + return {}; + } + + const auto& stop = arrayMember(first, 0); + + if (!isObject(stop)) { + err = { "stop must be an object" }; + return {}; + } + + auto sourceValue = objectMember(stop, "value"); + if (!sourceValue) { + err = { "stop must specify value" }; + return {}; + } + + if (toBool(*sourceValue)) { + switch (functionType(type, value)) { + case FunctionType::Categorical: + return composite<bool>(type, value, err, [] (type::Type type_, double, const std::string& property, std::map<bool, std::unique_ptr<Expression>> stops) { + return categorical<bool>(type_, property, std::move(stops)); + }); + default: + err = { "unsupported function type" }; + return {}; + } + } + + if (toNumber(*sourceValue)) { + switch (functionType(type, value)) { + case FunctionType::Interval: + return composite<double>(type, value, err, [] (type::Type type_, double, const std::string& property, std::map<double, std::unique_ptr<Expression>> stops) { + return step(type_, number(get(literal(property))), std::move(stops)); + }); + case FunctionType::Exponential: + return composite<double>(type, value, err, [] (type::Type type_, double base, const std::string& property, std::map<double, std::unique_ptr<Expression>> stops) { + return interpolate(type_, exponential(base), number(get(literal(property))), std::move(stops)); + }); + case FunctionType::Categorical: + return composite<int64_t>(type, value, err, [] (type::Type type_, double, const std::string& property, std::map<int64_t, std::unique_ptr<Expression>> stops) { + return categorical<int64_t>(type_, property, std::move(stops)); + }); + default: + err = { "unsupported function type" }; + return {}; + } + } + + if (toString(*sourceValue)) { + switch (functionType(type, value)) { + case FunctionType::Categorical: + return composite<std::string>(type, value, err, [] (type::Type type_, double, const std::string& property, std::map<std::string, std::unique_ptr<Expression>> stops) { + return categorical<std::string>(type_, property, std::move(stops)); + }); + default: + err = { "unsupported function type" }; + return {}; + } + } + + err = { "stop domain value must be a number, string, or boolean" }; + return {}; +} + +} // namespace conversion +} // namespace style +} // namespace mbgl diff --git a/src/mbgl/style/expression/dsl.cpp b/src/mbgl/style/expression/dsl.cpp index 2298bdf8d3..5532e0a520 100644 --- a/src/mbgl/style/expression/dsl.cpp +++ b/src/mbgl/style/expression/dsl.cpp @@ -1,4 +1,5 @@ #include <mbgl/style/expression/dsl.hpp> +#include <mbgl/style/expression/error.hpp> #include <mbgl/style/expression/literal.hpp> #include <mbgl/style/expression/assertion.hpp> #include <mbgl/style/expression/coercion.hpp> @@ -28,6 +29,10 @@ static std::unique_ptr<Expression> compound(const char* op, Args... args) { return std::move(*result); } +std::unique_ptr<Expression> error(std::string message) { + return std::make_unique<Error>(std::move(message)); +} + std::unique_ptr<Expression> literal(const char* value) { return literal(std::string(value)); } @@ -60,6 +65,10 @@ std::unique_ptr<Expression> string(std::unique_ptr<Expression> value) { return std::make_unique<Assertion>(type::String, vec(std::move(value))); } +std::unique_ptr<Expression> boolean(std::unique_ptr<Expression> value) { + return std::make_unique<Assertion>(type::Boolean, vec(std::move(value))); +} + std::unique_ptr<Expression> toColor(const char* value) { return toColor(literal(value)); } diff --git a/src/mbgl/style/function/categorical_stops.cpp b/src/mbgl/style/function/categorical_stops.cpp deleted file mode 100644 index dd179f5376..0000000000 --- a/src/mbgl/style/function/categorical_stops.cpp +++ /dev/null @@ -1,41 +0,0 @@ -#include <mbgl/style/function/categorical_stops.hpp> -#include <mbgl/style/types.hpp> -#include <mbgl/util/color.hpp> - -#include <array> - -namespace mbgl { -namespace style { - -optional<CategoricalValue> categoricalValue(const Value& value) { - return value.match( - [] (bool t) { return optional<CategoricalValue>(t); }, - [] (uint64_t t) { return optional<CategoricalValue>(int64_t(t)); }, - [] (int64_t t) { return optional<CategoricalValue>(t); }, - [] (double t) { return optional<CategoricalValue>(int64_t(t)); }, - [] (const std::string& t) { return optional<CategoricalValue>(t); }, - [] (const auto&) { return optional<CategoricalValue>(); } - ); -} - -template <class T> -optional<T> CategoricalStops<T>::evaluate(const Value& value) const { - auto v = categoricalValue(value); - if (!v) { - return {}; - } - auto it = stops.find(*v); - return it == stops.end() ? optional<T>() : it->second; -} - -template class CategoricalStops<float>; -template class CategoricalStops<Color>; -template class CategoricalStops<std::array<float, 2>>; -template class CategoricalStops<std::string>; -template class CategoricalStops<TextTransformType>; -template class CategoricalStops<TextJustifyType>; -template class CategoricalStops<SymbolAnchorType>; -template class CategoricalStops<LineJoinType>; - -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/function/convert.cpp b/src/mbgl/style/function/convert.cpp deleted file mode 100644 index a3b19f287b..0000000000 --- a/src/mbgl/style/function/convert.cpp +++ /dev/null @@ -1,40 +0,0 @@ -#include <mbgl/style/function/convert.hpp> - -namespace mbgl { -namespace style { -namespace expression { - -std::unique_ptr<Expression> Convert::fromIdentityFunction(const std::string& property, type::Type type) { - return type.match( - [&] (const type::StringType&) { - return makeGet(type::String, property); - }, - [&] (const type::NumberType&) { - return makeGet(type::Number, property); - }, - [&] (const type::BooleanType&) { - return makeGet(type::Boolean, property); - }, - [&] (const type::ColorType&) { - std::vector<std::unique_ptr<Expression>> args; - args.push_back(makeGet(type::String, property)); - return std::make_unique<Coercion>(type::Color, std::move(args)); - }, - [&] (const type::Array& arr) { - std::vector<std::unique_ptr<Expression>> getArgs; - getArgs.push_back(makeLiteral(property)); - ParsingContext ctx; - ParseResult get = createCompoundExpression("get", std::move(getArgs), ctx); - assert(get); - assert(ctx.getErrors().size() == 0); - return std::make_unique<ArrayAssertion>(arr, std::move(*get)); - }, - [&] (const auto&) -> std::unique_ptr<Expression> { - return makeLiteral(Null); - } - ); -} - -} // namespace expression -} // namespace style -} // namespace mbgl diff --git a/src/mbgl/style/function/identity_stops.cpp b/src/mbgl/style/function/identity_stops.cpp deleted file mode 100644 index 0ac6fda846..0000000000 --- a/src/mbgl/style/function/identity_stops.cpp +++ /dev/null @@ -1,89 +0,0 @@ -#include <mbgl/style/function/identity_stops.hpp> -#include <mbgl/style/types.hpp> -#include <mbgl/util/enum.hpp> -#include <mbgl/util/color.hpp> - -#include <array> - -namespace mbgl { -namespace style { - -template <> -optional<float> IdentityStops<float>::evaluate(const Value& value) const { - return numericValue<float>(value); -} - -template <> -optional<std::string> IdentityStops<std::string>::evaluate(const Value& value) const { - if (!value.is<std::string>()) { - return {}; - } - - return value.get<std::string>(); -} - -template <> -optional<Color> IdentityStops<Color>::evaluate(const Value& value) const { - if (!value.is<std::string>()) { - return {}; - } - - return Color::parse(value.get<std::string>()); -} - -template <> -optional<TextTransformType> IdentityStops<TextTransformType>::evaluate(const Value& value) const { - if (!value.is<std::string>()) { - return {}; - } - - return Enum<TextTransformType>::toEnum(value.get<std::string>()); -} - -template <> -optional<TextJustifyType> IdentityStops<TextJustifyType>::evaluate(const Value& value) const { - if (!value.is<std::string>()) { - return {}; - } - - return Enum<TextJustifyType>::toEnum(value.get<std::string>()); -} - -template <> -optional<SymbolAnchorType> IdentityStops<SymbolAnchorType>::evaluate(const Value& value) const { - if (!value.is<std::string>()) { - return {}; - } - - return Enum<SymbolAnchorType>::toEnum(value.get<std::string>()); -} - -template <> -optional<LineJoinType> IdentityStops<LineJoinType>::evaluate(const Value& value) const { - if (!value.is<std::string>()) { - return {}; - } - - return Enum<LineJoinType>::toEnum(value.get<std::string>()); -} - -template <> -optional<std::array<float, 2>> IdentityStops<std::array<float, 2>>::evaluate(const Value& value) const { - if (!value.is<std::vector<Value>>()) { - return {}; - } - - const auto& vector = value.get<std::vector<Value>>(); - if (vector.size() != 2 || !numericValue<float>(vector[0]) || !numericValue<float>(vector[1])) { - return {}; - } - - std::array<float, 2> array {{ - *numericValue<float>(vector[0]), - *numericValue<float>(vector[1]) - }}; - return array; -} - -} // namespace style -} // namespace mbgl diff --git a/test/style/function/exponential_stops.test.cpp b/test/style/function/exponential_stops.test.cpp deleted file mode 100644 index 81438ec952..0000000000 --- a/test/style/function/exponential_stops.test.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include <mbgl/test/util.hpp> - -#include <mbgl/style/function/exponential_stops.hpp> - -using namespace mbgl; -using namespace mbgl::style; - -TEST(ExponentialStops, Empty) { - ExponentialStops<float> stops; - EXPECT_FALSE(bool(stops.evaluate(0))); -} - -TEST(ExponentialStops, NonNumericInput) { - ExponentialStops<float> stops(std::map<float, float> {{0.0f, 0.0f}}); - EXPECT_FALSE(bool(stops.evaluate(Value(NullValue())))); - EXPECT_FALSE(bool(stops.evaluate(Value(false)))); - EXPECT_FALSE(bool(stops.evaluate(Value(std::string())))); - EXPECT_FALSE(bool(stops.evaluate(Value(std::vector<Value>())))); - EXPECT_FALSE(bool(stops.evaluate(Value(std::unordered_map<std::string, Value>())))); -} diff --git a/test/style/function/interval_stops.test.cpp b/test/style/function/interval_stops.test.cpp deleted file mode 100644 index 8a5e74b8b6..0000000000 --- a/test/style/function/interval_stops.test.cpp +++ /dev/null @@ -1,20 +0,0 @@ -#include <mbgl/test/util.hpp> - -#include <mbgl/style/function/interval_stops.hpp> - -using namespace mbgl; -using namespace mbgl::style; - -TEST(IntervalStops, Empty) { - IntervalStops<float> stops; - EXPECT_FALSE(bool(stops.evaluate(0))); -} - -TEST(IntervalStops, NonNumericInput) { - IntervalStops<float> stops(std::map<float, float> {{0.0f, 0.0f}}); - EXPECT_FALSE(bool(stops.evaluate(Value(NullValue())))); - EXPECT_FALSE(bool(stops.evaluate(Value(false)))); - EXPECT_FALSE(bool(stops.evaluate(Value(std::string())))); - EXPECT_FALSE(bool(stops.evaluate(Value(std::vector<Value>())))); - EXPECT_FALSE(bool(stops.evaluate(Value(std::unordered_map<std::string, Value>())))); -} |