[core] Convert legacy functions directly to expressions
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
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<CameraFunction<T>> {
|
||||
optional<CameraFunction<T>> operator()(const Convertible& value, Error& error) const {
|
||||
auto expression = convertCameraFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error);
|
||||
if (!expression) {
|
||||
return {};
|
||||
}
|
||||
return CameraFunction<T>(std::move(*expression), false);
|
||||
}
|
||||
};
|
||||
|
||||
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" };
|
||||
auto expression = convertSourceFunctionToExpression(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<
|
||||
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 };
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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>);
|
||||
|
||||
39
include/mbgl/style/expression/error.hpp
Normal file
39
include/mbgl/style/expression/error.hpp
Normal file
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
699
src/mbgl/style/conversion/function.cpp
Normal file
699
src/mbgl/style/conversion/function.cpp
Normal file
@@ -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
|
||||
@@ -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));
|
||||
}
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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
|
||||
@@ -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>()))));
|
||||
}
|
||||
@@ -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>()))));
|
||||
}
|
||||
Reference in New Issue
Block a user