JOSM/MapCSS Validator Syntax

From OpenStreetMap Wiki
Jump to navigation Jump to search

The validator in JOSM is a potent tool underpinned by the MapCSS language, offering a robust framework for data quality assurance within the OpenStreetMap (OSM) ecosystem. With MapCSS as its foundation, this feature empowers you to extend built-in JOSM validation by creating and executing custom validation rules, providing meticulous control over the evaluation of OSM map data. This page shows only some of the most important parts of the MapCSS validation documentation. If you need more functionality, you can check if it is available on the JOSM Wiki.

Using the MapCSS Syntax Highlighter VS Code extension can help visualize the complicated JOSM validation syntax.

Selectors

Selectors are how rules pick which objects they target. Selectors are chained, refining the objects that will be returned to your functions as you add additional parameters, as if adding AND boolean operators. Order does not matter, as long as a type comes first.

Selectors can be combined, with a comma and newline between them, to apply rules to groups of selected objects. The last selector group should never have a comma between it and the function, otherwise the rule will be applied to all objects!

way["name"],
way["alt_name"] {
  /* do something to both of these selected groups*/
}

Types

Every selector must begin with one of the following types.

type description
* Select any object.
node Select any node.
way Select any way.
relation Select any relation.
area Select any area: either a closed way or a closed set of ways in a relation.

Blocks

Selector blocks are always contained within brackets, and filter through an object’s OSM tags. Regex patterns with escape characters for both the key and values need only be single-escaped.

Key evaluation within blocks

type description example
presence Key does exist on object. ["shop"]
absence Key does not exist on object. [!"amenity"]
regex Regular expression matching to key, contained within slashes. [/max[a-z]+/]
not regex Regular expression non-matching to key, contained within slashes. [!/addr:.*/]

Value evaluation within blocks

These value evaluations can be combined with any of the above key evaluations. More-niche operators are explained here.

operator description example
= Simple equivalence. ["shop"="gift"]
!= Not equal. ["amenity"!="fuel"]
>, <, >=, <= Comparison of numerical values. ["maxspeed">40]
^= Value starts with this string. ["name"^="St."]
$= Value ends with this string. ["name"$="Hwy"]
=~ Regular expression matching, contained within slashes. ["addr:postcode"=~/[0-9]{5}/]
!~ Regular expression non-matching, contained within slashes. ["addr:postcode"!~/[0-9]{5}/]

Classes

You can name selection groups so that they can be reused, mixed, or matched as needed elsewhere. For example:

*["name"] {
  set name;
}

way.name {
  /* do something */
}

is equivalent to:

way["name"] {
  /* do something */
}

Note that unlike the functions below, the set operator does not take a colon.

Pseudo-classes

Additional selectors that describe the state of an object. Always joined to previous selector elements with a :. Most common to use for validation rules is probably :modified, which will make the rule only apply to already-modified objects. You can read about the rest of these pseudo-classes here.

Negation

MapCSS negates any following selectors with a ! exclamation point after the last affirmative selector. Every selector thereafter will be negated like a NOT boolean operator.

*["name"] {
  set name;
}

way!.name {
  /* selects all ways that do not belong to the 'name' class */
}

way:modified!.name {
  /* selects modified ways that do not belong to the 'name' class */
}

way!.name:modified {
  /* selects unmodified ways that do not belong to the 'name' class */
}

Other

Other capabilities, like a zoom selector, parent/child object selector, and layer identifier are more advanced or seldom used for validator rules. More information is available in the full MapCSS documentation.

Function

Each line in the body of the rule represents a different operation for the rule to perform, always in the form of command: string;.

*["key"] {
  command: string;
  command: string;
  command: string;
}

Commands

throw

You can only use one throw* function per rule.

name description
throwError Raise an error message.
throwWarning Raise a warning message.
throwOther Raise an info message.

fix

You can use as many fix* functions per rule as needed. Using a fix* function will enable the Fix button at the button of the validator panel.

name description example
fixAdd Add or overwrite the given key and value. Requires both the key and value, separated by a =. "key=value"
fixRemove Remove the given key. "key"
fixChangeKey Change the key. "old_key=>new_key"
fixDeleteObject Delete the object. Rarely used!

suggestAlternative

Provide an alternative string without activating the Fix button.

group

Show a message under which other, more specific throwError, throwWarning, or throwOther messages can be grouped.

*["name"],
*["phone"] {
  throwWarning: "{0.key}";
  group: "Key is unexpected";
}
/*
Would appear in JOSM validation panel as:
Key is unexpected [+]
   phone [+]
   name [+]
*/

assert

Unit tests example keys and values to see if the selectors are working. Does not check the rule’s commands, just the selection. The validator.check_assert_local_rules setting must be set to true.

name description example selector of *["wikidata"][!"shop"]
assertMatch Check that the selector matches the given string. assertMatch: "way wikidata=Q12345";
assertNoMatch Check that the selector doesn’t match the given string. assertNoMatch: "way wikidata=Q12345 shop=gift";

Keys or values with internal punctuation or whitespace need to be enclosed in escaped quote marks: assertMatch: "node \"addr:street\"=\"Main Street\"";

Strings

The second part of each line must always evaluate to a string. The most basic of these possibilities is a simple "string". However, plenty of operations can be done to transform or manipulate tags and values before being passed to the command.

String functions

I’ve listed the most common functions for use with the validator below. The full list of functions supported by JOSM are available here.

tr(placeholder, *args) -> str

Returns a string with other strings inserted. The first string is the placeholder pattern, with later strings representing the arguments that match to the pattern, starting with 0. For example:

tr("{0} {1}-{2}@@{3}", "abc", "qwe", upper("xyz"), lower("Foo"));
/* would yield "abc qwe-XYZ@@foo" */

concat(*args) -> str

Joins strings together.

concat("phone=", replace("123-456-7890", "-", ""));
/* would yield "phone=1234567890" */

regexp_match(regex_pattern, string, regex_flags?) -> list

Returns a list of regex matches to the regex_pattern given the input string. First item of the list is always the full match, followed by any regex capture groups in the pattern string. The regex_flags argument is optional.

❗Escape characters in regex_pattern need to be double-escaped! ❗

get(list, item) -> str

Retrieve a string from a given list the item, provided as an integer starting at 0. Useful for retrieving regexp_match values.

replace(string, old, new) -> str

Returns the string with instances of the old string replaced by the new.

tag(key) -> str

Returns the value of a given key, provided as a str, if one exists. Otherwise, returns null.

b ? fst : snd

Conditional operator that can be used to add some simple logic for what to pass to the command. You can use any of the following common comparison operators in the conditional: ==, !=, <, >, <=, >=, ||, &&, or !. For example, the following rule checks if the values for website and url are the same, and if so, deletes url:

*["website"]["url"] {
  throwWarning: "Duplicate tag: {0.key}";
  fixRemove: tag("{0.value}") == tag("{1.value}") ? tr("{0}", "{1.key}") : "";
}

Other

There are lots of other useful string functions, like join, split, upper, lower, title, length, count, and many more in the JOSM Wiki.

Placeholders

Placeholders look up the selector, helping to reduce the need to rewrite the same code for different keys or values. The placeholder uses the index of the selector, starting at 0 with the first non-type filter. They are similar in syntax to Python f-strings, going between brackets in a string. They require one of the following selector attributes:

name output syntax
key String of the indicated selector’s key. "{i.key}"
value String of the indicated selector’s value. "{i.value}"
tag String of the indicated selector’s key and value, separated by a =. "{i.tag}"

For example, this:

way["name"]["shop"] {
  fixRemove: "name";
}

way["phone"]["shop"] {
  fixRemove: "phone";
}

has the same effect as this:

way["name"]["shop"],
way["phone"]["shop"] {
  fixRemove: "{0.key}";
}

Note that placeholders don’t work in fix* functions while also using classes. Placeholders will work in throw* or group functions, even when using classes.