[core] Implement image expression (#15877)

* [core] Bump gl-js version
* [core] Implement image expression
* [core] Use new image expression
* [core] Coerce image expression to / from string
* [core] Serialize evaluated image
* [core] Pass available images to layout
* [core] Pass images to evaluation context
* [core] Set available flag value based on image availability
* [core] Allow image coercion to boolean to indicate image availability
* [core] Coalesce image expression
* [core] Add image expression to next build system
* [core] Align serialization format and evaluated type with gl-js
* [core] Add images to expression evaluation method
* [core] Add support for Image expression to expression test runner
* [core] Unskip image expression tests
* [core] Update unit tests
* [core] Use image expression in annotation manager
* [core] Add string to ImageExpression conversion
* [core] Add image expression to expression dsl
* [core] Convert tokens for implicitly created Image literal
* [core] Fix clang format
* [core] Split generated style code lines that are over 120 characters
* [core] Add unit test for image expression equality
* [core] Add image property expression evaluation unit test
* [core] Unskip image expression render test
* [core] Skip 'in' expression tests
* [core] Ignore fill-pattern/update-feature-state render test
* [core] Rename Image::serialize to Image::toValue
This commit is contained in:
Alexander Shalamov
2019-10-30 10:21:17 +02:00
parent e1556fc539
commit c6f3cc8b60
86 changed files with 1056 additions and 531 deletions

View File

@@ -12,6 +12,7 @@ namespace conversion {
bool hasTokens(const std::string&);
std::unique_ptr<expression::Expression> convertTokenStringToFormatExpression(const std::string&);
std::unique_ptr<expression::Expression> convertTokenStringToImageExpression(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);

View File

@@ -39,6 +39,12 @@ struct Converter<PropertyValue<T>> {
? PropertyValue<T>(PropertyExpression<T>(convertTokenStringToFormatExpression(firstUnformattedSection)))
: PropertyValue<T>(t);
}
PropertyValue<T> maybeConvertTokens(const expression::Image& image) const {
return hasTokens(image.id())
? PropertyValue<T>(PropertyExpression<T>(convertTokenStringToImageExpression(image.id())))
: PropertyValue<T>(image);
}
};
} // namespace conversion

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/color_ramp_property_value.hpp>
#include <mbgl/style/conversion.hpp>
#include <mbgl/style/expression/image.hpp>
#include <mbgl/style/layer.hpp>
#include <mbgl/style/property_value.hpp>
#include <mbgl/style/transition_options.hpp>

View File

@@ -49,6 +49,7 @@ std::unique_ptr<Expression> toString(std::unique_ptr<Expression>,
std::unique_ptr<Expression> def = nullptr);
std::unique_ptr<Expression> toFormatted(std::unique_ptr<Expression>,
std::unique_ptr<Expression> def = nullptr);
std::unique_ptr<Expression> toImage(std::unique_ptr<Expression>, std::unique_ptr<Expression> def = nullptr);
std::unique_ptr<Expression> get(const char* value);
std::unique_ptr<Expression> get(std::unique_ptr<Expression>);
@@ -89,6 +90,9 @@ std::unique_ptr<Expression> concat(std::vector<std::unique_ptr<Expression>> inpu
std::unique_ptr<Expression> format(const char* value);
std::unique_ptr<Expression> format(std::unique_ptr<Expression>);
std::unique_ptr<Expression> image(const char* value);
std::unique_ptr<Expression> image(std::unique_ptr<Expression>);
} // namespace dsl
} // namespace expression
} // namespace style

View File

@@ -28,9 +28,7 @@ public:
EvaluationContext() = default;
explicit EvaluationContext(float zoom_) : zoom(zoom_) {}
explicit EvaluationContext(GeometryTileFeature const * feature_) : feature(feature_) {}
EvaluationContext(float zoom_, GeometryTileFeature const * feature_) :
zoom(zoom_), feature(feature_)
{}
EvaluationContext(float zoom_, GeometryTileFeature const* feature_) : zoom(zoom_), feature(feature_) {}
EvaluationContext(optional<mbgl::Value> accumulated_, GeometryTileFeature const * feature_) :
accumulated(std::move(accumulated_)), feature(feature_)
{}
@@ -50,6 +48,11 @@ public:
return *this;
};
EvaluationContext& withAvailableImages(const std::set<std::string>* availableImages_) noexcept {
availableImages = availableImages_;
return *this;
};
optional<float> zoom;
optional<mbgl::Value> accumulated;
GeometryTileFeature const * feature = nullptr;
@@ -57,6 +60,7 @@ public:
// Contains formatted section object, std::unordered_map<std::string, Value>.
const Value* formattedSection = nullptr;
const FeatureState* featureState = nullptr;
const std::set<std::string>* availableImages = nullptr;
};
template <typename T>
@@ -155,7 +159,8 @@ enum class Kind : int32_t {
Comparison,
FormatExpression,
FormatSectionOverride,
NumberFormat
NumberFormat,
ImageExpression
};
class Expression {
@@ -172,9 +177,14 @@ public:
Kind getKind() const { return kind; };
type::Type getType() const { return type; };
EvaluationResult evaluate(optional<float> zoom, const Feature& feature, optional<double> colorRampParameter) const;
EvaluationResult evaluate(optional<float> zoom,
const Feature& feature,
optional<double> colorRampParameter,
const std::set<std::string>& availableImages) const;
EvaluationResult evaluate(optional<mbgl::Value> accumulated, const Feature& feature) const;
/**
* Statically analyze the expression, attempting to enumerate possible outputs. Returns
* an array of values plus the sentinel null optional value, used to indicate that the

View File

@@ -36,7 +36,7 @@ struct FormattedSection {
class Formatted {
public:
Formatted() = default;
Formatted(const char* plainU8String) {
sections.emplace_back(std::string(plainU8String), nullopt, nullopt, nullopt);
}

View File

@@ -0,0 +1,49 @@
#pragma once
#include <mbgl/style/conversion.hpp>
#include <mbgl/util/color.hpp>
#include <mbgl/util/optional.hpp>
#include <string>
#include <vector>
namespace mbgl {
namespace style {
namespace expression {
class Image {
public:
Image() = default;
Image(const char* imageID);
Image(std::string imageID);
explicit Image(std::string imageID, bool available);
bool operator==(const Image&) const;
mbgl::Value toValue() const;
const std::string& id() const;
bool isAvailable() const;
bool empty() const;
private:
std::string imageID;
bool available;
};
} // namespace expression
namespace conversion {
template <>
struct Converter<expression::Image> {
public:
optional<expression::Image> operator()(const Convertible& value, Error& error) const;
};
template <>
struct ValueFactory<expression::Image> {
static Value make(const expression::Image& image) { return image.toValue(); }
};
} // namespace conversion
} // namespace style
} // namespace mbgl

View File

@@ -0,0 +1,33 @@
#pragma once
#include <mbgl/style/expression/expression.hpp>
namespace mbgl {
namespace style {
namespace expression {
class ParsingContext;
class ImageExpression final : public Expression {
public:
explicit ImageExpression(std::unique_ptr<Expression> imageID);
EvaluationResult evaluate(const EvaluationContext&) const override;
static ParseResult parse(const mbgl::style::conversion::Convertible&, ParsingContext&);
void eachChild(const std::function<void(const Expression&)>&) const override;
bool operator==(const Expression& e) const override;
std::vector<optional<Value>> possibleOutputs() const override { return {nullopt}; }
mbgl::Value serialize() const override;
std::string getOperator() const override { return "image"; }
private:
std::shared_ptr<Expression> imageID;
};
} // namespace expression
} // namespace style
} // namespace mbgl

View File

@@ -30,6 +30,8 @@ bool isGlobalPropertyConstant(const Expression& expression, const T& properties)
bool isFeatureConstant(const Expression& expression);
bool isZoomConstant(const Expression& e);
// Returns true if expression does not depend on information provided by the runtime.
bool isRuntimeConstant(const Expression& e);
} // namespace expression
} // namespace style

View File

@@ -14,65 +14,70 @@ template <class T>
std::string toString(const T& t);
struct NullType {
constexpr NullType() {};
constexpr NullType() = default;
std::string getName() const { return "null"; }
bool operator==(const NullType&) const { return true; }
};
struct NumberType {
constexpr NumberType() {};
constexpr NumberType() = default;
std::string getName() const { return "number"; }
bool operator==(const NumberType&) const { return true; }
};
struct BooleanType {
constexpr BooleanType() {};
constexpr BooleanType() = default;
std::string getName() const { return "boolean"; }
bool operator==(const BooleanType&) const { return true; }
};
struct StringType {
constexpr StringType() {};
constexpr StringType() = default;
std::string getName() const { return "string"; }
bool operator==(const StringType&) const { return true; }
};
struct ColorType {
constexpr ColorType() {};
constexpr ColorType() = default;
std::string getName() const { return "color"; }
bool operator==(const ColorType&) const { return true; }
};
struct ObjectType {
constexpr ObjectType() {};
constexpr ObjectType() = default;
std::string getName() const { return "object"; }
bool operator==(const ObjectType&) const { return true; }
};
struct ErrorType {
constexpr ErrorType() {};
constexpr ErrorType() = default;
std::string getName() const { return "error"; }
bool operator==(const ErrorType&) const { return true; }
};
struct ValueType {
constexpr ValueType() {};
constexpr ValueType() = default;
std::string getName() const { return "value"; }
bool operator==(const ValueType&) const { return true; }
};
struct CollatorType {
constexpr CollatorType() {}; // NOLINT
constexpr CollatorType() = default;
std::string getName() const { return "collator"; }
bool operator==(const CollatorType&) const { return true; }
};
struct FormattedType {
constexpr FormattedType() {}; // NOLINT
constexpr FormattedType() = default;
std::string getName() const { return "formatted"; }
bool operator==(const FormattedType&) const { return true; }
};
struct ImageType {
constexpr ImageType() = default;
std::string getName() const { return "resolvedImage"; }
bool operator==(const ImageType&) const { return true; }
};
constexpr NullType Null;
constexpr NumberType Number;
@@ -84,21 +89,22 @@ constexpr ObjectType Object;
constexpr CollatorType Collator;
constexpr FormattedType Formatted;
constexpr ErrorType Error;
constexpr ImageType Image;
struct Array;
using Type = variant<
NullType,
NumberType,
BooleanType,
StringType,
ColorType,
ObjectType,
ValueType,
mapbox::util::recursive_wrapper<Array>,
CollatorType,
FormattedType,
ErrorType>;
using Type = variant<NullType,
NumberType,
BooleanType,
StringType,
ColorType,
ObjectType,
ValueType,
mapbox::util::recursive_wrapper<Array>,
CollatorType,
FormattedType,
ErrorType,
ImageType>;
struct Array {
explicit Array(Type itemType_) : itemType(std::move(itemType_)) {}

View File

@@ -2,6 +2,7 @@
#include <mbgl/style/expression/collator.hpp>
#include <mbgl/style/expression/formatted.hpp>
#include <mbgl/style/expression/image.hpp>
#include <mbgl/style/expression/type.hpp>
#include <mbgl/style/position.hpp>
#include <mbgl/style/types.hpp>
@@ -19,16 +20,16 @@ namespace expression {
struct Value;
using ValueBase = variant<
NullValue,
bool,
double,
std::string,
Color,
Collator,
Formatted,
mapbox::util::recursive_wrapper<std::vector<Value>>,
mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>;
using ValueBase = variant<NullValue,
bool,
double,
std::string,
Color,
Collator,
Formatted,
Image,
mapbox::util::recursive_wrapper<std::vector<Value>>,
mapbox::util::recursive_wrapper<std::unordered_map<std::string, Value>>>;
struct Value : ValueBase {
using ValueBase::ValueBase;

View File

@@ -40,9 +40,9 @@ public:
void setBackgroundOpacityTransition(const TransitionOptions&);
TransitionOptions getBackgroundOpacityTransition() const;
static PropertyValue<std::string> getDefaultBackgroundPattern();
const PropertyValue<std::string>& getBackgroundPattern() const;
void setBackgroundPattern(const PropertyValue<std::string>&);
static PropertyValue<expression::Image> getDefaultBackgroundPattern();
const PropertyValue<expression::Image>& getBackgroundPattern() const;
void setBackgroundPattern(const PropertyValue<expression::Image>&);
void setBackgroundPatternTransition(const TransitionOptions&);
TransitionOptions getBackgroundPatternTransition() const;

View File

@@ -52,9 +52,9 @@ public:
void setFillExtrusionOpacityTransition(const TransitionOptions&);
TransitionOptions getFillExtrusionOpacityTransition() const;
static PropertyValue<std::string> getDefaultFillExtrusionPattern();
const PropertyValue<std::string>& getFillExtrusionPattern() const;
void setFillExtrusionPattern(const PropertyValue<std::string>&);
static PropertyValue<expression::Image> getDefaultFillExtrusionPattern();
const PropertyValue<expression::Image>& getFillExtrusionPattern() const;
void setFillExtrusionPattern(const PropertyValue<expression::Image>&);
void setFillExtrusionPatternTransition(const TransitionOptions&);
TransitionOptions getFillExtrusionPatternTransition() const;

View File

@@ -52,9 +52,9 @@ public:
void setFillOutlineColorTransition(const TransitionOptions&);
TransitionOptions getFillOutlineColorTransition() const;
static PropertyValue<std::string> getDefaultFillPattern();
const PropertyValue<std::string>& getFillPattern() const;
void setFillPattern(const PropertyValue<std::string>&);
static PropertyValue<expression::Image> getDefaultFillPattern();
const PropertyValue<expression::Image>& getFillPattern() const;
void setFillPattern(const PropertyValue<expression::Image>&);
void setFillPatternTransition(const TransitionOptions&);
TransitionOptions getFillPatternTransition() const;

View File

@@ -91,9 +91,9 @@ public:
void setLineOpacityTransition(const TransitionOptions&);
TransitionOptions getLineOpacityTransition() const;
static PropertyValue<std::string> getDefaultLinePattern();
const PropertyValue<std::string>& getLinePattern() const;
void setLinePattern(const PropertyValue<std::string>&);
static PropertyValue<expression::Image> getDefaultLinePattern();
const PropertyValue<expression::Image>& getLinePattern() const;
void setLinePattern(const PropertyValue<expression::Image>&);
void setLinePatternTransition(const TransitionOptions&);
TransitionOptions getLinePatternTransition() const;

View File

@@ -42,9 +42,9 @@ public:
const PropertyValue<bool>& getIconIgnorePlacement() const;
void setIconIgnorePlacement(const PropertyValue<bool>&);
static PropertyValue<std::string> getDefaultIconImage();
const PropertyValue<std::string>& getIconImage() const;
void setIconImage(const PropertyValue<std::string>&);
static PropertyValue<expression::Image> getDefaultIconImage();
const PropertyValue<expression::Image>& getIconImage() const;
void setIconImage(const PropertyValue<expression::Image>&);
static PropertyValue<bool> getDefaultIconKeepUpright();
const PropertyValue<bool>& getIconKeepUpright() const;

View File

@@ -16,7 +16,7 @@ public:
bool isZoomConstant() const noexcept;
bool isFeatureConstant() const noexcept;
bool canEvaluateWith(const expression::EvaluationContext&) const noexcept;
bool isRuntimeConstant() const noexcept;
float interpolationFactor(const Range<float>&, const float) const noexcept;
Range<float> getCoveringStops(const float, const float) const noexcept;
const expression::Expression& getExpression() const noexcept;
@@ -28,6 +28,7 @@ protected:
variant<std::nullptr_t, const expression::Interpolate*, const expression::Step*> zoomCurve;
bool isZoomConstant_;
bool isFeatureConstant_;
bool isRuntimeConstant_;
};
template <class T>
@@ -40,7 +41,6 @@ public:
}
T evaluate(const expression::EvaluationContext& context, T finalDefaultValue = T()) const {
assert(canEvaluateWith(context));
const expression::EvaluationResult result = expression->evaluate(context);
if (result) {
const optional<T> typed = expression::fromExpressionValue<T>(*result);
@@ -50,18 +50,38 @@ public:
}
T evaluate(float zoom) const {
assert(!isZoomConstant());
assert(isFeatureConstant());
return evaluate(expression::EvaluationContext(zoom));
}
T evaluate(const GeometryTileFeature& feature, T finalDefaultValue) const {
assert(isZoomConstant());
assert(!isFeatureConstant());
return evaluate(expression::EvaluationContext(&feature), finalDefaultValue);
}
T evaluate(const GeometryTileFeature& feature,
const std::set<std::string>& availableImages,
T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(&feature).withAvailableImages(&availableImages),
finalDefaultValue);
}
T evaluate(float zoom, const GeometryTileFeature& feature, T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(zoom, &feature), finalDefaultValue);
}
T evaluate(float zoom,
const GeometryTileFeature& feature,
const std::set<std::string>& availableImages,
T finalDefaultValue) const {
return evaluate(expression::EvaluationContext(zoom, &feature).withAvailableImages(&availableImages),
finalDefaultValue);
}
T evaluate(float zoom, const GeometryTileFeature& feature, const FeatureState& state, T finalDefaultValue) const {
assert(!isFeatureConstant());
return evaluate(expression::EvaluationContext(zoom, &feature, &state), finalDefaultValue);
}