[core] Convert token strings to expressions
This commit is contained in:
@@ -33,7 +33,7 @@ static void Parse_CompositeFunction(benchmark::State& state) {
|
||||
state.PauseTiming();
|
||||
auto doc = createFunctionJSON(stopCount);
|
||||
state.ResumeTiming();
|
||||
optional<DataDrivenPropertyValue<float>> result = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error);
|
||||
optional<DataDrivenPropertyValue<float>> result = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error, false);
|
||||
if (!result) {
|
||||
state.SkipWithError(error.message.c_str());
|
||||
}
|
||||
@@ -45,7 +45,7 @@ static void Evaluate_CompositeFunction(benchmark::State& state) {
|
||||
size_t stopCount = state.range(0);
|
||||
auto doc = createFunctionJSON(stopCount);
|
||||
conversion::Error error;
|
||||
optional<DataDrivenPropertyValue<float>> function = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error);
|
||||
optional<DataDrivenPropertyValue<float>> function = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error, false);
|
||||
if (!function) {
|
||||
state.SkipWithError(error.message.c_str());
|
||||
}
|
||||
|
||||
@@ -28,7 +28,7 @@ static void Parse_SourceFunction(benchmark::State& state) {
|
||||
state.PauseTiming();
|
||||
auto doc = createFunctionJSON(stopCount);
|
||||
state.ResumeTiming();
|
||||
optional<DataDrivenPropertyValue<float>> result = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error);
|
||||
optional<DataDrivenPropertyValue<float>> result = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error, false);
|
||||
if (!result) {
|
||||
state.SkipWithError(error.message.c_str());
|
||||
}
|
||||
@@ -40,7 +40,7 @@ static void Evaluate_SourceFunction(benchmark::State& state) {
|
||||
size_t stopCount = state.range(0);
|
||||
auto doc = createFunctionJSON(stopCount);
|
||||
conversion::Error error;
|
||||
optional<DataDrivenPropertyValue<float>> function = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error);
|
||||
optional<DataDrivenPropertyValue<float>> function = conversion::convertJSON<DataDrivenPropertyValue<float>>(doc, error, false);
|
||||
if (!function) {
|
||||
state.SkipWithError(error.message.c_str());
|
||||
}
|
||||
|
||||
@@ -14,7 +14,7 @@ namespace conversion {
|
||||
|
||||
template <>
|
||||
struct Converter<ColorRampPropertyValue> {
|
||||
optional<ColorRampPropertyValue> operator()(const Convertible& value, Error& error) const {
|
||||
optional<ColorRampPropertyValue> operator()(const Convertible& value, Error& error, bool /* convertTokens */ = false) const {
|
||||
using namespace mbgl::style::expression;
|
||||
if (isUndefined(value)) {
|
||||
return ColorRampPropertyValue();
|
||||
|
||||
@@ -16,7 +16,7 @@ namespace conversion {
|
||||
|
||||
template <class T>
|
||||
struct Converter<DataDrivenPropertyValue<T>> {
|
||||
optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error) const {
|
||||
optional<DataDrivenPropertyValue<T>> operator()(const Convertible& value, Error& error, bool convertTokens) const {
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
if (isUndefined(value)) {
|
||||
@@ -34,13 +34,13 @@ struct Converter<DataDrivenPropertyValue<T>> {
|
||||
}
|
||||
expression = PropertyExpression<T>(std::move(*parsed));
|
||||
} else if (isObject(value)) {
|
||||
expression = convertFunctionToExpression<T>(value, error);
|
||||
expression = convertFunctionToExpression<T>(value, error, convertTokens);
|
||||
} else {
|
||||
optional<T> constant = convert<T>(value, error);
|
||||
if (!constant) {
|
||||
return {};
|
||||
}
|
||||
return DataDrivenPropertyValue<T>(*constant);
|
||||
return convertTokens ? maybeConvertTokens(*constant) : DataDrivenPropertyValue<T>(*constant);
|
||||
}
|
||||
|
||||
if (!expression) {
|
||||
@@ -56,6 +56,17 @@ struct Converter<DataDrivenPropertyValue<T>> {
|
||||
return DataDrivenPropertyValue<T>(*constant);
|
||||
}
|
||||
}
|
||||
|
||||
template <class S>
|
||||
DataDrivenPropertyValue<T> maybeConvertTokens(const S& t) const {
|
||||
return DataDrivenPropertyValue<T>(t);
|
||||
};
|
||||
|
||||
DataDrivenPropertyValue<T> maybeConvertTokens(const std::string& t) const {
|
||||
return hasTokens(t)
|
||||
? DataDrivenPropertyValue<T>(PropertyExpression<T>(convertTokenStringToExpression(t)))
|
||||
: DataDrivenPropertyValue<T>(t);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace conversion
|
||||
|
||||
@@ -10,11 +10,14 @@ namespace mbgl {
|
||||
namespace style {
|
||||
namespace conversion {
|
||||
|
||||
optional<std::unique_ptr<expression::Expression>> convertFunctionToExpression(expression::type::Type, const Convertible&, Error&);
|
||||
bool hasTokens(const std::string&);
|
||||
std::unique_ptr<expression::Expression> convertTokenStringToExpression(const std::string&);
|
||||
|
||||
optional<std::unique_ptr<expression::Expression>> convertFunctionToExpression(expression::type::Type, const Convertible&, Error&, bool convertTokens);
|
||||
|
||||
template <class T>
|
||||
optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error) {
|
||||
auto expression = convertFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error);
|
||||
optional<PropertyExpression<T>> convertFunctionToExpression(const Convertible& value, Error& error, bool convertTokens) {
|
||||
auto expression = convertFunctionToExpression(expression::valueTypeToExpressionType<T>(), value, error, convertTokens);
|
||||
if (!expression) {
|
||||
return {};
|
||||
}
|
||||
|
||||
@@ -16,9 +16,13 @@ namespace conversion {
|
||||
|
||||
template <class T>
|
||||
struct Converter<PropertyValue<T>> {
|
||||
optional<PropertyValue<T>> operator()(const Convertible& value, Error& error) const {
|
||||
optional<PropertyValue<T>> operator()(const Convertible& value, Error& error, bool convertTokens = false) const {
|
||||
using namespace mbgl::style::expression;
|
||||
|
||||
// Only icon-image and text-field support tokens, and they are both data-driven.
|
||||
assert(!convertTokens);
|
||||
(void)convertTokens;
|
||||
|
||||
if (isUndefined(value)) {
|
||||
return PropertyValue<T>();
|
||||
}
|
||||
@@ -34,7 +38,7 @@ struct Converter<PropertyValue<T>> {
|
||||
}
|
||||
expression = PropertyExpression<T>(std::move(*parsed));
|
||||
} else if (isObject(value)) {
|
||||
expression = convertFunctionToExpression<T>(value, error);
|
||||
expression = convertFunctionToExpression<T>(value, error, false);
|
||||
} else {
|
||||
optional<T> constant = convert<T>(value, error);
|
||||
if (!constant) {
|
||||
|
||||
@@ -52,14 +52,6 @@ public:
|
||||
);
|
||||
}
|
||||
|
||||
bool isExpression() const {
|
||||
return value.match(
|
||||
[] (const Undefined&) { return false; },
|
||||
[] (const T&) { return false; },
|
||||
[] (const PropertyExpression<T>& fn) { return fn.isExpression; }
|
||||
);
|
||||
}
|
||||
|
||||
const T & asConstant() const { return value.template get< T >(); }
|
||||
const PropertyExpression<T>& asExpression() const { return value.template get<PropertyExpression<T>>(); }
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <mbgl/style/expression/value.hpp>
|
||||
#include <mbgl/style/expression/expression.hpp>
|
||||
#include <mbgl/style/expression/interpolator.hpp>
|
||||
#include <mbgl/util/ignore.hpp>
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
@@ -16,6 +17,13 @@ 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.
|
||||
|
||||
template <class... Args>
|
||||
std::vector<std::unique_ptr<Expression>> vec(Args... args) {
|
||||
std::vector<std::unique_ptr<Expression>> result;
|
||||
util::ignore({ (result.push_back(std::move(args)), 0)... });
|
||||
return result;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> error(std::string);
|
||||
|
||||
std::unique_ptr<Expression> literal(const char* value);
|
||||
@@ -27,8 +35,8 @@ 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>);
|
||||
std::unique_ptr<Expression> toString(std::unique_ptr<Expression>);
|
||||
|
||||
std::unique_ptr<Expression> get(const char* value);
|
||||
std::unique_ptr<Expression> get(std::unique_ptr<Expression>);
|
||||
@@ -68,6 +76,8 @@ std::unique_ptr<Expression> interpolate(Interpolator interpolator,
|
||||
double input2, std::unique_ptr<Expression> output2,
|
||||
double input3, std::unique_ptr<Expression> output3);
|
||||
|
||||
std::unique_ptr<Expression> concat(std::vector<std::unique_ptr<Expression>> inputs);
|
||||
|
||||
} // namespace dsl
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
|
||||
@@ -14,16 +14,9 @@ namespace style {
|
||||
template <class T>
|
||||
class PropertyExpression {
|
||||
public:
|
||||
PropertyExpression(std::unique_ptr<expression::Expression> expression_)
|
||||
: isExpression(true),
|
||||
expression(std::move(expression_)),
|
||||
zoomCurve(expression::findZoomCurveChecked(expression.get())) {
|
||||
}
|
||||
|
||||
// To be used only for conversions from legacy functions.
|
||||
PropertyExpression(std::unique_ptr<expression::Expression> expression_, optional<T> defaultValue_)
|
||||
: isExpression(false),
|
||||
expression(std::move(expression_)),
|
||||
// Second parameter to be used only for conversions from legacy functions.
|
||||
PropertyExpression(std::unique_ptr<expression::Expression> expression_, optional<T> defaultValue_ = {})
|
||||
: expression(std::move(expression_)),
|
||||
defaultValue(std::move(defaultValue_)),
|
||||
zoomCurve(expression::findZoomCurveChecked(expression.get())) {
|
||||
}
|
||||
@@ -108,7 +101,6 @@ public:
|
||||
const expression::Expression& getExpression() const { return *expression; }
|
||||
|
||||
bool useIntegerZoom = false;
|
||||
bool isExpression;
|
||||
|
||||
friend bool operator==(const PropertyExpression& lhs,
|
||||
const PropertyExpression& rhs) {
|
||||
|
||||
@@ -86,7 +86,7 @@ public:
|
||||
|
||||
mbgl::style::conversion::Error valueError;
|
||||
auto value = mbgl::style::conversion::convert<MBGLValue>(
|
||||
mbgl::style::conversion::makeConvertible(jsonExpression), valueError);
|
||||
mbgl::style::conversion::makeConvertible(jsonExpression), valueError, false);
|
||||
if (!value) {
|
||||
[NSException raise:NSInvalidArgumentException
|
||||
format:@"Invalid property value: %@", @(valueError.message.c_str())];
|
||||
|
||||
@@ -639,9 +639,9 @@ void GLFWView::toggle3DExtrusions(bool visible) {
|
||||
extrusionLayer->setFilter(Filter(eq(get("extrude"), literal("true"))));
|
||||
extrusionLayer->setFillExtrusionColor(PropertyExpression<mbgl::Color>(
|
||||
interpolate(linear(), number(get("height")),
|
||||
0.f, toColor("#160e23"),
|
||||
50.f, toColor("#00615f"),
|
||||
100.f, toColor("#55e9ff"))));
|
||||
0.f, toColor(literal("#160e23")),
|
||||
50.f, toColor(literal("#00615f")),
|
||||
100.f, toColor(literal("#55e9ff")))));
|
||||
extrusionLayer->setFillExtrusionOpacity(0.6f);
|
||||
extrusionLayer->setFillExtrusionHeight(PropertyExpression<float>(get("height")));
|
||||
extrusionLayer->setFillExtrusionBase(PropertyExpression<float>(get("min_height")));
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <mbgl/style/style_impl.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer_impl.hpp>
|
||||
#include <mbgl/style/expression/dsl.hpp>
|
||||
#include <mbgl/storage/file_source.hpp>
|
||||
|
||||
#include <boost/function_output_iterator.hpp>
|
||||
@@ -160,8 +161,9 @@ void AnnotationManager::updateStyle() {
|
||||
|
||||
std::unique_ptr<SymbolLayer> layer = std::make_unique<SymbolLayer>(PointLayerID, SourceID);
|
||||
|
||||
using namespace expression::dsl;
|
||||
layer->setSourceLayer(PointLayerID);
|
||||
layer->setIconImage({SourceID + ".{sprite}"});
|
||||
layer->setIconImage(PropertyExpression<std::string>(concat(vec(literal(SourceID + "."), toString(get("sprite"))))));
|
||||
layer->setIconAllowOverlap(true);
|
||||
layer->setIconIgnorePlacement(true);
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@
|
||||
#include <mbgl/text/shaping.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/utf.hpp>
|
||||
#include <mbgl/util/token.hpp>
|
||||
#include <mbgl/util/std.hpp>
|
||||
#include <mbgl/util/constants.hpp>
|
||||
#include <mbgl/util/string.hpp>
|
||||
@@ -106,31 +105,10 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
|
||||
|
||||
ft.index = i;
|
||||
|
||||
auto getValue = [&ft](const std::string& key) -> std::string {
|
||||
auto value = ft.getValue(key);
|
||||
if (!value)
|
||||
return std::string();
|
||||
if (value->is<std::string>())
|
||||
return value->get<std::string>();
|
||||
if (value->is<bool>())
|
||||
return value->get<bool>() ? "true" : "false";
|
||||
if (value->is<int64_t>())
|
||||
return util::toString(value->get<int64_t>());
|
||||
if (value->is<uint64_t>())
|
||||
return util::toString(value->get<uint64_t>());
|
||||
if (value->is<double>())
|
||||
return util::toString(value->get<double>());
|
||||
return "null";
|
||||
};
|
||||
|
||||
if (hasText) {
|
||||
std::string u8string = layout.evaluate<TextField>(zoom, ft);
|
||||
if (layout.get<TextField>().isConstant() && !leader.layout.get<TextField>().isExpression()) {
|
||||
u8string = util::replaceTokens(u8string, getValue);
|
||||
}
|
||||
|
||||
auto textTransform = layout.evaluate<TextTransform>(zoom, ft);
|
||||
|
||||
auto textTransform = layout.evaluate<TextTransform>(zoom, ft);
|
||||
if (textTransform == TextTransformType::Uppercase) {
|
||||
u8string = platform::uppercase(u8string);
|
||||
} else if (textTransform == TextTransformType::Lowercase) {
|
||||
@@ -157,11 +135,7 @@ SymbolLayout::SymbolLayout(const BucketParameters& parameters,
|
||||
}
|
||||
|
||||
if (hasIcon) {
|
||||
std::string icon = layout.evaluate<IconImage>(zoom, ft);
|
||||
if (layout.get<IconImage>().isConstant() && !leader.layout.get<IconImage>().isExpression()) {
|
||||
icon = util::replaceTokens(icon, getValue);
|
||||
}
|
||||
ft.icon = icon;
|
||||
ft.icon = layout.evaluate<IconImage>(zoom, ft);
|
||||
imageDependencies.insert(*ft.icon);
|
||||
}
|
||||
|
||||
|
||||
@@ -16,6 +16,60 @@ namespace conversion {
|
||||
using namespace expression;
|
||||
using namespace expression::dsl;
|
||||
|
||||
const static std::string tokenReservedChars = "{}";
|
||||
|
||||
bool hasTokens(const std::string& source) {
|
||||
auto pos = source.begin();
|
||||
const auto end = source.end();
|
||||
|
||||
while (pos != end) {
|
||||
auto brace = std::find(pos, end, '{');
|
||||
if (brace == end)
|
||||
return false;
|
||||
for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++);
|
||||
if (brace != end && *brace == '}') {
|
||||
return true;
|
||||
}
|
||||
pos = brace;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> convertTokenStringToExpression(const std::string& source) {
|
||||
std::vector<std::unique_ptr<Expression>> inputs;
|
||||
|
||||
auto pos = source.begin();
|
||||
const auto end = source.end();
|
||||
|
||||
while (pos != end) {
|
||||
auto brace = std::find(pos, end, '{');
|
||||
if (pos != brace) {
|
||||
inputs.push_back(literal(std::string(pos, brace)));
|
||||
}
|
||||
pos = brace;
|
||||
if (pos != end) {
|
||||
for (brace++; brace != end && tokenReservedChars.find(*brace) == std::string::npos; brace++);
|
||||
if (brace != end && *brace == '}') {
|
||||
inputs.push_back(toString(get(literal(std::string(pos + 1, brace)))));
|
||||
pos = brace + 1;
|
||||
} else {
|
||||
inputs.push_back(literal(std::string(pos, brace)));
|
||||
pos = brace;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch (inputs.size()) {
|
||||
case 0:
|
||||
return literal(source);
|
||||
case 1:
|
||||
return std::move(inputs[0]);
|
||||
default:
|
||||
return concat(std::move(inputs));
|
||||
}
|
||||
}
|
||||
|
||||
// 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).
|
||||
@@ -66,7 +120,7 @@ static bool interpolatable(type::Type type) {
|
||||
);
|
||||
}
|
||||
|
||||
static optional<std::unique_ptr<Expression>> convertLiteral(type::Type type, const Convertible& value, Error& error) {
|
||||
static optional<std::unique_ptr<Expression>> convertLiteral(type::Type type, const Convertible& value, Error& error, bool convertTokens = false) {
|
||||
return type.match(
|
||||
[&] (const type::NumberType&) -> optional<std::unique_ptr<Expression>> {
|
||||
auto result = convert<float>(value, error);
|
||||
@@ -87,7 +141,7 @@ static optional<std::unique_ptr<Expression>> convertLiteral(type::Type type, con
|
||||
if (!result) {
|
||||
return {};
|
||||
}
|
||||
return literal(*result);
|
||||
return convertTokens ? convertTokenStringToExpression(*result) : literal(*result);
|
||||
},
|
||||
[&] (const type::ColorType&) -> optional<std::unique_ptr<Expression>> {
|
||||
auto result = convert<Color>(value, error);
|
||||
@@ -163,7 +217,8 @@ static optional<std::unique_ptr<Expression>> convertLiteral(type::Type type, con
|
||||
|
||||
static optional<std::map<double, std::unique_ptr<Expression>>> convertStops(type::Type type,
|
||||
const Convertible& value,
|
||||
Error& error) {
|
||||
Error& error,
|
||||
bool convertTokens) {
|
||||
auto stopsValue = objectMember(value, "stops");
|
||||
if (!stopsValue) {
|
||||
error = { "function value must specify stops" };
|
||||
@@ -199,7 +254,7 @@ static optional<std::map<double, std::unique_ptr<Expression>>> convertStops(type
|
||||
return {};
|
||||
}
|
||||
|
||||
optional<std::unique_ptr<Expression>> e = convertLiteral(type, arrayMember(stopValue, 1), error);
|
||||
optional<std::unique_ptr<Expression>> e = convertLiteral(type, arrayMember(stopValue, 1), error, convertTokens);
|
||||
if (!e) {
|
||||
return {};
|
||||
}
|
||||
@@ -320,8 +375,9 @@ std::unique_ptr<Expression> categorical<bool>(type::Type type, const std::string
|
||||
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);
|
||||
std::unique_ptr<Expression> input,
|
||||
bool convertTokens = false) {
|
||||
auto stops = convertStops(type, value, error, convertTokens);
|
||||
if (!stops) {
|
||||
return {};
|
||||
}
|
||||
@@ -331,8 +387,9 @@ static optional<std::unique_ptr<Expression>> convertIntervalFunction(type::Type
|
||||
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);
|
||||
std::unique_ptr<Expression> input,
|
||||
bool convertTokens = false) {
|
||||
auto stops = convertStops(type, value, error, convertTokens);
|
||||
if (!stops) {
|
||||
return {};
|
||||
}
|
||||
@@ -486,7 +543,8 @@ optional<std::unique_ptr<Expression>> composite(type::Type type,
|
||||
|
||||
optional<std::unique_ptr<Expression>> convertFunctionToExpression(type::Type type,
|
||||
const Convertible& value,
|
||||
Error& err) {
|
||||
Error& err,
|
||||
bool convertTokens) {
|
||||
if (!isObject(value)) {
|
||||
err = { "function must be an object" };
|
||||
return {};
|
||||
@@ -515,9 +573,9 @@ optional<std::unique_ptr<Expression>> convertFunctionToExpression(type::Type typ
|
||||
// Camera function.
|
||||
switch (functionType) {
|
||||
case FunctionType::Interval:
|
||||
return convertIntervalFunction(type, value, err, zoom());
|
||||
return convertIntervalFunction(type, value, err, zoom(), convertTokens);
|
||||
case FunctionType::Exponential:
|
||||
return convertExponentialFunction(type, value, err, zoom());
|
||||
return convertExponentialFunction(type, value, err, zoom(), convertTokens);
|
||||
default:
|
||||
err = { "unsupported function type" };
|
||||
return {};
|
||||
|
||||
@@ -18,7 +18,7 @@ optional<Light> Converter<Light>::operator()(const Convertible& value, Error& er
|
||||
const auto anchor = objectMember(value, "anchor");
|
||||
if (anchor) {
|
||||
optional<PropertyValue<LightAnchorType>> convertedAnchor =
|
||||
convert<PropertyValue<LightAnchorType>>(*anchor, error);
|
||||
convert<PropertyValue<LightAnchorType>>(*anchor, error, false);
|
||||
|
||||
if (convertedAnchor) {
|
||||
light.setAnchor(*convertedAnchor);
|
||||
@@ -41,7 +41,7 @@ optional<Light> Converter<Light>::operator()(const Convertible& value, Error& er
|
||||
const auto color = objectMember(value, "color");
|
||||
if (color) {
|
||||
optional<PropertyValue<Color>> convertedColor =
|
||||
convert<PropertyValue<Color>>(*color, error);
|
||||
convert<PropertyValue<Color>>(*color, error, false);
|
||||
|
||||
if (convertedColor) {
|
||||
light.setColor(*convertedColor);
|
||||
@@ -64,7 +64,7 @@ optional<Light> Converter<Light>::operator()(const Convertible& value, Error& er
|
||||
const auto position = objectMember(value, "position");
|
||||
if (position) {
|
||||
optional<PropertyValue<Position>> convertedPosition =
|
||||
convert<PropertyValue<Position>>(*position, error);
|
||||
convert<PropertyValue<Position>>(*position, error, false);
|
||||
|
||||
if (convertedPosition) {
|
||||
light.setPosition(*convertedPosition);
|
||||
@@ -87,7 +87,7 @@ optional<Light> Converter<Light>::operator()(const Convertible& value, Error& er
|
||||
const auto intensity = objectMember(value, "intensity");
|
||||
if (intensity) {
|
||||
optional<PropertyValue<float>> convertedIntensity =
|
||||
convert<PropertyValue<float>>(*intensity, error);
|
||||
convert<PropertyValue<float>>(*intensity, error, false);
|
||||
|
||||
if (convertedIntensity) {
|
||||
light.setIntensity(*convertedIntensity);
|
||||
|
||||
@@ -41,7 +41,7 @@ inline auto makeLayoutPropertySetters() {
|
||||
result["icon-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconSize>;
|
||||
result["icon-text-fit"] = &setProperty<SymbolLayer, PropertyValue<IconTextFitType>, &SymbolLayer::setIconTextFit>;
|
||||
result["icon-text-fit-padding"] = &setProperty<SymbolLayer, PropertyValue<std::array<float, 4>>, &SymbolLayer::setIconTextFitPadding>;
|
||||
result["icon-image"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage>;
|
||||
result["icon-image"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setIconImage, true>;
|
||||
result["icon-rotate"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setIconRotate>;
|
||||
result["icon-padding"] = &setProperty<SymbolLayer, PropertyValue<float>, &SymbolLayer::setIconPadding>;
|
||||
result["icon-keep-upright"] = &setProperty<SymbolLayer, PropertyValue<bool>, &SymbolLayer::setIconKeepUpright>;
|
||||
@@ -50,7 +50,7 @@ inline auto makeLayoutPropertySetters() {
|
||||
result["icon-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setIconPitchAlignment>;
|
||||
result["text-pitch-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextPitchAlignment>;
|
||||
result["text-rotation-alignment"] = &setProperty<SymbolLayer, PropertyValue<AlignmentType>, &SymbolLayer::setTextRotationAlignment>;
|
||||
result["text-field"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField>;
|
||||
result["text-field"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::string>, &SymbolLayer::setTextField, true>;
|
||||
result["text-font"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<std::vector<std::string>>, &SymbolLayer::setTextFont>;
|
||||
result["text-size"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextSize>;
|
||||
result["text-max-width"] = &setProperty<SymbolLayer, DataDrivenPropertyValue<float>, &SymbolLayer::setTextMaxWidth>;
|
||||
|
||||
@@ -21,7 +21,7 @@ inline auto makeLayoutPropertySetters() {
|
||||
|
||||
<% for (const layer of locals.layers) { -%>
|
||||
<% for (const property of layer.layoutProperties) { -%>
|
||||
result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %>>;
|
||||
result["<%- property.name %>"] = &setProperty<<%- camelize(layer.type) %>Layer, <%- propertyValueType(property) %>, &<%- camelize(layer.type) %>Layer::set<%- camelize(property.name) %><%- property.name === 'icon-image' || property.name === 'text-field' ? ', true' : '' %>>;
|
||||
<% } -%>
|
||||
|
||||
<% } -%>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <mbgl/style/layer.hpp>
|
||||
#include <mbgl/style/layers/symbol_layer.hpp>
|
||||
#include <mbgl/style/conversion.hpp>
|
||||
#include <mbgl/style/conversion/color_ramp_property_value.hpp>
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
@@ -16,7 +17,7 @@ namespace conversion {
|
||||
|
||||
using PropertySetter = optional<Error> (*) (Layer&, const Convertible&);
|
||||
|
||||
template <class L, class PropertyValue, void (L::*setter)(PropertyValue)>
|
||||
template <class L, class PropertyValue, void (L::*setter)(PropertyValue), bool convertTokens = false>
|
||||
optional<Error> setProperty(Layer& layer, const Convertible& value) {
|
||||
auto* typedLayer = layer.as<L>();
|
||||
if (!typedLayer) {
|
||||
@@ -24,7 +25,7 @@ optional<Error> setProperty(Layer& layer, const Convertible& value) {
|
||||
}
|
||||
|
||||
Error error;
|
||||
optional<PropertyValue> typedValue = convert<PropertyValue>(value, error);
|
||||
optional<PropertyValue> typedValue = convert<PropertyValue>(value, error, convertTokens);
|
||||
if (!typedValue) {
|
||||
return error;
|
||||
}
|
||||
|
||||
@@ -7,26 +7,22 @@
|
||||
#include <mbgl/style/expression/step.hpp>
|
||||
#include <mbgl/style/expression/interpolate.hpp>
|
||||
#include <mbgl/style/expression/compound_expression.hpp>
|
||||
#include <mbgl/util/ignore.hpp>
|
||||
|
||||
namespace mbgl {
|
||||
namespace style {
|
||||
namespace expression {
|
||||
namespace dsl {
|
||||
|
||||
template <class... Args>
|
||||
static std::vector<std::unique_ptr<Expression>> vec(Args... args) {
|
||||
std::vector<std::unique_ptr<Expression>> result;
|
||||
util::ignore({ (result.push_back(std::move(args)), 0)... });
|
||||
return result;
|
||||
static std::unique_ptr<Expression> compound(const char* op, std::vector<std::unique_ptr<Expression>> args) {
|
||||
ParsingContext ctx;
|
||||
ParseResult result = createCompoundExpression(op, std::move(args), ctx);
|
||||
assert(result);
|
||||
return std::move(*result);
|
||||
}
|
||||
|
||||
template <class... Args>
|
||||
static std::unique_ptr<Expression> compound(const char* op, Args... args) {
|
||||
ParsingContext ctx;
|
||||
ParseResult result = createCompoundExpression(op, vec(std::move(args)...), ctx);
|
||||
assert(result);
|
||||
return std::move(*result);
|
||||
return compound(op, vec(std::move(args)...));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> error(std::string message) {
|
||||
@@ -69,14 +65,14 @@ 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));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> toColor(std::unique_ptr<Expression> value) {
|
||||
return std::make_unique<Coercion>(type::Color, vec(std::move(value)));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> toString(std::unique_ptr<Expression> value) {
|
||||
return compound("to-string", std::move(value));
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> get(const char* value) {
|
||||
return get(literal(value));
|
||||
}
|
||||
@@ -177,6 +173,10 @@ std::unique_ptr<Expression> interpolate(Interpolator interpolator,
|
||||
return std::move(*result);
|
||||
}
|
||||
|
||||
std::unique_ptr<Expression> concat(std::vector<std::unique_ptr<Expression>> inputs) {
|
||||
return compound("concat", std::move(inputs));
|
||||
}
|
||||
|
||||
} // namespace dsl
|
||||
} // namespace expression
|
||||
} // namespace style
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <mbgl/style/conversion/constant.hpp>
|
||||
#include <mbgl/style/conversion/property_value.hpp>
|
||||
#include <mbgl/style/conversion/data_driven_property_value.hpp>
|
||||
#include <mbgl/style/expression/dsl.hpp>
|
||||
|
||||
using namespace mbgl;
|
||||
using namespace mbgl::style;
|
||||
@@ -56,7 +57,7 @@ TEST(StyleConversion, CompositeFunctionExpression) {
|
||||
Error error;
|
||||
|
||||
auto parseFunction = [&](const std::string& json) {
|
||||
return convertJSON<DataDrivenPropertyValue<float>>(json, error);
|
||||
return convertJSON<DataDrivenPropertyValue<float>>(json, error, false);
|
||||
};
|
||||
|
||||
auto fn1 = parseFunction(R"(["interpolate", ["linear"], ["zoom"], 0, ["number", ["get", "x"]], 10, 10])");
|
||||
@@ -75,3 +76,20 @@ TEST(StyleConversion, CompositeFunctionExpression) {
|
||||
ASSERT_FALSE(fn5);
|
||||
ASSERT_EQ(R"("zoom" expression may only be used as input to a top-level "step" or "interpolate" expression.)", error.message);
|
||||
}
|
||||
|
||||
TEST(StyleConversion, TokenStrings) {
|
||||
ASSERT_FALSE(hasTokens(""));
|
||||
ASSERT_FALSE(hasTokens("{"));
|
||||
ASSERT_FALSE(hasTokens("{token"));
|
||||
ASSERT_TRUE(hasTokens("{token}"));
|
||||
ASSERT_TRUE(hasTokens("token {token}"));
|
||||
ASSERT_TRUE(hasTokens("{token} {token}"));
|
||||
|
||||
using namespace mbgl::style::expression::dsl;
|
||||
ASSERT_EQ(*convertTokenStringToExpression("{token}"), *toString(get(literal("token"))));
|
||||
ASSERT_EQ(*convertTokenStringToExpression("token {token}"), *concat(vec(literal("token "), toString(get(literal("token"))))));
|
||||
ASSERT_EQ(*convertTokenStringToExpression("{token} token"), *concat(vec(toString(get(literal("token"))), literal(" token"))));
|
||||
ASSERT_EQ(*convertTokenStringToExpression("{token} {token}"), *concat(vec(toString(get(literal("token"))), literal(" "), toString(get(literal("token"))))));
|
||||
ASSERT_EQ(*convertTokenStringToExpression("{token} {token"), *concat(vec(toString(get(literal("token"))), literal(" "), literal("{token"))));
|
||||
ASSERT_EQ(*convertTokenStringToExpression("{token {token}"), *concat(vec(literal("{token "), toString(get(literal("token"))))));
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user