What are expressions and conditions?
- Expression
-
An expression is an opaque object that returns a value upon evaluation.
- Condition
-
A condition is a case of an expression that always returns a Boolean value.
Conditions are used to identify particular sub-sets of data such that specific actions can be applied on the sub-set only. Conditions are not modeled in the API as a separate class.
Expressions use the method
isCondition
isCondition
isCondition
to indicate that their result is a Boolean value.
Creating expressions and conditions
Expressions and conditions are created using the factory class
ExpressionFactory
ExpressionFactory
ExpressionFactory
.
For example:
Expression literal1 = ExpressionFactory::literal(std::string("motorway"));
Expression literal2 = ExpressionFactory::literal(std::string("primary"));
Expression valueReference = ExpressionFactory::valueReference("roadType");
Expression equal1 = ExpressionFactory::equal(valueReference, literal1);
Expression equal2 = ExpressionFactory::equal(valueReference, literal2);
std::vector<Expression> qeVector = {equal1, equal2};
Expression condition = ExpressionFactory::orOp(qeVector);
var literal1 = ExpressionFactory.Literal(new ExpressionValue("motorway"));
var literal2 = ExpressionFactory.Literal(new ExpressionValue("primary"));
var valueReference = ExpressionFactory.ValueReference("roadType");
var equal1 = ExpressionFactory.Equal(valueReference, literal1);
var equal2 = ExpressionFactory.Equal(valueReference, literal2);
Expression[] qeVector = { equal1, equal2 };
var condition = ExpressionFactory.OrOp(qeVector);
Expression literal1 = ExpressionFactory.literal(new ExpressionValue("motorway"));
Expression literal2 = ExpressionFactory.literal(new ExpressionValue("primary"));
Expression valueReference = ExpressionFactory.valueReference("roadType");
Expression equal1 = ExpressionFactory.equal(valueReference, literal1);
Expression equal2 = ExpressionFactory.equal(valueReference, literal2);
List<Expression> qeVector = Arrays.asList(equal1, equal2);
Expression condition = ExpressionFactory.orOp(qeVector);
The result of the example is:
std::string expected = "((prop(roadType) = 'motorway') OR (prop(roadType) = 'primary'))";
var expected = "((prop(roadType) = 'motorway') OR (prop(roadType) = 'primary'))";
String expected = "((prop(roadType) = 'motorway') OR (prop(roadType) = 'primary'))";
This is an example of bounds expression:
auto referenceExpected = CoordinateReferenceProvider::create("EPSG:4326");
auto reference = referenceExpected.value();
auto bounds1 = std::make_shared<Bounds>(reference, Coordinate{15, 15}, 20, 20, 1000);
auto bounds2 = std::make_shared<Bounds>(reference, Coordinate{10, 10}, 10, 10, 1000);
auto expression = ExpressionFactory::bbox(ExpressionFactory::literal({bounds1}), ExpressionFactory::literal({bounds2}));
const auto* expected = "Bounds [x:15, y:15, w:20, h:20] Intersects Bounds [x:10, y:10, w:10, h:10]";
CoordinateReference reference = CoordinateReferenceProvider.Create("EPSG:4326");
Bounds bounds1 = new Bounds(reference, new Coordinate(15, 15), 20, 20, 1000);
Bounds bounds2 = new Bounds(reference, new Coordinate(10, 10), 10, 10, 1000);
Expression expression = ExpressionFactory.Bbox(ExpressionFactory.Literal(new ExpressionValue(bounds1)),
ExpressionFactory.Literal(new ExpressionValue(bounds2)));
String expected = "Bounds [x:15, y:15, w:20, h:20] Intersects Bounds [x:10, y:10, w:10, h:10]";
CoordinateReference reference = CoordinateReferenceProvider.create("EPSG:4326");
Bounds bounds1 = new Bounds(reference, new Coordinate(15, 15), 20, 20, 1000);
Bounds bounds2 = new Bounds(reference, new Coordinate(10, 10), 10, 10, 1000);
Expression expression = ExpressionFactory.bbox(ExpressionFactory.literal(new ExpressionValue(bounds1)),
ExpressionFactory.literal(new ExpressionValue(bounds2)));
String expected = "Bounds [x:15.0, y:15.0, w:20.0, h:20.0] Intersects Bounds [x:10.0, y:10.0, w:10.0, h:10.0]";
Traversing an expression hierarchy
The expressions returned from the expression factory are opaque objects.
To traverse the expression hierarchy, you can use a visitor
IExpressionVisitor
IExpressionVisitor
IExpressionVisitor
.
This is an example of traversing an expression tree with a visitor:
class ToStringExpressionVisitor : public luciad::IExpressionVisitor {
std::string result;
public:
std::string getResult() {
return result;
}
void visitLiteral(const luciad::ExpressionValue& value) override {
if (value.getDataType() == luciad::DataType::getStringType()) {
result += "'" + value.getStringValue() + "'";
} else if (value.getDataType() == luciad::DataType::getFloatType()) {
result += std::to_string(value.getFloatValue());
} else if (value.getDataType() == luciad::DataType::getDoubleType()) {
result += std::to_string(value.getDoubleValue());
} else if (value.getDataType() == luciad::DataType::getIntType()) {
result += std::to_string(value.getIntValue());
} else if (value.getDataType() == luciad::DataType::getLongType()) {
result += std::to_string(value.getLongValue());
} else if (value.getDataType() == luciad::DataType::getBooleanType()) {
result += std::to_string(value.getBoolValue());
} else if (value.getDataType() == luciad::DataType::getGeometryType()) {
auto* bounds = dynamic_cast<luciad::Bounds*>(value.getGeometryValue().get());
if (bounds) {
result += luciad::String::format("Bounds [x:{0}, y:{1}, w:{2}, h:{3}]", bounds->getLocation().x, bounds->getLocation().y, bounds->getWidth(),
bounds->getHeight());
}
}
}
void visitValueReference(const std::string& propertyPath) override {
result += "prop(" + propertyPath + ")";
}
void visitAnd(const std::vector<luciad::Expression>& conditions) override {
result += "(";
bool first = true;
for (const luciad::Expression& condition: conditions) {
if (!first) {
result += " AND ";
}
condition.accept(*this);
first = false;
}
result += ")";
}
void visitOr(const std::vector<luciad::Expression>& conditions) override {
result += "(";
bool first = true;
for (const luciad::Expression& condition: conditions) {
if (!first) {
result += " OR ";
}
condition.accept(*this);
first = false;
}
result += ")";
}
void visitNot(const luciad::Expression& condition) override {
result += "!";
condition.accept(*this);
}
void visitEqual(const luciad::Expression& left, const luciad::Expression& right) override {
result += "(";
left.accept(*this);
result += " = ";
right.accept(*this);
result += ")";
}
void visitNotEqual(const luciad::Expression& left, const luciad::Expression& right) override {
result += "(";
left.accept(*this);
result += " != ";
right.accept(*this);
result += ")";
}
void visitGt(const luciad::Expression& left, const luciad::Expression& right) override {
result += "(";
left.accept(*this);
result += " > ";
right.accept(*this);
result += ")";
}
void visitGte(const luciad::Expression& left, const luciad::Expression& right) override {
result += "(";
left.accept(*this);
result += " >= ";
right.accept(*this);
result += ")";
}
void visitLt(const luciad::Expression& left, const luciad::Expression& right) override {
result += "(";
left.accept(*this);
result += " < ";
right.accept(*this);
result += ")";
}
void visitLte(const luciad::Expression& left, const luciad::Expression& right) override {
result += "(";
left.accept(*this);
result += " <= ";
right.accept(*this);
result += ")";
}
void visitBBox(const luciad::Expression& left, const luciad::Expression& right) override {
left.accept(*this);
result += " Intersects ";
right.accept(*this);
}
void visitBBox(const luciad::Expression& expression) override {
result += "Intersects ";
expression.accept(*this);
}
void visitLike(const std::string& propertyPath, const std::string& pattern, bool matchCase, char wildCard, char singleChar, char escapeChar) override {
result += "Like ['" + propertyPath + "', '" + pattern + "', '" + (matchCase ? "true" : "false") + "', '" + wildCard + "', '" + singleChar + "', '" + escapeChar + "']";
}
};
internal class ToStringExpressionVisitor : IExpressionVisitor
{
public string Result { get; private set; }
public void VisitLiteral(ExpressionValue value)
{
if (Equals(value.DataType, DataType.StringType))
{
Result += "'" + value.StringValue + "'";
}
else if (Equals(value.DataType, DataType.DoubleType))
{
Result += value.DoubleValue;
}
else if (Equals(value.DataType, DataType.IntType))
{
Result += value.IntValue;
}
else if (Equals(value.DataType, DataType.BooleanType))
{
Result += value.BoolValue;
}
else if (Equals(value.DataType, DataType.GeometryType))
{
if (value.GeometryValue is Bounds bounds)
{
Result += "Bounds [x:" + bounds.Location.X + ", y:" + bounds.Location.Y + ", w:" + bounds.Width + ", h:" + bounds.Height + "]";
}
else
{
Result += "Geometry(" + value.GeometryValue + ")";
}
}
}
public void VisitValueReference(string propertyPath)
{
Result += "prop(" + propertyPath + ")";
}
public void VisitAnd(IList<Expression> conditions)
{
Result += "(";
var first = true;
foreach (var condition in conditions)
{
if (!first)
{
Result += " AND ";
}
condition.Accept(this);
first = false;
}
Result += ")";
}
public void VisitOr(IList<Expression> conditions)
{
Result += "(";
bool first = true;
foreach (var condition in conditions)
{
if (!first)
{
Result += " OR ";
}
condition.Accept(this);
first = false;
}
Result += ")";
}
public void VisitNot(Expression condition)
{
Result += "!";
condition.Accept(this);
}
public void VisitEqual(Expression left, Expression right)
{
Result += "(";
left.Accept(this);
Result += " = ";
right.Accept(this);
Result += ")";
}
public void VisitNotEqual(Expression left, Expression right)
{
Result += "(";
left.Accept(this);
Result += " != ";
right.Accept(this);
Result += ")";
}
public void VisitGt(Expression left, Expression right)
{
Result += "(";
left.Accept(this);
Result += " > ";
right.Accept(this);
Result += ")";
}
public void VisitGte(Expression left, Expression right)
{
Result += "(";
left.Accept(this);
Result += " >= ";
right.Accept(this);
Result += ")";
}
public void VisitLt(Expression left, Expression right)
{
Result += "(";
left.Accept(this);
Result += " < ";
right.Accept(this);
Result += ")";
}
public void VisitLte(Expression left, Expression right)
{
Result += "(";
left.Accept(this);
Result += " <= ";
right.Accept(this);
Result += ")";
}
public void VisitBBox(Expression left, Expression right)
{
left.Accept(this);
Result += " Intersects ";
right.Accept(this);
}
public void VisitBBox(Expression expression)
{
Result += "Intersects ";
expression.Accept(this);
}
public void VisitLike(string propertyPath, string pattern, bool matchCase, char wildCard, char singleChar, char escapeChar)
{
Result += "Like ['" + propertyPath + "', '" + pattern + "', '" + (matchCase ? "true" : "false") + "', '" + wildCard + "', '" + singleChar + "', '" + escapeChar + "']";
}
}
private static class ToStringExpressionVisitor implements IExpressionVisitor {
private final StringBuilder result = new StringBuilder();
public String getResult() {
return result.toString();
}
@Override
public void visitLiteral(ExpressionValue value) {
if (Objects.equals(value.getDataType(), DataType.getStringType())) {
result.append("'").append(value.getStringValue()).append("'");
} else if (Objects.equals(value.getDataType(), DataType.getDoubleType())) {
result.append(value.getDoubleValue());
} else if (Objects.equals(value.getDataType(), DataType.getIntType())) {
result.append(value.getIntValue());
} else if (Objects.equals(value.getDataType(), DataType.getBooleanType())) {
result.append(value.getBoolValue());
} else if (Objects.equals(value.getDataType(), DataType.getGeometryType())) {
if (value.getGeometryValue() instanceof Bounds) {
Bounds bounds = (Bounds) value.getGeometryValue();
result.append("Bounds [x:").append(bounds.getLocation().getX()).append(", y:").append(bounds.getLocation().getY())
.append(", w:").append(bounds.getWidth()).append(", h:").append(bounds.getHeight()).append("]");
} else {
result.append("Geometry(").append(value.getGeometryValue()).append(")");
}
}
}
@Override
public void visitValueReference(String propertyPath) {
result.append("prop(").append(propertyPath).append(")");
}
@Override
public void visitAnd(List<Expression> conditions) {
result.append("(");
boolean first = true;
for (Expression condition : conditions) {
if (!first) {
result.append(" AND ");
}
condition.accept(this);
first = false;
}
result.append(")");
}
@Override
public void visitOr(List<Expression> conditions) {
result.append("(");
boolean first = true;
for (Expression condition : conditions) {
if (!first) {
result.append(" OR ");
}
condition.accept(this);
first = false;
}
result.append(")");
}
@Override
public void visitNot(Expression condition) {
result.append("!");
condition.accept(this);
}
@Override
public void visitEqual(Expression left, Expression right) {
result.append("(");
left.accept(this);
result.append(" = ");
right.accept(this);
result.append(")");
}
@Override
public void visitNotEqual(Expression left, Expression right) {
result.append("(");
left.accept(this);
result.append(" != ");
right.accept(this);
result.append(")");
}
@Override
public void visitGt(Expression left, Expression right) {
result.append("(");
left.accept(this);
result.append(" > ");
right.accept(this);
result.append(")");
}
@Override
public void visitGte(Expression left, Expression right) {
result.append("(");
left.accept(this);
result.append(" >= ");
right.accept(this);
result.append(")");
}
@Override
public void visitLt(Expression left, Expression right) {
result.append("(");
left.accept(this);
result.append(" < ");
right.accept(this);
result.append(")");
}
@Override
public void visitLte(Expression left, Expression right) {
result.append("(");
left.accept(this);
result.append(" <= ");
right.accept(this);
result.append(")");
}
@Override
public void visitBBox(Expression left, Expression right) {
left.accept(this);
result.append(" Intersects ");
right.accept(this);
}
@Override
public void visitBBox(Expression expression) {
result.append("Intersects ");
expression.accept(this);
}
@Override
public void visitLike(@NotNull String propertyPath, @NotNull String pattern, boolean matchCase, char wildCard, char singleChar, char escapeChar) {
result.append("Like ['")
.append(propertyPath)
.append("', '").append(pattern)
.append("', '").append((matchCase ? "true" : "false"))
.append("', '").append(wildCard)
.append("', '").append(singleChar)
.append("', '").append(escapeChar)
.append("']");
}
}