Skip to content

Symbology

Symbology is how a layer is drawn. Every layer carries a symbology field (Esri layers can omit it). There are three kinds you write inline, plus a ref that points at a reusable named entry in app/symbology.ts.

// inline simple symbology
symbology: { kind: 'simple', color: '#2c8a3e', strokeColor: '#fff', strokeWidth: 1 }

// reference a named entry from app/symbology.ts
symbology: { kind: 'ref', name: 'airports-by-passengers' }

simple

One color (plus a few paint knobs). The field meanings depend on the layer's geometry.

Field Type Default Applies to
color string — (required) point: dot fill · line: line color · polygon: fill color
width number 2 line width / polygon outline width
radius number 5 point dot radius
strokeColor string #ffffff (point), #ffffff (polygon outline) point: ring color · polygon: border color
strokeWidth number 1 (point), 0 (polygon) point: ring width · polygon: border width (0 = no border)
// a point layer
symbology: { kind: 'simple', color: '#0891b2', radius: 6, strokeColor: '#fff', strokeWidth: 3 }

// a polygon layer with a visible outline
symbology: { kind: 'simple', color: '#838291', strokeColor: '#fff', strokeWidth: 1 }

raw

A raw MapLibre paint object — full control, including data-driven expressions for categorized and graduated styling. Use this when simple isn't expressive enough.

Field Type Description
paint Record<string, unknown> A MapLibre paint object (e.g. circle-color, fill-color, line-width)
layout Record<string, unknown> Optional extra layout properties
legend LegendOverride Optional explicit legend (below)
description string A muted one-line note shown under the layer name in the legend (e.g. 'Annual passengers') — friendlier than a raw column name

Categorized (a match expression)

{
  kind: 'raw',
  description: 'Energy source',
  paint: {
    'circle-color': [
      'match', ['get', 'primsource'],
      'solar',       '#f59e0b',
      'wind',        '#22c55e',
      'natural gas', '#60a5fa',
      'coal',        '#78716c',
      /* other */    '#94a3b8',
    ],
    'circle-radius': 4,
    'circle-stroke-color': '#ffffff',
    'circle-stroke-width': 0.5,
  },
}

Graduated (an interpolate expression)

{
  kind: 'raw',
  description: 'Annual passengers',
  paint: {
    'circle-color': [
      'interpolate', ['linear'], ['get', 'passengers'],
      0, '#bfdbfe',  2000000, '#60a5fa',  8000000, '#2563eb',  20000000, '#1e3a8a',
    ],
    'circle-radius': [
      'interpolate', ['linear'], ['get', 'passengers'],
      0, 4,  2000000, 7,  8000000, 11,  20000000, 16,
    ],
  },
}

The legend automatically introspects common match and interpolate expressions on color paint props, rendering category swatches or a gradient bar. For anything it can't read, supply an explicit legend (below).

ref

Point at a named entry in app/symbology.ts so multiple layers share one definition (and you edit it in one place).

symbology: { kind: 'ref', name: 'airports-by-passengers' }

app/symbology.ts exports a symbologies registry — a map of name → a simple or raw symbology:

import type { SymbologyRegistry } from '@/core/types'

export const symbologies: SymbologyRegistry = {
  'airports-by-passengers': {
    kind: 'raw',
    description: 'Annual passengers',
    paint: { /* … */ },
  },
}

If a layer references a name that doesn't exist, the app throws a clear error at boot listing the available names.

Legend overrides (raw symbology only)

When the legend can't auto-introspect your raw paint — or you want to control exactly what's shown — add a legend field. Three shapes:

// discrete swatches
legend: {
  kind: 'categories',
  entries: [
    { label: 'Solar', color: '#f59e0b' },
    { label: 'Wind', color: '#22c55e' },
  ],
}

// a continuous gradient bar
legend: {
  kind: 'gradient',
  stops: [
    { value: 0, label: 'Low', color: '#bfdbfe' },
    { value: 20000000, label: 'High', color: '#1e3a8a' },
  ],
}

// hide this raw layer from the legend
legend: { kind: 'hidden' }

Hiding any layer from the legend

legend: { kind: 'hidden' } only applies to raw symbology. To hide a layer of any symbology kind, use the top-level hideFromLegend: true on the layer instead.

Defaults

The simple paint defaults (width: 2, radius: 5, white point stroke at width 1, no polygon border) can be set app-wide — but currently only per layer. App-wide label defaults exist (see UI & Layout); global symbology paint defaults are a planned addition.