X-GIS

Language

Expressions & operators.

The expression grammar that runs inside utility brackets, filters, block-property values, and match arms. Every builtin function listed in the function reference can be combined with the operators below.

Operators (tightest → loosest binding)

Listed in precedence order — a higher group binds tighter than a lower one. Within each group operators associate left-to-right.

1. Postfix (tightest)

Bind tightly to the operand on their left. Can chain: `obj.a.b[0]`.

.field

Field access — Read a property from the current feature's tags or from a nested object. The leading `.` (with no LHS) is shorthand for "this feature's `field`".

.height
.props.population
[i]

Index — Subscript an array. Out-of-bounds returns `null`.

.tags[0]
(args)

Function call — Invoke a builtin (`min`, `interpolate`, `circle`, …) or a user-defined function from a `fn` block.

clamp(.speed, 0, 60)

2. Unary

Single-operand operators applied before any binary op.

-x

Negation — Numeric negation.

opacity-[-.delta]
!x

Logical not — Truthy → false, falsy → true.

filter: !.disputed

3. Multiplicative

Standard arithmetic. Division-by-zero returns `0` (not `Infinity`).

*

Multiplication — Multiply.

.levels * 3.5
/

Division — Divide. `a / 0 → 0`.

.area / 1000
%

Remainder — Modulo. `a % 0 → 0`.

floor(.zoom) % 2

4. Additive

Add and subtract.

+

Addition — Numeric add (no string concat).

.x + 5
-

Subtraction — Subtract.

.zoom - 14

5. Comparison

Yield boolean. Used most often inside `filter:`.

<

Less than

filter: .area < 100
<=

Less or equal

filter: .level <= 3
>

Greater than

filter: .gdp > 1e12
>=

Greater or equal

filter: .pop >= 1e6

6. Equality

Strict equality. No type coercion (number ≠ string).

==

Equal

filter: .class == "highway"
!=

Not equal

filter: .kind != "park"

7. Logical AND

Short-circuits on falsy LHS.

&&

And — Both operands must be truthy.

filter: .class == "river" && .scalerank < 6

8. Logical OR

Short-circuits on truthy LHS.

||

Or — Either operand truthy.

filter: .kind == "rail" || .kind == "subway"

9. Coalesce

Null-coalesce. Falls through to RHS only when LHS is `null` / `undefined` / non-finite. `0` and `""` are NOT treated as missing.

??

Coalesce — Use RHS when LHS is missing. The standard "explicit fallback" idiom for sparse vector tile data.

fill-extrusion-height-[.render_height ?? .height ?? 5]

10. Pipe (right-binding sugar)

Pass the LHS as the FIRST argument to the function call on the RHS. Reads left-to-right like a Unix shell pipe — no equivalent in JS.

a | f(b)

Pipe — Equivalent to `f(a, b)`. Stack multiple pipes for chained transforms.

.speed / 50 | clamp(4, 24)

11. Ternary (lowest)

Inline conditional. Evaluates the condition first, then exactly one branch.

cond ? a : b

Conditional — When `cond` is truthy yield `a`, otherwise `b`. Right-associative — chain by repeating: `a ? b : c ? d : e`.

.level == 1 ? red-500 : .level == 2 ? amber-500 : gray-400

Where expressions go

Four positions accept the full expression grammar. Pick the one that matches your intent — the runtime + compiler choose the lowering path (per-frame, per-feature, or constant-folded) automatically.

Bracketed binding

Any utility name followed by `-[<expr>]` lets the expression decide the value at runtime. Inside the brackets the full expression grammar is available.

| fill-[.kind == "park" ? green-500 : gray-300]
| size-[clamp(.population / 1e5, 4, 24)]

Match block

Categorical lookup with explicit defaults. Works in any expression position. Pattern matches against the input on the left of the arrow.

| fill match(.continent) {
"Asia" -> red-500
"Europe" -> blue-500
_ -> gray-400
}

Filter predicate

A boolean expression on the layer's `filter:` block-property. Only features satisfying the predicate flow through to the renderer.

layer highways {
source: roads
filter: .class == "highway" && .level >= 3
}

Field-modifier conditional fill

Tailwind-style modifier: `<fieldName>:utility` applies the utility when the named property is truthy. Multiple modifiers stack — earlier wins.

| friendly:fill-green-500 hostile:fill-red-500 fill-gray-400

Was this page helpful?

Tell us what's missing