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 isConditionisConditionisCondition to indicate that their result is a Boolean value.

Creating expressions and conditions

Expressions and conditions are created using the factory class ExpressionFactoryExpressionFactoryExpressionFactory.

For example:

Program: Creating a condition
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:

Program: Result of the example condition
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:

Program: Creating a 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 IExpressionVisitorIExpressionVisitorIExpressionVisitor.

This is an example of traversing an expression tree with a visitor:

Program: 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("']");
  }
}