Deep Map™ Rule specification

Rules are style definitions which override the feature style definition of a named feature. For the specifications, see Deep Map™ Style Specification. The criteria are checked for each possibly visible object on the map in realtime, and when the criteria within the rule’s condition are met, the style definition in the rule’s body overrides the feature style definition for the object. The style definitions within the rule are the same as the feature style definition. The rules are defined in the .mapRule file.

The rule syntax is defined as follows:

rule <ruleName> ( <feature1> , ... , <featureN> ) [" <rule expression> "]
{
        <property> : <propertyValue> ;

        <nestedRule>
}

rule is the keyword to start a new rule definition. <ruleName> is a unique name for the rule. The feature name is followed by a list of feature names, enclosed by parentheses. Those are the features the rule is applied to. All features must be of the same basic type (point, icon, line, polygon). [" and "] mark the beginning and end of the rule condition. The <rule expression> is a boolean expression which specifies if the rule should be applied or not. We will take a closer look at the rule expression later. The rule condition is optional. If left out, the rule is always applied. The body of a rule is the same as the body of a feature, and the properties must match the assigned feature type. Additionally, it is possible to nest rules within another rule. The nested rule is then only executed if the embracing rules conditions are met. More on nested rules later.

Rule expression

The rule expression mimics closely a SQL-WHERE clause. There are two key differences: the SQL data fields are replaced by feature attributes, and the rule body is applied if something matches the clause, instead of returning a data set. All functions from SQL are usable, but there are some additional functions for querying the map state.

Expression syntax:

Logical expressions:

AND, OR

Comparative expressions:

<, <=, >, >=, =, !=, NOT, IS NULL, NOT NULL, IN (1,2,3, ..), BETWEEN x AND y

Math operators:

+, -, *, /

Functions:

distance(), isSelected(), isHighlighted(),  “any SQLITE function”

Data fields:

feature type attributes, map attributes

The new function for querying the map are:

function

return value

runtime complexity

version

distance()

double: 3D distance from object to the camera

point/icon/polygon: O(1), lines: O(lineSegments)

v2.0.0

isSelected()

int: 1 if object is selected, otherwise 0

std::set lookup: Ω(1), O(n)

v2.0.0

isHighlighted()

int: 1 if object is highlighted, otherwise 0

std::set lookup: Ω(1), O(n)

v2.0.0

cameraHeight()

double: distance from the camera to the ground

O(1)

v2.1.2

currentZoomlevel()

int: the current Zoomlevel

O(1)

v2.1.2

Examples:

//rule applied to a single feature type using the distance() function
rule distanceVisibility(building) [" distance() > 1000 "]
{
        visibility: none;
}

//rule applied to multiple feature types using the isSelected() function
rule selectionColor(building, room, stand) [" isSelected() "]
{
        fill-color: #FF0000;
}

//this rule is true, if a room has the attribute "iAmSpecial" and the value of the attribute is "yes"
rule specialRoom(room) [" iAmSpecial = 'yes' "]
{
        fill-color: #00FF00;
}

Multiple rules can set the same property. In the above example, selectionColor and specialRoom modify both the fill-color of an object. If this happens, the first defined rule wins. In this case, a room with the attribute “iAmSpecial: yes” will be blue, but as soon as it is selected it turns red. If the two rules were switched, the room would always be blue, and selection would have no effect.

Rule inheritance

If one feature inherits from another, it inherits not only the properties, but the rules as well! This can be useful if multiple feature have a similar style and should share the same behaviour. For example:

feature building:polygon {}
feature specialBuilding:building {}

rule highlightBuilding(building) [" isHighlighted() "]
{
        fill-outline-width: 2.0;
        fill-outline-color: #FFFFFF;
}

In this case, all objects of the type “building” and “specialBuilding” will get a thick, white outline when they are highlighted. Inherited rules have a lower priority than rules directly defined for the feature type in question. The further up in the ancestry, the lower the priority of a rule.

feature building:polygon {}
feature specialBuilding:building {}


rule buildingRule(building) [" isHighlighted() "]
{
        fill-outline-width: 2.0;
        fill-outline-color: #FFFFFF;
}

//this rule wins; if a specialBuilding has the attribute "isVerySpecial: yes", its outline will be blue
//the fill-outline-width is still set to 2.0, because the property is only set in buildingRule
rule highlightSpecial(specialBuilding) [" isHighlighted() AND isVerySpecial = 'yes']
{
        fill-outline-color: #0000FF;
}

Rule nesting

It is possible to define rules inside the body of another rule:

rule outerRule(building) [" isSelected() "] {
        rule innerRule(room) {
                visibility: none;
        }
}

A nested rule is conditionally depending on the outer rule. In this case “innerRule” is only evaluated if the condition of the “outerRule” if fulfilled. So all rooms of a building will now be hidden if the building is selected.

There are several limitations which need to be considered when nesting rules:

  • The feature types of the nested rules are not allowed to be related to the feature types of the outer rules over the Deep Map™ Style definitions. This is because the behaviour of a rule can become undefined due to rule inheritance

  • The map objects affected by the outer rule have to be ancestors of the map objects affected by the nested rule to take effect.

The following example shows valid and invalid nested rules:

feature building:polygon {}
feature specialBuilding:building {}
feature room:polygon {}
feature tree:polygon {}

rule distanceVisibility(building) ["distance() > 200"]
{
        text-visiblity: visible;

        rule roomVisiblity(room) //valid rule, since room is not related by style to building.
        {
                visibility: none;
                text-visibility: none;
        }

        rule specialVisibility(specialBuilding) //invalid rule, because specialBuilding and building are related in the styling
        {
                visibility: none;
                text-visibility: none;
        }

        rule treeVisibility(tree)       //valid rule, but unlikely to have an effect, since trees are seldom inside buildings
        {
                visibility: none;
                text-visibility: none;
        }
}

Nested rules have the highest priority, since they are the most specific. The deeper a rule is nested, the higher is its priority. The following diagram illustrates again in which order style properties are evaluated:

../_images/rule-priority.png

Property Rules (Version 2.1)

In some scenarios a user might want to set many different values for a single property. Doing this with the normal rules would be cumbersome, because this would require one rule for each individual value. These cases are better handled by property rules.

A property rule is defined by setting a rule-clause instead of the normal property-value:

feature building : polygon{
    fill-color: [" <SELECT Statement> [WHERE <Where statement>] " ];
}

The selected value has to match the string-format of the value type. For example, a custom set color value could be set in the following way:

feature building:polygon {
    fill-color: ["CASE WHEN customColor NOT NULL THEN customColor ELSE '#ff0000' END"];
}

In this case, the value of customColor needs to be a string representing a hex-color value. The query is discarded if the return-type does not match the property-type.