Writing Rules and Actions

A Rule or Action operates on objects within a specified class if one is chosen, or against all or most objects if no class is specified. Standard Classes can be used to limit all objects to a subset of either spatial or non-spatial objects.

Rules

A Rule needs to answer the question:

"Given an object from the class, what does this Rule require of the object (sometimes referred to as the root object) in order for it to be valid?"

This means that a Rule ultimately returns true (object is valid) or false (object is not valid) and therefore is always represented by a Predicate.

     Note: It is better to use a number of simpler Rules than a limited number of more complex Rules for checks that are not directly related, for example:

  • Instead of a single Rule to check for two attributes, implement two Rules that check each attribute independently.
  • Instead of writing a Rule that checks if a Valve is connected to two pipes and whether the connected pipes have the same pressure rating, it is better to have one Rule which checks the number of connected pipes and one Rule which checks the pressure rating of the connected pipes.

This practice ensures that the report of the Rules that failed will provide more specific information, which can be more effectively acted upon.

Rules that check within an object

A simple Rule might only check the attributes within an object, for example it could:

  • Check the geometry is of a certain type.

  • Check an attribute is not null.

  • If one attribute is set to X then check the other attribute is set to Y.

These are handled by having Predicates, such as check that, operating with other Predicates, such as IF statements or logical operators (AND, OR, NOT).

Rules that check other objects

More complex Rules check the current object against other loaded objects, for example:

  • Check that a building's geometry does not sit on top of other objects.

  • Check that a valve is connected by two pipes.

  • Check that an attribute has a Value from a specified class of allowed Values.

There are four main ways to check other objects:

  • Existence:

    This can be used when you are interested in the number of other objects. Exists is a Predicate and will thus return 'True' or 'False'. It identifies the matching objects using Predicates that typically compare with the root object's attributes. Some examples of Exists in use could be: 'This building MUST have no other overlapping buildings', 'This valve MUST have two connected pipes' and 'This object MUST have an attribute Value that equals one other object Value from a class of allowed Values'.

  • For All:

    For all is used when the number of other objects is not important, but if there are any then they must be in a certain form (e.g. pipes connected to a valve must be of the same pressure). This returns 'True', or 'False', and looks for the matching objects using the Predicates specified. If there are no objects of the type specified then it returns true (nothing is wrong).

  • For the Nearest:

    For the nearest is used when checking objects based on vicinity, and will check the nearest n objects to see if they satisfy a second Predicate (e.g. check that the name of the nearest street to a building is present in the building's address). An optional filter can be applied, meaning only the nearest objects passing this filter would be tested against the second Predicate.

  • Aggregate Values:

    These return a single Value (count, sum, average, or other function) calculated by visiting a number of related objects (e.g the maximum voltage of all connected cables). This also works for geometries (e.g. find the union of the objects' geometries). The result of the Aggregate Value can then be used in a comparison check (e.g. check that the union of all referenced road centrelines is a single-part geometry).

The techniques above are used to identify the other objects, which are subsequently used to perform checks against. Ultimately, the eventual checks are typically comparisons (X=Y), range checks (X=[1..5]) or reference checks (is there a reference, such as a foreign key, defined between these objects?).

     Example: Some checks can be achieved in more than one way. 'Check that I connect to two pipes' could be implemented using Existence (there exists at least two pipes for which...) or an Aggregate Value (aggregate sum over all pipes for which... is equal to 2).

     Note: Remember to exclude the root object when identifying other objects within the same class.

For example, a Rule performed on roads which checks that there are no other roads with an equal geometry will find the root road object because a geometry equals itself, so all roads would fail the Rule. To rectify this, the Rule must exclude the root object by comparing a unique attribute, or by comparing the object themselves. An example use could be:

Rule for road objects: there exists exactly 0 road:other objects where road.geometry equals road:other.geometry AND road.ID not equal road:other.ID.

Any relationship that returns true when comparing a Value with itself could cause this. It is worth noting that this applies to the following spatial relationships: Equal, Within, Contains, Covered, Covers and Within Distance.

Name Parameters

If the other selected objects have the same class as the current 'root' object then when specifying the other objects, the Rule author will require you to give the other objects a Name Parameter to be able to distinguish between the 'root' and the others.

     Example: Rule for objects Building: There existing exactly 0 Building:other objects where Building.geometry overlaps Building:other.geometry.

Actions

Unlike a Rule, an Action does not return true or false but simply performs either a single task or a series of tasks, linked together in a sequence or loop.

Actions typically achieve their aims in three ways:

  • Change the data (create an object, delete an object, or update an object by assigning a Value to one of its attributes).
  • Report (produce information to appear in the Task Results report).
  • Run a built-in Operation (a packaged piece of logic in a single function).

As with Rules, Actions can be simple and operate purely on the current root object or may be more complex and work with other related objects. The primary way to access other objects is using a Loop Over Objects Operation. Similar to the For All Predicate in a Rule, a Loop Over Objects Operation in an Action will iterate over the other objects, then perform an Operation on either the root object or other objects within the loop. However, unlike a For All Predicate, the Loop Over Objects Operation performs an Operation using the found objects, rather than just checking a Predicate.

Control flow in Actions

As well as Loop over objects, there are also other ways to control the flow of the Actions, in particular:

  • Sequence, a way to string a series of Operations together.

  • If…Then…Else, only performs an Operation if the Predicate is true (any of the Predicate types used in Rules can be used for the check).

  • Loop Over Objects, as described above, to access other objects. This is used whether accessing a single or iterating over multiple objects and can be constrained to only iterate over a certain number if there are multiple objects that match.

  • Loop Over Nearest Objects, loops over the nearest n number of objects in the cache and performs an Operation on each one. The Operation is performed for the nearest n objects that satisfy a test Predicate, in order of increasing distance from the source geometry.

  • Loop Over a Collection or a Geometry, iterate over the parts of a single or multi-part geometry. Can also be used for iterating over any collection, but these are rare.

  • While loop, keep looping until a Predicates condition is met (any of the Predicate types used in Rules can be used as the check here).

     Note: In a If…Then…Else, the objects found using the If Predicate are not directly accessible for the subsequent Then Operation. You will need a subsequent Loop Over Objects Operation to actually get hold of the objects.

For example to get hold of a single object:

If there exists at least one X then for the first X <do something>

In practice, the Loop Over Objects Operation will only operate on objects that exist, in which case it is usually most efficient to only do the loop. For example:

For the first X <do something>

If the number of objects is important to check first, then the exists check is still needed. For example:

If there exists at least 5 X then for all X <do something>