# @mapmap/maps reference

> Generated from the published **@mapmap/maps 0.25.1** type declarations by `website/scripts/sdk-reference.mjs`. Do not edit by hand: change the JSDoc in the package, or the examples in `website/content/sdk-reference/maps.examples.md`, and regenerate.

Every public export of the web maps SDK, from the main entry point and each subpath (`/live`, `/models`, `/tiles`, `/avatars`, `/road-signs`, `/direction-icons`, `/asset-markers`). For the task-by-task guide, read [Web maps in SDKs & installation](/docs/sdks#web-maps-mapmapmaps), [Maps, tiles & Studio](/docs/maps) and [Live layers](/docs/live-layers).

```bash
npm install @mapmap/maps@0.25.1 maplibre-gl
```

Other SDK references: [@mapmap/points](/docs/points-reference), [@mapmap/core](/docs/core-reference), [@mapmap/tile](/docs/tile-reference), [@mapmap/react-native](/docs/react-native-reference), [@mapmap/voice](/docs/voice-reference), [MapMapKit](/docs/ios-reference), [ai.mapmap:core](/docs/android-reference).

663 exports across 8 entry points: 32 classes, 240 functions, 173 interfaces, 84 types, 134 constants.

## How to read this page

Each entry gives the declaration as published, the JSDoc description, and a table of parameters or properties with their types. The **Default** column quotes the default the source states (an `@default` tag or a "Default ..." sentence in the JSDoc). "required" means the property must be given; "not stated" means the source gives no default, so none is claimed here. Classes, factories and the main entry functions each carry a minimal example; examples marked *from the source* are the package's own `@example`.

## Setup used by the examples

The examples below share one map, made once in `src/map.ts`, as a reader's
own project would. Replace `snk_…` with your key.

```ts
// src/map.ts
import "maplibre-gl/dist/maplibre-gl.css";
import { createMap } from "@mapmap/maps";

export const map = createMap({ container: "map", apiKey: "snk_…", center: [-0.1276, 51.5072], zoom: 12 });
```

## Contents

- [`@mapmap/maps`](#mapmapmaps): 441 exports
- [`@mapmap/maps/direction-icons`](#mapmapmapsdirection-icons): no exports of its own, 7 re-exported from an earlier entry point
- [`@mapmap/maps/avatars`](#mapmapmapsavatars): 29 exports
- [`@mapmap/maps/road-signs`](#mapmapmapsroad-signs): 42 exports
- [`@mapmap/maps/asset-markers`](#mapmapmapsasset-markers): 53 exports, plus 26 re-exported from an earlier entry point
- [`@mapmap/maps/models`](#mapmapmapsmodels): 31 exports
- [`@mapmap/maps/tiles`](#mapmapmapstiles): 2 exports
- [`@mapmap/maps/live`](#mapmapmapslive): 65 exports

## `@mapmap/maps`

```ts
import { /* … */ } from "@mapmap/maps";
```

### Classes (@mapmap/maps)

[`AdrCheck`](#adrcheck), [`CameraAlertChip`](#cameraalertchip), [`ColourThemeLayer`](#colourthemelayer), [`CreditControl`](#creditcontrol), [`DataLayer`](#datalayer), [`FlowRouteEffectLayer`](#flowrouteeffectlayer), [`Geocoder`](#geocoder), [`GuidanceBanner`](#guidancebanner), [`InteractionRegistry`](#interactionregistry), [`IsochroneError`](#isochroneerror), [`IsochroneLayer`](#isochronelayer), [`LogoControl`](#logocontrol), [`MapMapMap`](#mapmapmap), [`MarkersLayer`](#markerslayer), [`NavigationCamera`](#navigationcamera), [`PlacesLayer`](#placeslayer), [`PositionPuck`](#positionpuck), [`ProgramCache`](#programcache), [`RouteLayer`](#routelayer), [`SearchBox`](#searchbox), [`ThemeScheduler`](#themescheduler), [`TransitStopsLayer`](#transitstopslayer), [`TransitVehiclesLayer`](#transitvehicleslayer), [`VoiceGuidance`](#voiceguidance)

#### `AdrCheck`

*Class*

```ts
class AdrCheck { … }
```

Calls `POST /adr/check` and returns the parsed compliance decision.

**Constructor and methods**

- `constructor(options: AdrCheckOptions)`
- `check(request: AdrCheckRequest): Promise<AdrCheckResult>`

**Example**

```ts
import { AdrCheck } from "@mapmap/maps";

const adr = new AdrCheck({ baseUrl: "https://api.mapmap.ai", apiKey: "snk_…" });
const result = await adr.check({ hazmat: true, tunnelCode: "D/E", tunnelCategory: "C" });
console.log(result.status, result.reason);
```

#### `CameraAlertChip`

*Class*

```ts
class CameraAlertChip { … }
```

A minimal DOM safety-alert chip: kind icon, kind label and (when known)
the limit and distance, in a calm or escalated state.

Style with the `mapmap-alert-chip` class, or pass a Studio design
(`extra.nav` `alerts` block, e.g. `map.navDesign?.alerts`) to match a
Studio theme. Framework-free and DOM-only, exactly like
[`GuidanceBanner`](#guidancebanner); head units should read [`alertChipContent`](#alertchipcontent)
instead.

The chip NEVER overlaps the speed or the limit: its position comes from
a constrained slot enum, and `aboveSpeed` reserves its own clearance.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly element` | `HTMLElement` |  | The chip root element; attach it wherever your UI needs it. |
| `readonly isEscalated` | `boolean` |  | Whether the chip is currently in its escalated state. |

**Constructor and methods**

- `constructor(container?: HTMLElement, design?: NavAlertsDesign)`
- `update(alert: CameraAlert | null, state?: { speeding?: boolean; }): void`: Renders an alert (or hides the chip with `null`). `state.speeding` drives the escalated state; the chip itself shows regardless, because the visual alert is unconditional. A kind whose design turns `alertWhileDriving` off hides the chip: the customer chose not to surface that kind while driving.

**Example**

```ts
import { CameraAlertChip } from "@mapmap/maps";

const chip = new CameraAlertChip(document.getElementById("alerts") ?? undefined);
chip.update({ kind: "speed", limitKph: 50, distanceM: 300 }, { speeding: false });
// Hide it again:
chip.update(null);
```

#### `ColourThemeLayer`

*Class*

```ts
class ColourThemeLayer implements CustomLayerInterface { … }
```

A post-process custom layer that grades the composed frame through a LUT.

Implements `CustomLayerInterface`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly id` | `string` |  |  |
| `readonly type` | `"custom"` |  |  |
| `readonly renderingMode` | `"2d"` |  |  |

**Constructor and methods**

- `constructor(options: ColourThemeLayerOptions)`
- `setLut(lut: LutBytes, strength?: number): void`: Swap the LUT (and optionally the strength); the next frame uses it.
- `setStrength(strength: number): void`: Set the blend with the ungraded frame, 0..1.
- `onAdd(map: Map, gl: WebGL2RenderingContext): void`
- `onRemove(_map: Map, gl: WebGL2RenderingContext): void`
- `render(glArg: CustomLayerGl, _args: unknown): void`

**Example**

```ts
import { map } from "./map.ts";
import { ColourThemeLayer, IDENTITY_LUT } from "@mapmap/maps";

// A LUT that leaves colours unchanged; build your own with buildLut or lutFromPngData.
const grade = new ColourThemeLayer({ lut: IDENTITY_LUT, strength: 0.8 });
map.map.addLayer(grade);
grade.setStrength(0.5);
```

#### `CreditControl`

*Class*

```ts
class CreditControl<T extends CreditInnerControl = CreditInnerControl> { … }
```

A MapLibre `IControl` that renders MapLibre's attribution as the v2 map
credit. `attribution` is the wrapped control, unchanged.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly attribution` | `T` |  | The wrapped MapLibre attribution control. |
| `readonly theme` | [`MapBrandTheme`](#mapbrandtheme) |  | The variant on show. |

**Constructor and methods**

- `constructor(attribution: T, options?: CreditOptions)`
- `getDefaultPosition(): ChromeCorner`
- `onAdd(map: unknown): HTMLElement`
- `onRemove(map?: unknown): void`
- `setTheme(theme: MapBrandTheme): void`: Switch between the Day and Night credit (same size and shape; the colour pair follows the ground).

**Example**

```ts
import * as maplibregl from "maplibre-gl";
import { CreditControl } from "@mapmap/maps";

// For a plain maplibregl.Map: createMap already adds the credit for you.
const map = new maplibregl.Map({ container: "map", style: "https://example.com/style.json", attributionControl: false });
const credit = new CreditControl(new maplibregl.AttributionControl({ compact: false }), { theme: "day" });
map.addControl(credit);
credit.setTheme("night");
```

#### `DataLayer`

*Class*

```ts
class DataLayer { … }
```

A GeoJSON or PMTiles data layer on a MapMap map. See the module docs.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ source: string; layers: string[]; }` |  | The source id and every layer id this DataLayer draws, bottom first. |

**Constructor and methods**

- `constructor(map: MapMapMap, opts: DataLayerOptions)`
- `setData(data: GeoJSON.FeatureCollection | string): void`: Replace the data (inline or a GeoJSON URL). Clears hover and selection.
- `updateData(diff: GeoJSONSourceDiff): void`: Apply a MapLibre GeoJSON diff (add, remove, update by id) without re-sending the whole dataset. Needs inline data with a unique id on every feature (or `promoteId`); the change survives a theme swap.
- `setFilter(filter: FilterSpecification | null): void`: Filter every layer of this DataLayer (`null` clears). Survives a theme swap.
- `selected(): GeoJSON.Feature[]`: The selected features.
- `select(ids: (string | number)[]): void`: Select features by id (feature id, or the `promoteId` value).
- `clearSelection(): void`: Clear the selection.
- `on(event: DataLayerEventType, handler: (e: DataLayerEvent) => void): () => void`: Listen for `click`, `hover` or `select`. Returns an unsubscribe function.
- `remove(): void`: Remove the source, its layers, the popup and every listener.

**Example**

```ts
import { map } from "./map.ts";
import { DataLayer } from "@mapmap/maps";

const sites = new DataLayer(map, {
  data: "https://example.com/sites.geojson",
  mode: "point",
  style: { categories: { property: "status", values: { open: "#1a7f37", closed: "#cf222e" } } },
});
const off = sites.on("click", (e) => console.log(e.feature?.properties));
// Later:
off();
sites.remove();
```

#### `FlowRouteEffectLayer`

*Class*

```ts
class FlowRouteEffectLayer implements CustomLayerInterface { … }
```

A MapLibre custom layer (`CustomLayerInterface`) drawing the flowing
route ribbon. Construct via [`createRouteEffect`](#createrouteeffect) (the registry) or
use `MapMapMap.setRouteEffect("flow")`, which manages the lifecycle.

Implements `CustomLayerInterface`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly id` | `string` |  |  |
| `readonly type` | `"custom"` |  |  |
| `readonly renderingMode` | `"2d"` |  |  |

**Constructor and methods**

- `constructor(geometry: RouteGeometry, options?: RouteFlowOptions)`
- `setGeometry(geometry: RouteGeometry): void`: Swap the ribbon onto a new route line (e.g. after a reroute).
- `onAdd(map: Map, gl: WebGL2RenderingContext): void`
- `onRemove(_map: Map, gl: WebGL2RenderingContext): void`
- `render(gl: CustomLayerGl, args: unknown): void`

**Example**

```ts
import { map } from "./map.ts";
import { FlowRouteEffectLayer } from "@mapmap/maps";

const flow = new FlowRouteEffectLayer(
  { type: "LineString", coordinates: [[-0.1276, 51.5072], [-0.0877, 51.5055]] },
  { width: 8 },
);
map.map.addLayer(flow);
// After a reroute:
flow.setGeometry({ type: "LineString", coordinates: [[-0.1276, 51.5072], [-0.1, 51.52]] });
```

#### `Geocoder`

*Class*

```ts
class Geocoder { … }
```

Calls the gateway's geocoding endpoints and returns parsed hits.

**Constructor and methods**

- `constructor(options: GeocoderOptions)`
- `geocode(query: string, options?: GeocodeOptions): Promise<GeocodeHit[]>`: Forward geocode: free-text query → hits, best first.
- `reverse(point: LngLatLike, options?: ReverseGeocodeOptions): Promise<GeocodeHit[]>`: Reverse geocode: point → nearest hits, nearest first.

**Example**

```ts
import { map } from "./map.ts";
import { Geocoder } from "@mapmap/maps";

// A MapMapMap satisfies GeocoderOptions, so the map's gateway and key are reused.
const geocoder = new Geocoder(map);
const [best] = await geocoder.geocode("Temple Meads, Bristol");
if (best) map.map.flyTo({ center: best.lngLat, zoom: 15 });
const here = await geocoder.reverse([-2.5813, 51.4491]);
console.log(here[0]?.name, here[0]?.distanceM);
```

#### `GuidanceBanner`

*Class*

```ts
class GuidanceBanner { … }
```

A minimal DOM banner: primary text with a maneuver icon from the platform
direction set (the same `direction-icons` glyphs the MapMap apps use, ferry, roundabouts, forks, ramps and all), plus an optional lane row.
Style with the `mapmap-banner` / `mapmap-banner-lanes` /
`mapmap-banner-icon` classes, pass a Studio design (`extra.nav` `banner`
block, e.g. `map.navDesign?.banner`) to match a Studio theme, or read
[`StepGuidance`](#stepguidance) yourself for fully custom UI. The icon is drawn in
the banner's text colour; its secondary shapes (`class="mm2"`: the rest
of a roundabout ring, other lanes) are that same colour at 35% opacity,
never a fixed grey. Retune the opacity with
`.mapmap-banner-icon .mm2 { fill-opacity: …; stroke-opacity: … }`.

Roundabouts draw for the side traffic drives on: clockwise for
left-hand traffic (`roundabout_lht_*`), anticlockwise for right-hand
traffic. The side comes from the route, never a default; when it is
unknown the banner draws the plain turn glyph for the exit direction.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly element` | `HTMLElement` |  | The banner root element; attach it wherever your UI needs it. |

**Constructor and methods**

- `constructor(container?: HTMLElement, design?: NavBannerDesign)`
- `update(banner: BannerInstruction | null, step?: Pick<StepGuidance, "mode" | "drivingSide" | "bannerIcon">): void`: Renders a banner instruction (or hides the element with `null`). `step` is icon context: pass the [`StepGuidance`](#stepguidance) entry the banner came from. Its `bannerIcon` is the glyph for the manoeuvre the banner describes (the next step's, with that step's own driving side and, at a roundabout, the paired through-angle exit), and a ferry-mode step draws the ferry glyph. Without `bannerIcon` (a hand-built `{ mode, drivingSide }`), the glyph comes from the banner's own type and modifier with that `drivingSide`; with no side a roundabout draws the plain turn glyph for its direction.

**Example**

```ts
import { GuidanceBanner } from "@mapmap/maps";

const banner = new GuidanceBanner(document.getElementById("banner") ?? undefined);
banner.update({
  distanceAlongGeometry: 120,
  primary: { text: "Baldwin Street", type: "turn", modifier: "left" },
});
```

#### `InteractionRegistry`

*Class*

```ts
class InteractionRegistry { … }
```

The per-map interaction registry behind `MapMapMap.addInteraction`.
Exported for apps that drive a plain `maplibregl.Map`.

**Constructor and methods**

- `constructor(map: Map)`
- `add(id: string, interaction: Interaction): void`: Add (or replace) the interaction `id`.
- `remove(id: string): void`: Remove the interaction `id`. Unknown ids are ignored.
- `ids(): string[]`: The registered interaction ids, in the order they were added.
- `setFeatureState(target: InteractionTarget & { id: string | number; }, state: Record<string, unknown>): void`: Set feature state on every source layer behind `target`: a featureset sets it once per distinct source and source layer (`building` and `building-3d` share one), so a paint expression reading `["feature-state", ...]` on any of those layers sees it.
- `destroy(): void`: Detach every listener and forget every interaction.

**Example**

```ts
import * as maplibregl from "maplibre-gl";
import { InteractionRegistry } from "@mapmap/maps";

// For a plain maplibregl.Map; a MapMapMap has the same thing as map.addInteraction.
const map = new maplibregl.Map({ container: "map", style: "https://example.com/style.json" });
const interactions = new InteractionRegistry(map);
interactions.add("poi-click", {
  type: "click",
  target: { featuresetId: "poi" },
  handler: (e) => {
    console.log(e.feature?.properties);
    return true; // handled: stop here
  },
});
```

#### `IsochroneError`

*Class*

```ts
class IsochroneError extends Error { … }
```

A failed `showReachability`: the HTTP status and the gateway's RFC 9457
problem fields. The message keeps the long-standing form
`isochrone request failed: HTTP <status> (<title> - <detail>)`.

Extends `Error`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly status` | `number` |  | HTTP status of the gateway's answer. |
| `readonly problemType` | `string \| undefined` |  | The problem's `type` URN, e.g. [`NO_ROADS_NEAR_ORIGIN`](#no_roads_near_origin). |
| `readonly title` | `string \| undefined` |  |  |
| `readonly detail` | `string \| undefined` |  |  |
| `readonly noRoadsNearOrigin` | `boolean` |  | True when the origin has no road within 5 km (open sea, desert): move it onto land near a road and ask again. |

**Constructor and methods**

- `constructor(status: number, problem: Record<string, unknown> | undefined)`

**Example**

```ts
import { map } from "./map.ts";
import { IsochroneError, IsochroneLayer } from "@mapmap/maps";

const reach = new IsochroneLayer(map);
try {
  await reach.showReachability({ origin: [-30, 45], minutes: [15] });
} catch (err) {
  if (err instanceof IsochroneError && err.noRoadsNearOrigin) {
    console.log("Move the origin onto land near a road", err.status);
  } else {
    throw err;
  }
}
```

#### `IsochroneLayer`

*Class*

```ts
class IsochroneLayer { … }
```

Renders reachability rings from the gateway's `POST /isochrone`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly current` | [`IsochroneFeatureCollection \| undefined`](#isochronefeaturecollection) |  | The most recently drawn rings, if any. |

**Constructor and methods**

- `constructor(map: MapMapMap | Map, options?: IsochroneLayerOptions)`
- `showReachability(options: ShowReachabilityOptions): Promise<IsochroneFeatureCollection>`: Fetch and render reachability rings. Returns the GeoJSON FeatureCollection the gateway produced (each feature carries a `contour` property in minutes), for callers who also want the raw shapes. Replaces any rings already shown by this layer.
- `clear(): void`: Remove the rings' layers and source from the map.
- `destroy(): void`: Remove the rings and detach the layer's `style.load` listener and any pending wait for the style.

**Example**

```ts
import { map } from "./map.ts";
import { IsochroneLayer } from "@mapmap/maps";

const reach = new IsochroneLayer(map);
const rings = await reach.showReachability({ origin: [-0.1276, 51.5072], mode: "driving", minutes: [10, 20, 30] });
console.log(rings.features.length);
```

#### `LogoControl`

*Class*

```ts
class LogoControl { … }
```

A MapLibre `IControl` rendering the MapMap mark. Typed structurally
(`onAdd`/`onRemove`) so it needs no maplibre-gl import and works with
any instance the host app bundles.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly theme` | [`MapBrandTheme`](#mapbrandtheme) |  | The variant on show. |

**Constructor and methods**

- `constructor(options?: LogoOptions)`
- `getDefaultPosition(): LogoPosition`
- `onAdd(): HTMLElement`
- `onRemove(): void`
- `setTheme(theme: MapBrandTheme): void`: Switch between the Day and Night mark.

**Example**

```ts
import * as maplibregl from "maplibre-gl";
import { LogoControl } from "@mapmap/maps";

// createMap adds the mark itself; this is for a plain maplibregl.Map.
const map = new maplibregl.Map({ container: "map", style: "https://example.com/style.json" });
const logo = new LogoControl({ position: "bottom-left", theme: "day" });
map.addControl(logo);
```

#### `MapMapMap`

*Class*

```ts
class MapMapMap { … }
```

The MapMap map: a branded, tiles-and-routing-ready MapLibre map.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly map` | `Map` |  | The underlying MapLibre map. Use it for any native MapLibre call. |
| `readonly apiKey` | `string \| undefined` |  | Gateway API key, if provided. |
| `readonly baseUrl` | `string` |  | Gateway base URL (no trailing slash). |
| `readonly navDesign` | [`NavDesign \| undefined`](#navdesign) |  | The Studio navigation design (`extra.nav`) parsed from the theme passed as `style`, when it carried one. `RouteLayer`, `PositionPuck` and `GuidanceBanner` pick this up as their default look; `undefined` means the theme had no nav block (or `style` was not a theme document). |
| `readonly poiDesign` | [`PoiDesign \| undefined`](#poidesign) |  | The Studio POI category design (`extra.poi`) parsed from the theme passed as `style`, when it carried one. Apply its label-text part with `applyPoiDesign(map.map, map.poiDesign)`; `undefined` means the theme had no poi block (or `style` was not a theme document). |
| `readonly groundTheme` | [`MapBrandTheme`](#mapbrandtheme) |  | `day` or `night`: the variant of the map credit and the MapMap mark on show, read off the ground of the style actually drawn. Also set as `data-mapmap-ground` on the map container, so page CSS can follow it. |

**Constructor and methods**

- `constructor(options: MapMapOptions)`
- `setTerrain(terrain: boolean | TerrainOptions): void`: Turn terrain on or off after construction. `false` removes the DEM sources and any hillshade layer this SDK added.
- `setRouteEffect(effect: RouteEffectName | null, options?: SetRouteEffectOptions): void`

  Set (or clear) the visual effect on the active route line - the
  effects-engine entry point (see `effects.ts`). The first effect is
  `"flow"`: an animated energy ribbon flowing along the route, drawn in
  a MapLibre custom layer with first-party GLSL.

  ```ts
  const route = await routes.route(from, to);
  map.setRouteEffect("flow");                       // uses the drawn route
  map.setRouteEffect("flow", { color: "#ff7a1f" }); // options
  map.setRouteEffect(null);                          // back to the plain line
  ```

  Geometry: pass `{ geometry }` explicitly, or omit it and the effect
  attaches to whatever route a `RouteLayer` on this map draws (current
  and future - RouteLayer reports every drawn line via
  `setRouteEffectGeometry`).

  Styles whose theme carried an `effects` block auto-enable their effect
  from the compiled style's metadata; calling this method (with any
  value, including `null`) overrides the style's wish for the lifetime
  of this map.

  Accessibility/resilience (see effects.ts): honours
  `prefers-reduced-motion` (static gradient, no animation) and falls
  back to the plain route line with a single console warning if WebGL
  setup fails.

- `setRouteEffectGeometry(geometry: RouteGeometry | null): void`: Attach the active route effect to a route line (or detach it with `null`). `RouteLayer` calls this on every `draw()`/`clear()`, so apps normally never do - pass `geometry` to `setRouteEffect` for routes drawn outside a RouteLayer.
- `setBuildings3d(enabled: boolean): void`: Toggle 3D building extrusions at runtime, the live equivalent of compiling the style with `buildings_3d: true` (see `Theme`): adds or removes the `building-3d` fill-extrusion layer and hands the flat `building` footprint layer off at zoom 15. The extrusion takes its colour from the current `building` fill, so it follows the theme. Idempotent; safe to call before the style has loaded (it applies on load). Requires the MapMap skeleton's `building` layer, on a custom style without one this throws rather than guessing. Mobile note: leave 3D off in native navigation views for now (maplibre-native#4107, fill-extrusion memory at street zooms).
- `setStyle(style: MapMapTheme | Theme | StyleSpecification | string, options?: Parameters<Map["setStyle"]>[1]): void`: Swap the map style: a theme name (`"light"` / `"dark"`), a Studio `Theme` document, a full `StyleSpecification` or a style URL, the same values the `style` option takes. Theme names and documents build against the same territory tiles the map was created with. Everything the SDK draws comes back on the new style: routes, places, markers, terrain, route effects, and every persistent source and layer (`addPersistentLayer`, `DataLayer`), in order, at its slot. `navDesign` and `poiDesign` stay as they were read at construction.
- `slot(slot: Slot): string | undefined`

  The `beforeId` for an insertion slot on the live style, or `undefined`
  to append on top. See `SLOT_ANCHORS` for what each slot means.

  ```ts
  map.map.addLayer(myLayer, map.slot("bottom"));
  ```

  A layer added this way is gone after the next `setStyle`; use
  `addPersistentLayer` for one that stays.

- `addPersistentSource(id: string, source: SourceSpecification): void`: Add a source that survives `setStyle`. Calling it again with the same id replaces the registered source: a GeoJSON source swaps its data in place (`setData`); any other change needs `removePersistent` first, because MapLibre cannot change a source's type or URL under the layers drawing it.
- `addPersistentLayer(layer: LayerSpecification, opts?: PersistentLayerOptions): void`

  Add a layer that survives `setStyle`, at an insertion slot or below a
  layer id. Persistent layers are re-added on every style load in the
  order they were added, each at its slot resolved against the NEW
  style, so a theme swap keeps them in order and in place.

  ```ts
  map.addPersistentSource("depots", { type: "geojson", data: depots });
  map.addPersistentLayer(
    { id: "depots", type: "circle", source: "depots", paint: { "circle-radius": 6 } },
    { slot: "middle" },
  );
  map.setStyle("dark"); // the depots stay, above the roads, below the labels
  ```

  The layer's source must be a persistent source or one every style you
  switch to carries (`territory`); a layer whose source is missing after
  a swap is skipped with one console warning.

- `removePersistent(id: string): void`: Remove a persistent source or layer, from the map and the registry. Removing a source also removes every persistent layer drawing it. Unknown ids are ignored.
- `persistentIds(): PersistentIds`: The persistent source and layer ids, in the order they were added.
- `addInteraction(id: string, interaction: Interaction): void`: Run `interaction.handler` on clicks or hovers over a layer, a basemap featureset (`poi`, `place-labels`, `buildings`) or the map itself. Shaped like Mapbox GL JS `addInteraction`; a handler returning `true` stops the event reaching interactions further down. Adding an id that exists replaces it. Interactions survive `setStyle`.
- `removeInteraction(id: string): void`: Remove the interaction `id`. Unknown ids are ignored.
- `setFeatureState(target: InteractionTarget & { id: string | number; }, state: Record<string, unknown>): void`: Set feature state on a layer's or featureset's features, for paint expressions that read `["feature-state", key]`. Basemap features carry stable ids, so this works on `poi` and `place-labels` without `promoteId`. On `buildings` it is best effort: the tiles merge neighbouring footprints of the same height into one feature, so the state lands on that whole group.
- `whenReady(): Promise<void>`: Resolves once the style and first tiles are loaded. Safe to call at any point in the map's life, not just before its first load: `map.loaded()` goes false again whenever a source is loading (a theme swap, a pan into new tiles) while `load` only ever fires once, so this waits on `idle` too rather than hanging.
- `destroy(): void`: Remove the map and release its WebGL context. Anything still queued behind a style load is dropped first, so nothing touches the map after this returns.
- `setColorTheme(theme: ColorThemeSpec | null): Promise<void>`

  Grade the whole map through a colour theme (a 16x16x16 LUT), the
  Mapbox `setColorTheme` equivalent. `null` removes it.

  ```ts
  await map.setColorTheme({ data: "https://example.com/night.png" }); // Mapbox-format 256x16 PNG
  await map.setColorTheme({ lut: bytes, strength: 0.6 });             // raw LUT bytes or base64
  map.setColorTheme(null);
  ```

  The theme is drawn by a custom layer (`"mapmap-colour-theme"`) that the
  SDK keeps on top: it moves back above anything added later, and comes
  back after `setStyle`. Strength 0 removes the layer (no identity pass),
  while `getColorTheme()` still reports it. Resolves once the LUT is
  decoded; rejects with a readable message for a bad image, a LUT that is
  not 12288 bytes, or a strength outside 0..1. A later call wins over an
  earlier one still decoding.

  Over a style already graded at compile time (one carrying
  `metadata["mapmap:color-theme"]`) it still works, and logs one warning.

- `getColorTheme(): { digest: string; strength: number; } | null`: The runtime colour theme in force: its LUT digest (FNV-1a 64, 16 hex characters, the same digest compile-time grading stamps in `metadata["mapmap:color-theme"]`) and strength, or `null` with none.
- `addClip(id: string, opts: ClipOptions): void`

  Hide the basemap's buildings (or POI labels) inside a polygon, the
  Mapbox `clip` layer equivalent. Works on its own, with no model:

  ```ts
  map.addClip("new-tower", { polygon: site });                       // buildings
  map.addClip("plaza", { polygon: plaza, types: ["buildings", "symbols"] });
  map.removeClip("new-tower");
  ```

  A feature that touches the polygon is not drawn (the style-spec
  `distance` filter, which MapLibre GL JS and Native both evaluate).
  Clips survive `setStyle`, apply to `building-3d` when
  `setBuildings3d(true)` adds it later, and two clips on one layer hide
  features inside either polygon. Calling it again with the same id
  replaces that clip. The polygon may have at most 500 vertices in total.

- `removeClip(id: string): void`: Remove a clip; each layer gets back its own filter (or the other clips').
- `clips(): string[]`: The ids of the clips in place, in the order they were added.

**Example**

```ts
import { MapMapMap } from "@mapmap/maps";

// createMap(options) is the usual way in; the constructor takes the same options.
const map = new MapMapMap({ container: "map", apiKey: "snk_…", center: [-1.8904, 52.4862], zoom: 12 });
await map.whenReady();
map.setStyle("dark");
map.setBuildings3d(true);
map.addPersistentSource("depots", {
  type: "geojson",
  data: { type: "FeatureCollection", features: [] },
});
map.addPersistentLayer({ id: "depots", type: "circle", source: "depots" }, { slot: "top" });
```

#### `MarkersLayer`

*Class*

```ts
class MarkersLayer { … }
```

Draws a theme's custom markers & labels on a MapLibre map.

```ts
const markers = await markersFromThemeUrl(themeUrl);
if (markers) new MarkersLayer(map, markers);
```

Registers every needed marker image (rasterised glyph pins are cached by
`mm-marker-<icon>-<colour>-<size>`; custom images by data-URI hash),
builds one GeoJSON feature per marker carrying its image key, label,
precomputed `icon-size` ratio and anchor, and installs the single
`mm-user-markers` source + symbol layer. Survives `setStyle` like
PlacesLayer (re-installed on every `style.load`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly current` | [`MarkerItem[]`](#markeritem) |  | The layer's current markers. |
| `readonly ids` | [`MarkersLayerIds`](#markerslayerids) |  | The MapLibre source/layer ids - public API for escape-hatch styling. Both are the contract id `mm-user-markers`. |

**Constructor and methods**

- `constructor(map: MapMapMap | Map, markers?: MarkersBlock, options?: MarkersLayerOptions)`
- `setMarkers(markers: MarkersBlock | undefined): void`: Replace the layer's markers with a parsed block (`undefined` or an empty block clears the map of markers but keeps the layer alive). Custom images load asynchronously: their markers render as default pins until the image arrives, then swap in place; a failed or oversized image keeps the pin fallback.
- `clear(): void`: Remove the markers' layer, source and registered images from the map. Images are removed by the keys this layer REGISTERED, not by whatever `items` happens to hold now, deriving them from the items meant `setMarkers(undefined)` followed by `clear()`/`destroy()` removed nothing at all.
- `destroy(): void`: Remove the markers and detach the style.load listener and any pending wait for the style. Call when disposing of the layer (the map is left untouched). Any image load still in flight resolves into a no-op.

**Example**

```ts
import { map } from "./map.ts";
import { MarkersLayer, parseMarkers } from "@mapmap/maps";

const block = parseMarkers({
  version: 1,
  items: [{ id: "hq", lng: -0.1276, lat: 51.5072, icon: "star", colour: "#0b5fff", size: "m", label: "HQ" }],
});
const markers = new MarkersLayer(map, block);
// Later:
markers.destroy();
```

#### `NavigationCamera`

*Class*

```ts
class NavigationCamera { … }
```

A follow-mode chase camera for MapMap maps.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly mode` | [`NavigationCameraMode`](#navigationcameramode) |  | The camera's current behaviour: `"follow"`, `"overview"` or `"free"`. |

**Constructor and methods**

- `static isSupported(map: MapMapMap | Map): boolean`: `false` when the map runs the globe (or vertical-perspective) projection, whose camera geometry breaks the low-anchor offset maths and top-down overview framing. Switch to mercator for navigation.
- `constructor(map: MapMapMap | Map, options?: NavigationCameraOptions)`
- `attachPuck(puck: CoDrivenPuck): void`: Co-drive a [`PositionPuck`](#positionpuck): every `follow` call also `setLocation`s the puck, so camera and puck never diverge.
- `follow(fix: CameraFix, courseDeg?: number): void`: Glide the camera to a fix, course-up when `courseDeg` is given (bearing is left unchanged when it is `undefined`). Each call eases with linear timing over `easeMs` capped by the observed fix interval, and interrupts the previous ease (no queue buildup). The fix and an attached puck are always updated, but the camera only moves in `"follow"` mode - `"free"` and `"overview"` record the fix for `resume`.
- `overview(routeGeometry?: RouteGeometry): void`: Fit the route top-down (pitch 0, north-up). Pass the route geometry on the first call; later calls reuse the last geometry given. A no-op when no geometry has ever been provided.
- `resume(): void`: Return to follow mode, gliding back to the last fix (with its last known course) when one has been recorded.
- `destroy(): void`: Remove the map listeners and cancel any pending auto-recentre.

**Example**

```ts
import { map } from "./map.ts";
import { NavigationCamera, PositionPuck } from "@mapmap/maps";

if (NavigationCamera.isSupported(map)) {
  const camera = new NavigationCamera(map, { pitch: 55, zoom: 17 });
  camera.attachPuck(new PositionPuck(map));
  navigator.geolocation.watchPosition((p) => {
    camera.follow({ lat: p.coords.latitude, lon: p.coords.longitude }, p.coords.heading ?? undefined);
  });
}
```

#### `PlacesLayer`

*Class*

```ts
class PlacesLayer { … }
```

Draws customer places on a MapLibre map, with clustering and popups.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly current` | [`Place[]`](#place) |  | The layer's current places (normalised to `Place[]`). |
| `readonly ids` | [`PlacesLayerIds`](#placeslayerids) | `"mapmap-places"` | The generated MapLibre source/layer ids - public API for escape-hatch styling (`map.setPaintProperty`, `queryRenderedFeatures`, …) beyond the layer's options. Stable for the layer's lifetime, derived from the `id` option (default `"mapmap-places"`). The cluster ids are only installed on the map with `cluster: true` (the default), and `points` is a circle layer by default or a symbol layer once a custom `icon` has loaded. |

**Constructor and methods**

- `constructor(map: MapMapMap | Map, options?: PlacesLayerOptions)`
- `setPlaces(places: PlacesInput): void`: Replace the layer's places - a `Place[]` or a GeoJSON FeatureCollection of Points. Never silently drops an update: once the source exists the data is applied immediately, even while `isStyleLoaded()` is transiently `false` mid-render (search-as-you-type just works). Before the source exists it is installed as soon as the style is parsed, even while tiles are still loading; calls made before that are stashed - the latest one wins - and installed the moment the style is ready, with no later `setPlaces` or style swap needed. With `fitBounds: true` the first non-empty set also fits the map view.
- `select(id: string, options?: PlacesSelectOptions): Place | undefined`: Programmatically select a place by id - list-to-map sync for a store finder's results list. Opens the layer's configured `popup` at the place (`popup: false` to skip, no-op without a `popup` option) and eases the camera to it (`flyTo: false` to skip; `zoom` to also zoom). Returns the selected place, or `undefined` for an unknown id (in which case nothing happens). Does NOT invoke `onPlaceClick` - a programmatic selection is not a user click.
- `deselect(): void`: Close the popup opened by `select` or a place click, if any.
- `nearest(origin: { lat: number; lon: number; }, n?: number): PlaceWithDistance[]`: The nearest `n` places (default `1`) to `origin` by straight-line (haversine) distance, each with `distanceM` attached. Pure and instant - no network. For road-network answers use `nearestByDriveTime`.
- `nearestByDriveTime(origin: { lat: number; lon: number; }, options?: NearestByDriveTimeOptions): Promise<PlaceWithDriveTime[]>`: The nearest `n` places (default `1`) to `origin` by drive time, via the gateway's `POST /matrix` (one source, all places as targets). Resolves to places sorted by `durationS` ascending, with the driven `distanceM` attached; unreachable places (`null` duration) are dropped. Needs the gateway `baseUrl`/`apiKey` - picked up from a `MapMapMap` automatically, or pass them as layer options. Answers always describe the places that were asked about: the list is snapshotted at call time, so a `setPlaces` landing mid-flight (a search-as-you-type filter, say) cannot mis-pair the matrix rows with a different set of places.
- `clear(): void`: Remove the places' layers, source, image and popup from the map.
- `destroy(): void`: Remove the places and detach every listener (style.load, clicks, hovers, and a pending wait for the style). Call when disposing of the layer (the map is left untouched).

**Example**

```ts
import { map } from "./map.ts";
import { PlacesLayer } from "@mapmap/maps";

const stores = new PlacesLayer(map, {
  places: [
    { id: "a", name: "Old Market", lat: 51.4556, lon: -2.5795 },
    { id: "b", name: "Clifton", lat: 51.4648, lon: -2.6203 },
  ],
  cluster: true,
  onPlaceClick: (place) => console.log(place.name),
});
const nearest = stores.nearest({ lat: 51.4545, lon: -2.5879 }, 1);
console.log(nearest[0]?.distanceM);
```

#### `PositionPuck`

*Class*

```ts
class PositionPuck { … }
```

A current-position puck marker for MapMap maps.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly element` | `HTMLElement` |  | The puck's root DOM element (the Marker element). |

**Constructor and methods**

- `constructor(map: MapMapMap | Map, design?: NavPuckDesign, options?: PositionPuckOptions)`: Creates the puck (not yet on the map - it appears on the first `setLocation`). The design defaults to the map's `navDesign.puck` when given a `MapMapMap` whose theme carried an `extra.nav` block, then to the built-in blue puck.
- `setLocation(location: { lat: number; lon: number; }, headingDeg?: number): void`: Moves the puck (adding it to the map on the first call). `headingDeg` rotates the whole element - arrow or custom image - clockwise from north; omit it to keep the previous heading. With interpolation on (the default) every call after the first glides from the currently rendered position - a fix arriving mid-tween retargets smoothly rather than jumping.
- `remove(): void`: Removes the puck from the map (cancelling any tween). `setLocation` re-adds it.

**Example**

```ts
import { map } from "./map.ts";
import { PositionPuck } from "@mapmap/maps";

const puck = new PositionPuck(map);
puck.setLocation({ lat: 51.5072, lon: -0.1276 }, 90);
```

#### `ProgramCache`

*Class*

```ts
class ProgramCache<T> { … }
```

One compiled program (or any per-variant object) per
`shaderData.variantName`. The prelude differs by variant, so a program
built for one projection must never draw another.

**Constructor and methods**

- `get(variant: string, build: () => T): T`
- `delete(gl: WebGL2RenderingContext, dispose: (t: T) => void): void`: Dispose of every entry and empty the cache (call from `onRemove`).

**Example**

```ts
import { ProgramCache } from "@mapmap/maps";

// One compiled program per projection variant, inside your own custom layer.
const programs = new ProgramCache<WebGLProgram>();
export function programFor(gl: WebGL2RenderingContext, variant: string): WebGLProgram {
  return programs.get(variant, () => {
    const program = gl.createProgram();
    // ...compile and link the shaders for this variant...
    return program;
  });
}
export function release(gl: WebGL2RenderingContext): void {
  programs.delete(gl, (p) => gl.deleteProgram(p)); // from onRemove
}
```

#### `RouteLayer`

*Class*

```ts
class RouteLayer { … }
```

Draws MapMap routes on a MapLibre map.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | [`RouteLayerIds`](#routelayerids) | `mapmap-route` | The MapLibre source and layer ids this layer owns - public API for escape-hatch styling and layer ordering (`map.moveLayer`). All are derived from the `id` option (default `mapmap-route`). |
| `readonly current` | [`ParsedRoute \| undefined`](#parsedroute) |  | The most recently drawn route, if any. |
| `readonly currentWaypoints` | `[number, number][]` |  | The intermediate stops currently drawn, `[lng, lat]` each. |
| `readonly alternativeRoutes` | [`readonly ParsedRoute[]`](#parsedroute) |  | Which alternatives are currently drawn, in the order given. |

**Constructor and methods**

- `constructor(map: MapMapMap | Map, options?: RouteLayerOptions)`
- `route(from: LngLatLike, to: LngLatLike, options?: RouteOptions): Promise<ParsedRoute>`: Route from `from` to `to` (with optional intermediate `via` points passed through `options` is not supported here - pass a coordinate list to `routePath` for that), draw the line, and return the parsed route.
- `routePath(points: LngLatLike[], options?: RouteOptions): Promise<ParsedRoute>`: Route through an ordered list of coordinates and draw the result. Concurrent calls are ordered by ISSUE, not by arrival: only the newest request draws. A destination retyped while the first request is still in flight used to leave the map showing whichever response happened to land last, so a driver could be looking at the line to the address they had already replaced. Each caller still receives its own parsed route, so a caller that wants the data of a superseded request keeps getting it - only the drawing is suppressed. `RouteLayer.clear` likewise supersedes anything in flight.
- `draw(route: ParsedRoute): void`: Draw (or update) a parsed route's line on the map. Safe to call before the style has loaded: the route is drawn as soon as the style is parsed, even while tiles are still loading, and before that it is stored (the latest call wins) and installed the moment the style is ready, instead of throwing MapLibre's "Style is not done loading".
- `setWaypoints(waypoints: readonly LngLatLike[]): void`: Set the intermediate stops drawn by the endpoint markers, in order. `routePath` fills these in from its own via points, so this is only needed when drawing a route parsed elsewhere (e.g. a route fetched by the app and handed to `draw`). An empty array clears them.
- `drawGeometry(coordinates: [number, number][]): void`: Draw a bare line from coordinates, without a parsed OSRM route. `RouteLayer.draw` expects a [`ParsedRoute`](#parsedroute) because it is normally fed by `RouteLayer.route`. Callers that already have geometry and nothing else, an agent tool result, a stored polyline, a hand-built preview, had to invent a `ParsedRoute` with zeroed distance and duration that `draw` never reads. This is that path, named honestly.
- `drawAlternatives(routes: ParsedRoute[], selectedIndex?: number): void`: Draw a set of routes: one selected, the rest as dimmer alternatives beneath it. Offering alternatives is table stakes for a navigation UI, and the selected route keeps every feature of `RouteLayer.draw` (progress, manoeuvre arrow, corridors, route effects). Pass the index of the route to select; out-of-range values clamp to the first route. Calling with a single route is equivalent to `draw`, and calling with an empty array clears everything.
- `onSelectAlternative(handler: ((index: number) => void) | undefined): void`: Register a click handler for the alternative lines. The index is the position within the array last passed to `RouteLayer.drawAlternatives`, so a caller can re-issue that call with the new selection. Passing `undefined` removes the handler.
- `setFerrySegments(segments: [number, number][][]): void`: Draw ferry legs of the selected route as dashes over its line, so water crossings do not read as driving. Supplied as explicit geometries rather than derived from the route, because `line-dasharray` cannot be data-driven: a ferry leg needs its own layer, and only the caller knows which parts of their route are ferries.
- `clearFerrySegments(): void`: Remove the ferry dashes.
- `setLineStyle(style: RouteLineStyle): void`: Override the drawn line's paint at runtime, over whatever the design or the built-in look set. Route styling is not always static: a route can be provisional, or a straight-line approximation that must not be mistaken for a surveyed one. That is a paint change on a route already drawn, so it belongs here rather than in the constructor's design. Overrides survive style reloads. Pass `{}` to clear them.
- `setProgress(fraction: number): void`: Sets how much of the route has been travelled, as a fraction in `[0, 1]` of the line's length. The travelled part dims to `progressColor` (the "vanishing route line"); `0` restores the untinted line. The value is remembered across `draw` calls and style swaps. Pair with the guidance module's distance-remaining to derive the fraction.
- `setManeuver(lngLat: [number, number], bearingDeg: number): void`: Shows (or moves) the upcoming-manoeuvre arrow: a small map-aligned arrow at `lngLat` rotated to `bearingDeg` (clockwise from north). Survives style swaps until `clearManeuver`.
- `clearManeuver(): void`: Hides the manoeuvre arrow.
- `setAlertCorridors(corridors: readonly (readonly [number, number])[][], design?: NavAlertsDesign): void`: Highlights average-speed enforcement corridors on the route line. A corridor is a stretch of the route between an entry and an exit camera, so it is a property of the LINE, not a point alert: drawing it as a tinted overlay above the route is what makes "you are inside a section check right now" readable at a glance, and it is the only part of the alert design that persists rather than firing once. Pass the corridor geometries (arrays of `[lng, lat]` along the route) and, optionally, a Studio alerts design for the colour and opacity; without one the layer uses the design carried by the owning map, then a built-in tint. An empty array clears the highlight. Survives style swaps like the route itself.
- `clearAlertCorridors(): void`: Removes the corridor highlight (the route line itself is untouched).
- `clear(): void`: Remove the route's layers and source from the map. Any route request still in flight is superseded, so a late response cannot redraw a route the caller has already cleared.
- `destroy(): void`: Remove the route and detach the layer's `style.load` listener and any pending wait for the style. Call when disposing of the layer (the map itself is left untouched).

**Example**

```ts
import { map } from "./map.ts";
import { RouteLayer } from "@mapmap/maps";

const routes = new RouteLayer(map);
const route = await routes.route([-0.1276, 51.5072], [-1.2577, 51.752], {
  profile: "truck",
  truck: { heightM: 4.2, weightT: 26 },
});
console.log(`${(route.distanceM / 1000).toFixed(1)} km, ${Math.round(route.durationS / 60)} min`);
```

#### `SearchBox`

*Class*

```ts
class SearchBox { … }
```

A MapLibre `IControl` search bar: debounced suggestions, keyboard
navigation, and a typed `"select"` event carrying the picked feature.

```ts
const box = new SearchBox({ apiKey: "snk_…" });
box.on("select", ({ suggestion, hit }) => {
  console.log(suggestion.name, hit?.postcode);
});
map.map.addControl(box, "top-left");
```

**Constructor and methods**

- `constructor(options?: SearchBoxOptions)`
- `on(event: "select", listener: SearchBoxSelectListener): () => void`: Subscribe to the `"select"` event. Returns an unsubscribe function.
- `off(event: "select", listener: SearchBoxSelectListener): void`: Unsubscribe a `"select"` listener.
- `onAdd(map?: SearchBoxMap): HTMLElement`: The `IControl` hook: build the DOM and take the map handle.
- `onRemove(): void`: The `IControl` hook: tear everything down.

**Example**

```ts
import type { IControl } from "maplibre-gl";
import { map } from "./map.ts";
import { SearchBox } from "@mapmap/maps";

const search = new SearchBox({ apiKey: "snk_…", placeholder: "Search places" });
// Up to 0.25.1, SearchBox's type does not match maplibre-gl 6's IControl
// (isStyleLoaded may return void there), so strict TypeScript needs this cast.
// Fixed at source for the next release, after which addControl(search) compiles as is.
map.map.addControl(search as unknown as IControl, "top-left");
search.on("select", (e) => console.log(e.suggestion.name, e.hit?.postcode));
```

#### `ThemeScheduler`

*Class*

```ts
class ThemeScheduler { … }
```

Applies the sun-appropriate theme now and at every subsequent
sunrise/sunset until `dispose`. Pair with the OS dark-mode signal in the app if the
user's system preference should win instead.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly current` | `"light" \| "dark" \| undefined` |  | The theme most recently applied, if any. |

**Constructor and methods**

- `constructor(options: ThemeSchedulerOptions)`
- `setPosition(lat: number, lng: number): void`: Moves the observer (e.g. a new GPS fix region) and re-evaluates.
- `dispose(): void`: Stops all future flips.

**Example**

```ts
import { map } from "./map.ts";
import { ThemeScheduler } from "@mapmap/maps";

// Light map by day, dark after sunset, at the map's location.
const scheduler = new ThemeScheduler({
  lat: 51.5072,
  lng: -0.1276,
  onLight: () => map.setStyle("light"),
  onDark: () => map.setStyle("dark"),
});
// Later:
scheduler.dispose();
```

#### `TransitStopsLayer`

*Class*

```ts
class TransitStopsLayer { … }
```

Bus stops and bus lines from the MapMap tiles host (see module docs).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ sources: string[]; layers: string[]; }` |  | The source and layer ids this layer adds. |
| `readonly ready` | `Promise<boolean>` |  | Resolves true once the layers are added, false when the tiles index announces neither archive (the feature is not published there). |

**Constructor and methods**

- `constructor(map: MapMapMap, options?: TransitStopsLayerOptions)`
- `remove(): void`: Remove the sources, layers and handlers.

**Example**

```ts
import { map } from "./map.ts";
import { TransitStopsLayer } from "@mapmap/maps";

const stops = new TransitStopsLayer(map, {
  onStopClick: (stop) => console.log(stop.id, stop.name, stop.lines),
});
// Later:
stops.remove();
```

#### `TransitVehiclesLayer`

*Class*

```ts
class TransitVehiclesLayer { … }
```

Live bus positions from `GET /v1/transit/vehicles`, drawn smoothly.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ source: string; layers: string[]; }` |  | The source and layer ids this layer owns. |
| `readonly owner` | [`MapMapMap`](#mapmapmap) |  | The map this layer draws on (for companion layers such as the 3D buses). |
| `readonly defaultVehicleType` | [`BusVehicleType`](#busvehicletype-1) |  | What a bus whose feed names no body type is drawn as (`defaultVehicleType`, default `"generic"`). |

**Constructor and methods**

- `constructor(map: MapMapMap, options?: TransitVehiclesLayerOptions)`
- `start(): void`: Start polling and drawing. Safe to call before the style has loaded: the layer installs as soon as the style is parsed, even while tiles are still loading, and before that waits once for it (`StyleWait`).
- `stop(): void`: Stop polling and drawing. The vehicles last drawn stay on the map.
- `remove(): void`: Stop, and remove the layer's layers, source, image and popup.
- `vehicles(): TransitVehicle[]`: The vehicles as last drawn (interpolated positions).
- `feedStatus(): TransitFeedReport`: The feed's freshness after the latest poll (the last [`TransitFeedReport`](#transitfeedreport)).
- `lastAnswer(): TransitFeedAnswer | null`: The last successful answer's counts, or null before the first answer.
- `setFollowed(id: string | null): void`: Follow one vehicle by id, or `null` to stop. Its feature carries `followed: true`; with `marker: "bus"` it wears a ring (2 px white, 2 px ink) and the others draw at 55 %. The camera is yours to move.
- `followed(): string | null`: The id passed to `setFollowed`, or `null`.
- `onDraw(listener: (vehicles: readonly TransitDrawnVehicle[]) => void): () => void`: Call `listener` with the vehicles as drawn after every draw (at most 10 a second while vehicles move) and when the followed vehicle changes. Returns the unsubscribe function. The 3D buses (`TransitVehicles3dLayer`, `@mapmap/maps/models`) are fed this way.
- `setSpriteOpacity(factor: number): void`: Fade the top-down sprite and its followed ring (`marker: "bus"`), or the heading arrow (the default), by `factor` (0 to 1, on top of every other dim), so a companion layer can crossfade with it. Labels, pucks and dots are untouched. Default 1.

**Example**

```ts
import { map } from "./map.ts";
import { TransitVehiclesLayer } from "@mapmap/maps";

const buses = new TransitVehiclesLayer(map, {
  bbox: "viewport",
  onFeedStatus: (report) => console.log(report.status),
});
buses.start();
```

#### `VoiceGuidance`

*Class*

```ts
class VoiceGuidance { … }
```

Speaks core guidance updates through the browser's `SpeechSynthesis`,
with severity-aware prosody, optional earcons, and mute/volume
controls. Feed every update from the wasm `GuidanceSession` to
`VoiceGuidance.update`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly available` | `boolean` |  | Whether this environment can speak at all. |
| `readonly muted` | `boolean` |  | Whether announcements are currently muted. |

**Constructor and methods**

- `constructor(options?: VoiceGuidanceOptions)`
- `mute(): void`: Mute announcements (also cancels anything mid-utterance).
- `unmute(): void`: Unmute announcements. Prompts seen while muted are not replayed.
- `setVolume(volume: number): void`: Set the volume for speech and earcons (clamped to 0–1).
- `update(update: VoiceGuidanceUpdate): void`: Consume one guidance update. Speaks the update's prompt the first time its `utteranceId` appears; plays the warning earcon once per off-route episode. Safe to call in any environment.
- `dispose(): void`: Cancel any speech in progress and forget the de-duplication state.

**Example**

```ts
import { VoiceGuidance } from "@mapmap/maps";

const voice = new VoiceGuidance({ lang: "en-GB", volume: 0.9 });
voice.update({
  state: "navigating",
  spoken: { text: "In 300 metres, turn left", triggerDistanceM: 300, utteranceId: "step-4" },
});
voice.mute();
```

### Functions (@mapmap/maps)

[`addBusMarkerImages`](#addbusmarkerimages), [`alertChipContent`](#alertchipcontent), [`alertContrastIssues`](#alertcontrastissues), [`alertIconSvg`](#alerticonsvg), [`alertIconUrl`](#alerticonurl), [`alertPresentation`](#alertpresentation), [`alertSoundUrl`](#alertsoundurl), [`alertSpeedReadoutColor`](#alertspeedreadoutcolor), [`applyColourTheme`](#applycolourtheme), [`applyGeoJSONDiff`](#applygeojsondiff), [`applyPoiDesign`](#applypoidesign), [`applyTerrain`](#applyterrain), [`bannerLanes`](#bannerlanes), [`baseTheme`](#basetheme), [`bearingBetween`](#bearingbetween), [`bindFlythroughToScroll`](#bindflythroughtoscroll), [`buildAdrCheckBody`](#buildadrcheckbody), [`buildGeocodeUrl`](#buildgeocodeurl), [`buildLut`](#buildlut), [`buildProbeUrl`](#buildprobeurl), [`buildRetrieveUrl`](#buildretrieveurl), [`buildReverseGeocodeUrl`](#buildreversegeocodeurl), [`buildRouteQuery`](#buildroutequery), [`buildRouteUrl`](#buildrouteurl), [`buildStyle`](#buildstyle), [`buildSuggestUrl`](#buildsuggesturl), [`buildVertexSource`](#buildvertexsource), [`builtInPoiColor`](#builtinpoicolor), [`busMarkerLayers`](#busmarkerlayers), [`busPuckImage`](#buspuckimage), [`busPuckImageOptions`](#buspuckimageoptions), [`busSilhouetteSdf`](#bussilhouettesdf), [`busSpriteBody`](#busspritebody), [`busTickImage`](#bustickimage), [`busVehicleType`](#busvehicletype), [`cameraKindLabel`](#camerakindlabel), [`cameraKindsShownOnMap`](#camerakindsshownonmap), [`cameraShownOnMap`](#camerashownonmap), [`cameraSpriteName`](#cameraspritename), [`cameraSymbolLayer`](#camerasymbollayer), [`capTransitBbox`](#captransitbbox), [`checkSourceUrl`](#checksourceurl), [`clipFilter`](#clipfilter), [`colourThemeMetadata`](#colourthememetadata), [`contrastRatio`](#contrastratio), [`createMap`](#createmap), [`createRouteEffect`](#createrouteeffect), [`creditTier`](#credittier), [`dataLayerColor`](#datalayercolor), [`defaultNavAlertsDesign`](#defaultnavalertsdesign), [`defaultNavDesign`](#defaultnavdesign), [`defaultPoiDesign`](#defaultpoidesign), [`directionArrow`](#directionarrow), [`directionIconSvg`](#directioniconsvg), [`effectsFromStyleMetadata`](#effectsfromstylemetadata), [`extractGuidance`](#extractguidance), [`featuresetLayerIds`](#featuresetlayerids), [`fixSetFromCollection`](#fixsetfromcollection), [`flythrough`](#flythrough), [`flythroughPose`](#flythroughpose), [`formatCoord`](#formatcoord), [`formatCoords`](#formatcoords), [`globeModelMatrix`](#globemodelmatrix), [`gradeColourString`](#gradecolourstring), [`gradeStyleColours`](#gradestylecolours), [`hasBakedMarkers`](#hasbakedmarkers), [`haversineDistanceM`](#haversinedistancem), [`holdLayersOnTop`](#holdlayersontop), [`iconNameForManeuver`](#iconnameformaneuver), [`iconNameForStep`](#iconnameforstep), [`iconNamesForSteps`](#iconnamesforsteps), [`interpolateFixes`](#interpolatefixes), [`isMarkerGlyphId`](#ismarkerglyphid), [`isNameTextField`](#isnametextfield), [`isSlot`](#isslot), [`keyedStyleUrl`](#keyedstyleurl), [`keyedTilesUrl`](#keyedtilesurl), [`laneArrowDirection`](#lanearrowdirection), [`languageTextField`](#languagetextfield), [`layersOnTopMoves`](#layersontopmoves), [`liveTargetLayerIds`](#livetargetlayerids), [`lngLatToMercator`](#lnglattomercator), [`locateProjectionUniforms`](#locateprojectionuniforms), [`lutApply`](#lutapply), [`lutDigest`](#lutdigest), [`lutFromBase64`](#lutfrombase64), [`lutFromImageData`](#lutfromimagedata), [`lutFromPngData`](#lutfrompngdata), [`lutToBase64`](#luttobase64), [`lutToImageData`](#luttoimagedata), [`mapBrandSvg`](#mapbrandsvg), [`mapGroundTheme`](#mapgroundtheme), [`markerImage`](#markerimage), [`markerImageDataKey`](#markerimagedatakey), [`markerImageKey`](#markerimagekey), [`markersFromTheme`](#markersfromtheme), [`markersFromThemeUrl`](#markersfromthemeurl), [`markerText`](#markertext), [`markerTextImageKey`](#markertextimagekey), [`mercatorModelMatrix`](#mercatormodelmatrix), [`modelClipMatrices`](#modelclipmatrices), [`multiplyMat4`](#multiplymat4), [`namedBase`](#namedbase), [`navAlertLeadDistanceM`](#navalertleaddistancem), [`navAlertSoundUrl`](#navalertsoundurl), [`navCameraKind`](#navcamerakind), [`navDesignFromTheme`](#navdesignfromtheme), [`navDesignFromThemeUrl`](#navdesignfromthemeurl), [`osmAndMoreCredit`](#osmandmorecredit), [`parseCssColour`](#parsecsscolour), [`parseGeocodeResponse`](#parsegeocoderesponse), [`parseHexColor`](#parsehexcolor), [`parseMarkers`](#parsemarkers), [`parseNavAlertsDesign`](#parsenavalertsdesign), [`parseNavDesign`](#parsenavdesign), [`parseOsrmRoute`](#parseosrmroute), [`parsePoiDesign`](#parsepoidesign), [`parseSuggestResponse`](#parsesuggestresponse), [`placesFromGeoJSON`](#placesfromgeojson), [`poiDesignFromTheme`](#poidesignfromtheme), [`poiDesignFromThemeUrl`](#poidesignfromthemeurl), [`poiDesignIsDefault`](#poidesignisdefault), [`poiTextColorExpression`](#poitextcolorexpression), [`prefersReducedMotion`](#prefersreducedmotion), [`projectionVariant`](#projectionvariant), [`readProjection`](#readprojection), [`registerPmtilesProtocol`](#registerpmtilesprotocol), [`relativeLuminance`](#relativeluminance), [`removeTerrain`](#removeterrain), [`renderClockMs`](#renderclockms), [`renderMarkerImage`](#rendermarkerimage), [`resetDiagnostics`](#resetdiagnostics), [`resolveBaseId`](#resolvebaseid), [`resolveSlot`](#resolveslot), [`resolveTerrain`](#resolveterrain), [`resolveTheme`](#resolvetheme), [`runMapDiagnostics`](#runmapdiagnostics), [`setMapLanguage`](#setmaplanguage), [`setProjectionUniforms`](#setprojectionuniforms), [`severityProsody`](#severityprosody), [`shortCredit`](#shortcredit), [`shortestArcDeg`](#shortestarcdeg), [`shortestArcDelta`](#shortestarcdelta), [`speak`](#speak), [`sphereFrame`](#sphereframe), [`sphereSurface`](#spheresurface), [`ssmlToText`](#ssmltotext), [`styleFromBase`](#stylefrombase), [`styleGroundTheme`](#stylegroundtheme), [`subdivideMercator`](#subdividemercator), [`sunTimes`](#suntimes), [`supportsTerrainDraping`](#supportsterraindraping), [`territoryTilesSourceUrl`](#territorytilessourceurl), [`tessellateRouteRibbon`](#tessellaterouteribbon), [`tidyPlaceName`](#tidyplacename), [`toLngLat`](#tolnglat), [`toPmtilesUrl`](#topmtilesurl), [`transitArchiveUrl`](#transitarchiveurl), [`transitFreshnessLabel`](#transitfreshnesslabel), [`transitStopFromProperties`](#transitstopfromproperties), [`transitTileJsonUrl`](#transittilejsonurl), [`transitUrlsFromIndex`](#transiturlsfromindex), [`truncateChars`](#truncatechars), [`uploadProbeBatch`](#uploadprobebatch), [`validateColourTheme`](#validatecolourtheme), [`validateUseTheme`](#validateusetheme)

#### `addBusMarkerImages`

*Function*

```ts
function addBusMarkerImages(map: Pick<Map, "hasImage" | "addImage">): void;
```

Add the bus marker's images to the style, skipping any already there.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `Pick<Map, "hasImage" \| "addImage">` | required |  |

#### `alertChipContent`

*Function*

```ts
function alertChipContent(design: NavAlertsDesign, alert: CameraAlert): AlertChipContent;
```

The text/icon content of an alert chip, free of any styling tokens.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `alert` | [`CameraAlert`](#cameraalert) | required |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`CameraAlert`](#cameraalert).

#### `alertContrastIssues`

*Function*

```ts
function alertContrastIssues(design: NavAlertsDesign): AlertContrastIssue[];
```

Every chip colour pair in `design` that fails the contrast floor, in
state order. An empty array means both states are legible (or, for
colours this module cannot parse, not judgeable - those are skipped
rather than reported as failures).

Advisory by design: the parser stays lenient and never rejects a theme
on contrast. Studio surfaces these as warnings next to the pickers so a
designer sees the problem while choosing, which is the only moment the
warning can actually change the outcome.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign).

#### `alertIconSvg`

*Function*

```ts
function alertIconSvg(design: NavAlertsDesign, kind: NavCameraKind | string): string;
```

The icon for a kind under a design, as a complete inline SVG string.

`iconSet` chooses the frame (a European circular plate, a US rounded
plate, or no frame); the glyph inside always encodes the KIND, because
that is the channel kind is allowed to travel on. `"brand"` draws the
unframed glyph as its fallback - it expects an `iconUrlOverrides` entry,
and [`alertIconUrl`](#alerticonurl) is what actually resolves one.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `kind` | `NavCameraKind \| string` | required |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`NavCameraKind`](#navcamerakind-1).

#### `alertIconUrl`

*Function*

```ts
function alertIconUrl(design: NavAlertsDesign, kind: NavCameraKind | string): string | undefined;
```

The override URL for a kind, if the design sets one. Overrides win over
`iconSet` for that kind only; every other kind keeps the curated icon.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `kind` | `NavCameraKind \| string` | required |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`NavCameraKind`](#navcamerakind-1).

#### `alertPresentation`

*Function*

```ts
function alertPresentation(design: NavAlertsDesign, alert: CameraAlert, state?: {
    speeding?: boolean;
}): AlertPresentation;
```

Resolves a design plus live state into what to present.

Escalation is by INTENSITY, not identity: `escalated` restyles the same
chip and re-uses the same earcon motif more insistently. It never swaps
in a different icon or a different sound - kind is carried by the icon
and by speech, urgency by timbre, and mixing the two channels is how
drivers end up unable to tell "a camera" from "you are speeding".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `alert` | [`CameraAlert`](#cameraalert) | required |  |
| `state?` | `{ speeding?: boolean; }` | not stated |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`CameraAlert`](#cameraalert).

#### `alertSoundUrl`

*Function*

```ts
function alertSoundUrl(design: NavAlertsDesign, escalated: boolean): string;
```

The earcon URL for a design in the calm or escalated state.

A design-level `alertSoundUrl` / `escalatedSoundUrl` override wins;
otherwise the chosen built-in resolves against the hosted earcon
library. There is deliberately no per-kind sound: kind travels in the
icon and in speech, urgency travels in timbre.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `escalated` | `boolean` | required |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign).

#### `alertSpeedReadoutColor`

*Function*

```ts
function alertSpeedReadoutColor(design: NavAlertsDesign, escalated: boolean): string | undefined;
```

The colour a host should paint the SPEED readout, given a design and
whether the driver is speeding.

Deliberate division of labour: the escalation the driver reads is the
speed number turning the escalated colour, while the alert chip stays
legible in its own escalated tokens. Colouring the card and leaving the
speed untouched puts the urgency on the wrong element - the driver has
to act on the speed, so the speed is what changes. Returns `undefined`
when calm, meaning "leave the readout in its normal colour".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `escalated` | `boolean` | required |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign).

#### `applyColourTheme`

*Function*

```ts
function applyColourTheme(style: StyleSpecification, theme: {
    color_theme?: ColourTheme;
    layers?: Record<string, {
        use_theme?: unknown;
    }>;
}): StyleSpecification;
```

The compilers' last step: grade `style` through `theme.color_theme`,
skipping the layers whose override says `use_theme: "none"`, and stamp
`metadata["mapmap:color-theme"]` as the third metadata key (after effects
and territories). A theme without `color_theme` returns `style` untouched,
so its bytes do not move. LOCKSTEP with the end of `sn_style::compile`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `style` | `StyleSpecification` | required |  |
| `theme` | `{ color_theme?: ColourTheme; layers?: Record<string, { use_theme?: unknown; }>; }` | required |  |

Options and types: [`ColourTheme`](#colourtheme).

#### `applyGeoJSONDiff`

*Function*

```ts
function applyGeoJSONDiff(data: GeoJSON.FeatureCollection, diff: GeoJSONSourceDiff, promoteId?: string): GeoJSON.FeatureCollection;
```

Apply a MapLibre GeoJSON diff to a local FeatureCollection.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `data` | `GeoJSON.FeatureCollection` | required |  |
| `diff` | `GeoJSONSourceDiff` | required |  |
| `promoteId?` | `string` | not stated |  |

#### `applyPoiDesign`

*Function*

```ts
function applyPoiDesign(map: PoiPaintMap, design: PoiDesign, fallbackTextColor?: string): void;
```

Applies a design's per-category label text colours to every `poi-labels`
layer on a live map (including per-territory clones named
`poi-labels@<territory>`). `fallbackTextColor` is the colour
non-overridden categories keep - pass the style's `poi-labels` text
colour (the theme's `textSecondary` slot; defaults to the light-base
value). A default design restores the plain fallback, so this is safe to
call on every design change.

NOTE: runtime paint changes do not survive a later `setStyle`; re-apply
after style swaps (the MapMap website bakes the expression into the
style object instead for exactly this reason).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `PoiPaintMap` | required |  |
| `design` | [`PoiDesign`](#poidesign) | required |  |
| `fallbackTextColor?` | `string` | not stated |  |

Options and types: [`PoiDesign`](#poidesign).

#### `applyTerrain`

*Function*

```ts
function applyTerrain(map: Map, cfg: ResolvedTerrain): void;
```

Add the sources/layers and switch terrain on. Safe to call repeatedly.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `Map` | required |  |
| `cfg` | `ResolvedTerrain` | required |  |

#### `bannerLanes`

*Function*

```ts
function bannerLanes(banner: BannerInstruction): LaneIndication[];
```

Lane entries of a banner's sub line, if it carries a lane diagram.

Banner lane components only carry `active` (the recommended lane), the
gateway drops the per-lane `valid` flag from banners, keeping it in the
raw route's `intersections[].lanes`. An active lane is by definition
usable, so `valid` is `true` for active lanes and *unknown* (undefined,
never `false`) for the rest: a non-recommended lane may still be legal.
Read `route.raw` `legs[].steps[].intersections[].lanes` for the full
`valid` set.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `banner` | [`BannerInstruction`](#bannerinstruction) | required |  |

Options and types: [`BannerInstruction`](#bannerinstruction).

#### `baseTheme`

*Function*

```ts
function baseTheme(id: string): Theme | undefined;
```

A fresh theme document for a named base, or `undefined` for an id the
catalogue never had. Every call returns its own object, so editing one is
safe.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |

#### `bearingBetween`

*Function*

```ts
function bearingBetween(a: [number, number], b: [number, number]): number;
```

Initial great-circle bearing from `a` to `b`, degrees clockwise from
north, normalised to `[0, 360)`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `a` | `[number, number]` | required |  |
| `b` | `[number, number]` | required |  |

#### `bindFlythroughToScroll`

*Function*

```ts
function bindFlythroughToScroll(controller: FlythroughController, target?: HTMLElement | Window): () => void;
```

Scrollytelling: bind a flythrough to a scroll position, so scrolling a
story column scrubs the camera along the route. The binder pauses the
controller (scroll owns the timeline) and seeks it to the scrolled
fraction - immediately on bind, then on every scroll. Returns the
unbind function.

```ts
const replay = flythrough(map, route);
const unbind = bindFlythroughToScroll(replay, document.querySelector("#story")!);
```

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `controller` | [`FlythroughController`](#flythroughcontroller) | required |  |
| `target?` | `HTMLElement \| Window` | not stated |  |

Options and types: [`FlythroughController`](#flythroughcontroller).

#### `buildAdrCheckBody`

*Function*

```ts
function buildAdrCheckBody(request: AdrCheckRequest): Record<string, unknown>;
```

Builds the exact wire body the gateway deserialises
(`sn-gateway` `routes/adr.rs` → `sn_adr::AdrVehicleProfile`):
`{"adr": {"dimensions": {…}, "tunnel_code"?, "hazmat"}, "tunnel_category"}`.
Unset dimensions default to the EU 96/53/EC artic maximums, mirroring
`TruckDimensions::default()`. Exported for contract tests.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `request` | [`AdrCheckRequest`](#adrcheckrequest) | required |  |

Options and types: [`AdrCheckRequest`](#adrcheckrequest).

#### `buildGeocodeUrl`

*Function*

```ts
function buildGeocodeUrl(baseUrl: string, query: string, options?: GeocodeOptions): string;
```

Build the full forward-geocode URL. `baseUrl` is the gateway origin
(trailing slashes are trimmed). Note the API key is NOT embedded -
fetches of this URL need the `Authorization: Bearer snk_…` header,
exactly like [`buildRouteUrl`](#buildrouteurl).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required |  |
| `query` | `string` | required |  |
| `options?` | [`GeocodeOptions`](#geocodeoptions) | not stated |  |

Options and types: [`GeocodeOptions`](#geocodeoptions).

#### `buildLut`

*Function*

```ts
function buildLut(fn: (r: number, g: number, b: number) => [number, number, number]): LutBytes;
```

Build a LUT by evaluating `fn` at every lattice point. `fn` gets the
lattice indices (0..15) and returns 0..255 channel values, which are
rounded and clamped.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `fn` | `(r: number, g: number, b: number) => [number, number, number]` | required |  |

#### `buildProbeUrl`

*Function*

```ts
function buildProbeUrl(baseUrl: string): string;
```

The `/v1/probe` endpoint for a gateway origin (trailing slashes trimmed).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required |  |

#### `buildRetrieveUrl`

*Function*

```ts
function buildRetrieveUrl(gatewayUrl: string, id: string): string;
```

Build the `GET /geocode/retrieve` URL for one document id.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `gatewayUrl` | `string` | required |  |
| `id` | `string` | required |  |

#### `buildReverseGeocodeUrl`

*Function*

```ts
function buildReverseGeocodeUrl(baseUrl: string, point: LngLatLike, options?: ReverseGeocodeOptions): string;
```

Build the full reverse-geocode URL. Same auth caveat as forward.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required |  |
| `point` | [`LngLatLike`](#lnglatlike) | required |  |
| `options?` | [`ReverseGeocodeOptions`](#reversegeocodeoptions) | not stated |  |

Options and types: [`LngLatLike`](#lnglatlike), [`ReverseGeocodeOptions`](#reversegeocodeoptions).

#### `buildRouteQuery`

*Function*

```ts
function buildRouteQuery(truck?: TruckParams, guidance?: GuidanceQuery): string;
```

Build the OSRM query string (without the leading `?`) for a request.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `truck?` | [`TruckParams`](#truckparams) | not stated |  |
| `guidance?` | `GuidanceQuery` | not stated |  |

Options and types: [`TruckParams`](#truckparams).

#### `buildRouteUrl`

*Function*

```ts
function buildRouteUrl(baseUrl: string, profile: RouteProfile, points: LngLatLike[], truck?: TruckParams, guidance?: GuidanceQuery): string;
```

Build the full OSRM route URL. `baseUrl` is the gateway origin (trailing
slashes are trimmed); `points` is the ordered coordinate path.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required |  |
| `profile` | [`RouteProfile`](#routeprofile) | required |  |
| `points` | [`LngLatLike[]`](#lnglatlike) | required |  |
| `truck?` | [`TruckParams`](#truckparams) | not stated |  |
| `guidance?` | `GuidanceQuery` | not stated |  |

Options and types: [`RouteProfile`](#routeprofile), [`LngLatLike`](#lnglatlike), [`TruckParams`](#truckparams).

#### `buildStyle`

*Function*

```ts
function buildStyle(options?: BuildStyleOptions): StyleSpecification;
```

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `options?` | [`BuildStyleOptions`](#buildstyleoptions) | not stated |  |

Options and types: [`BuildStyleOptions`](#buildstyleoptions).

#### `buildSuggestUrl`

*Function*

```ts
function buildSuggestUrl(gatewayUrl: string, query: string, options?: SuggestUrlOptions): string;
```

Build the `GET /geocode/suggest` URL. The API key is NOT embedded, fetches need the `Authorization: Bearer snk_…` header, exactly like
[`buildGeocodeUrl`](#buildgeocodeurl).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `gatewayUrl` | `string` | required |  |
| `query` | `string` | required |  |
| `options?` | [`SuggestUrlOptions`](#suggesturloptions) | not stated |  |

Options and types: [`SuggestUrlOptions`](#suggesturloptions).

#### `buildVertexSource`

*Function*

```ts
function buildVertexSource(args: ProjectionArgs, body: string): string;
```

A GLSL ES 3.00 vertex shader around MapLibre's projection prelude. `body`
uses `in`/`out` and calls `projectTile(vec2)` (mercator 0..1, ground) or
`projectTileFor3D(vec2, float elevationMetres)`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `args` | [`ProjectionArgs`](#projectionargs) | required |  |
| `body` | `string` | required |  |

Options and types: [`ProjectionArgs`](#projectionargs).

#### `builtInPoiColor`

*Function*

```ts
function builtInPoiColor(categoryId: string): string | undefined;
```

The built-in dot colour for a category id.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `categoryId` | `string` | required |  |

#### `busMarkerLayers`

*Function*

```ts
function busMarkerLayers(prefix: string, source: string, opacity: {
    feed: number;
    othersDimmed: boolean;
}): LayerSpecification[];
```

The bus marker's layer specs, bottom to top: the dot (z8 to 12), the
heading tick and the line-number puck (z12 to 15.5), the followed ring,
the sprite and its label (z15.5 and in). `feed` is the feed-level
opacity, `othersDimmed` whether a vehicle is followed.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `prefix` | `string` | required |  |
| `source` | `string` | required |  |
| `opacity` | `{ feed: number; othersDimmed: boolean; }` | required |  |

#### `busPuckImage`

*Function*

```ts
function busPuckImage(size?: number, options?: {
    followed?: boolean;
}): Rgba8;
```

The line-number puck as raw RGBA: an ink (#0f1115) disc of radius 9 with
a 1.5 px white halo, centred on a 24 px square drawn at `size` pixels
(default 48, pixel ratio 2). With `followed`, the ring the followed
vehicle wears instead: 2 px white then 2 px ink, on a 28 px square
(default 56). The layer stretches its middle column to fit the line
number ([`busPuckImageOptions`](#buspuckimageoptions)).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `size?` | `number` | not stated |  |
| `options?` | `{ followed?: boolean; }` | not stated |  |

#### `busPuckImageOptions`

*Function*

```ts
function busPuckImageOptions(size?: number, options?: {
    followed?: boolean;
}): Partial<StyleImageMetadata>;
```

`addImage` options for a [`busPuckImage`](#buspuckimage): its pixel ratio, a one-px
stretchable middle column, and the content box `icon-text-fit` fits the
line number into (for the followed puck, inset by the extra 2 px of ring
so both pills are the same width around the same text).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `size?` | `number` | not stated |  |
| `options?` | `{ followed?: boolean; }` | not stated |  |

#### `busSilhouetteSdf`

*Function*

```ts
function busSilhouetteSdf(heightPx?: number, radius?: number, type?: BusVehicleType): Sdf;
```

A top-down bus of body `type`, nose north, as a MapLibre SDF image
(`addImage(id, img, { sdf: true })`): white, with the signed distance to
the outline in alpha by the TinySDF convention (edge at alpha 191.25, so
192 rounds inside; `radius` px of distance per 255 * 1 alpha; MapLibre
reads halo widths in true pixels when `radius` is 8). Drawn from the
modelled bus's top view: the body outline, with the windscreen (a block
behind the nose, the heading cue), the roof hatches and (single-decker)
the roof pod's front edge cut out, so the halo colour shows through them
as roof detail. At the
default size the canvas is 48 px wide and the double-decker's 96 px tall
(body 72 x 22 px); `heightPx` scales the double-decker's height and every
other type by the same factor.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `heightPx?` | `number` | not stated |  |
| `radius?` | `number` | not stated |  |
| `type?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |

Options and types: [`BusVehicleType`](#busvehicletype-1).

#### `busSpriteBody`

*Function*

```ts
function busSpriteBody(type?: BusVehicleType): {
    length: number;
    width: number;
};
```

A body type's outline length and width on the reference canvas, px.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |

Options and types: [`BusVehicleType`](#busvehicletype-1).

#### `busTickImage`

*Function*

```ts
function busTickImage(pixelRatio?: number): Rgba8;
```

The heading tick as raw RGBA: an ink triangle 8 px wide and 6 px tall,
pointing north, with a 1 px white edge, centred on a 12 x 10 px canvas
drawn at `pixelRatio` (default 2). The layer orbits it round the puck
with `icon-offset`, which MapLibre turns with `icon-rotate`, so one image
serves every bearing.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `pixelRatio?` | `number` | not stated |  |

#### `busVehicleType`

*Function*

```ts
function busVehicleType(properties: Record<string, unknown> | null | undefined): BusVehicleType | null;
```

The body type a feed feature's properties name, or null when they name
none: `vehicle_type` (`"double"`, `"single"`, `"mini"` or `"generic"`)
first, then `deck` (`"double"` or `"single"`). Anything else is unknown.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `properties` | `Record<string, unknown> \| null \| undefined` | required |  |

#### `cameraKindLabel`

*Function*

```ts
function cameraKindLabel(kind: NavCameraKind | string): string;
```

Human-readable name for a camera kind, used in the chip's text and in
the spoken announcement. Kind lives here and in the icon, never in the
earcon.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `kind` | `NavCameraKind \| string` | required |  |

Options and types: [`NavCameraKind`](#navcamerakind-1).

#### `cameraKindsShownOnMap`

*Function*

```ts
function cameraKindsShownOnMap(design: NavAlertsDesign, mode?: MapDisplayMode): NavCameraKind[];
```

The camera kinds a style should draw in the given mode, in the canonical
kind order. Feed it straight into a MapLibre `filter` (see
[`cameraSymbolLayer`](#camerasymbollayer)); an empty array means "draw nothing", which
a caller must honour by removing or hiding the layer.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `mode?` | [`MapDisplayMode`](#mapdisplaymode) | not stated |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`MapDisplayMode`](#mapdisplaymode).

#### `cameraShownOnMap`

*Function*

```ts
function cameraShownOnMap(design: NavAlertsDesign, kind: NavCameraKind | string, mode?: MapDisplayMode): boolean;
```

Whether a kind's pin should be drawn on the map in the given mode.

Browsing and navigating are separate switches because they are separate
decisions - the market warns with or without a planned route, so a
design that shows a camera on the planning map is not thereby committed
to a cluttered driving map, nor the reverse.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `kind` | `NavCameraKind \| string` | required |  |
| `mode?` | [`MapDisplayMode`](#mapdisplaymode) | not stated |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`NavCameraKind`](#navcamerakind-1), [`MapDisplayMode`](#mapdisplaymode).

#### `cameraSpriteName`

*Function*

```ts
function cameraSpriteName(kind: NavCameraKind | string, variant?: "light" | "dark"): string;
```

Sprite image id for a camera kind, as built into the published sprite
sheet by `map-assets/icons/cameras/generate.mjs`.

LOCKSTEP: the file names in `map-assets/sprites/svg/camera-*.svg` are
exactly these ids, spreet names each image after its file.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `kind` | `NavCameraKind \| string` | required |  |
| `variant?` | `"light" \| "dark"` | not stated |  |

Options and types: [`NavCameraKind`](#navcamerakind-1).

#### `cameraSymbolLayer`

*Function*

```ts
function cameraSymbolLayer(design: NavAlertsDesign, source: string, mode?: MapDisplayMode, variant?: "light" | "dark", id?: string): Record<string, unknown>;
```

A MapLibre symbol layer that draws camera POIs from the sprite sheet.

Additive by design: the style compiler is untouched and ships no camera
layer of its own, because cameras arrive as host data (the gateway's
`POST /v1/cameras/along`, projected into a GeoJSON source) rather than
in the vector tiles. Point this at that source and the sprite icons
built from `map-assets/icons/cameras` render at the design's size, zoom
floor and per-mode visibility.

The features must carry a `kind` property holding one of
[`NAV_CAMERA_KINDS`](#nav_camera_kinds) or any wire spelling [`navCameraKind`](#navcamerakind)
accepts (the gateway's `redlight`, OSM's `traffic_signals`, …); an
unrecognised or absent value falls back to the `unknown` icon, never to a
guess at a specific camera type.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`NavAlertsDesign`](#navalertsdesign) | required |  |
| `source` | `string` | required | GeoJSON source id holding the camera features. |
| `mode?` | [`MapDisplayMode`](#mapdisplaymode) | not stated |  |
| `variant?` | `"light" \| "dark"` | not stated | Sprite variant to draw: `"light"` icons read on a dark basemap, `"dark"` icons on a light one. |
| `id?` | `string` | not stated |  |

Options and types: [`NavAlertsDesign`](#navalertsdesign), [`MapDisplayMode`](#mapdisplaymode).

#### `capTransitBbox`

*Function*

```ts
function capTransitBbox(bbox: [number, number, number, number], maxSqDeg?: number): [number, number, number, number];
```

A `[west, south, east, north]` box shrunk around its centre to at most
`maxSqDeg` square degrees (the gateway refuses larger ones).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `bbox` | `[number, number, number, number]` | required |  |
| `maxSqDeg?` | `number` | not stated |  |

#### `checkSourceUrl`

*Function*

```ts
function checkSourceUrl(url: string, allowPmtiles: boolean): string | undefined;
```

The URL rules for a customer source: `https://` (or, with
`allowPmtiles`, `pmtiles://https://`), no user name or password, no
literal IP address, no `localhost`, no spaces, at most
`MAX_SOURCE_URL_CHARS`. Returns the reason, or `undefined` when the
URL passes. Mirrors `sn_style::check_source_url`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `allowPmtiles` | `boolean` | required |  |

#### `clipFilter`

*Function*

```ts
function clipFilter(polygon: Polygon | MultiPolygon, original: FilterSpecification | null | undefined, minzoom?: number): FilterSpecification;
```

A layer filter that keeps `original` and hides every feature touching
`polygon`: `["all", original, [">", ["distance", polygon], 0]]` (just the
distance test when there is no original filter). With `minzoom`, the
distance test only applies from that zoom up.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `polygon` | `Polygon \| MultiPolygon` | required |  |
| `original` | `FilterSpecification \| null \| undefined` | required |  |
| `minzoom?` | `number` | not stated |  |

#### `colourThemeMetadata`

*Function*

```ts
function colourThemeMetadata(theme: ColourTheme, lut: LutBytes): Record<string, unknown>;
```

`metadata["mapmap:color-theme"]`: name, size, strength and the LUT digest.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | [`ColourTheme`](#colourtheme) | required |  |
| `lut` | [`LutBytes`](#lutbytes) | required |  |

Options and types: [`ColourTheme`](#colourtheme), [`LutBytes`](#lutbytes).

#### `contrastRatio`

*Function*

```ts
function contrastRatio(a: unknown, b: unknown): number | undefined;
```

WCAG 2.x contrast ratio between two hex colours, from 1 (identical) to
21 (black on white). Order-independent. Returns `undefined` when either
colour cannot be parsed, so callers can distinguish "not judgeable" from
"fails".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `a` | `unknown` | required |  |
| `b` | `unknown` | required |  |

#### `createMap`

*Function*

```ts
function createMap(options: MapMapOptions): MapMapMap;
```

Functional alias for [`MapMapMap`](#mapmapmap), mirroring the Mapbox `new Map` feel.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `options` | [`MapMapOptions`](#mapmapoptions) | required |  |

Options and types: [`MapMapOptions`](#mapmapoptions).

**Example**

```ts
import "maplibre-gl/dist/maplibre-gl.css";
import { createMap } from "@mapmap/maps";

const map = createMap({
  container: "map",       // an element id or the element itself
  apiKey: "snk_…",
  style: "light",         // or "dark", a Studio theme, a StyleSpecification or a style URL
  center: [-2.5879, 51.4545],
  zoom: 13,
});
await map.whenReady();
map.map.setPitch(45);     // map.map is the underlying MapLibre map
```

#### `createRouteEffect`

*Function*

```ts
function createRouteEffect(name: RouteEffectName, geometry: RouteGeometry, options?: RouteFlowOptions): RouteEffectLayer;
```

Create a named route effect layer. Throws an agent-readable error for
unknown names (the accepted list included).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | [`RouteEffectName`](#routeeffectname) | required |  |
| `geometry` | [`RouteGeometry`](#routegeometry) | required |  |
| `options?` | [`RouteFlowOptions`](#routeflowoptions) | not stated |  |

Options and types: [`RouteEffectName`](#routeeffectname), [`RouteGeometry`](#routegeometry), [`RouteFlowOptions`](#routeflowoptions).

**Example**

```ts
import { map } from "./map.ts";
import { createRouteEffect } from "@mapmap/maps";

const effect = createRouteEffect("flow", {
  type: "LineString",
  coordinates: [[-0.1276, 51.5072], [-0.0877, 51.5055]],
});
map.map.addLayer(effect);
```

#### `creditTier`

*Function*

```ts
function creditTier(room: number, fullWidth: number, shortWidth: number): CreditTier;
```

The [`CreditTier`](#credittier-1) for a credit with `room` px for its text, whose
full line is `fullWidth` px and short line (with the (i) and its gap)
`shortWidth` px wide at the 6px standard.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `room` | `number` | required |  |
| `fullWidth` | `number` | required |  |
| `shortWidth` | `number` | required |  |

#### `dataLayerColor`

*Function*

```ts
function dataLayerColor(style?: DataLayerStyle): string | ExpressionSpecification;
```

The colour expression for `style`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `style?` | [`DataLayerStyle`](#datalayerstyle) | not stated |  |

Options and types: [`DataLayerStyle`](#datalayerstyle).

#### `defaultNavAlertsDesign`

*Function*

```ts
function defaultNavAlertsDesign(): NavAlertsDesign;
```

Defaults for the optional `alerts` block. LOCKSTEP: mirrors
`defaultNavAlertsDesign` in `website/lib/studio-nav.ts`.

Calm chip = the banner's dark pill, so an unstyled alert already matches
the rest of the guidance surface. Escalated = a red that clears 4.5:1
against white text.

Per-kind defaults follow the "visual always, audio conditional" rule:
the three confidently classified kinds alert unconditionally, a mobile
site (a location where enforcement sometimes happens, not a device)
warns only if speeding, and an unclassified camera stays on the map but
does not interrupt a drive.

#### `defaultNavDesign`

*Function*

```ts
function defaultNavDesign(): NavDesign;
```

Defaults mirror what the SDK draws today with no design: `RouteLayer`'s
signal-blue line over a dark casing, and `GuidanceBanner`'s dark pill.

#### `defaultPoiDesign`

*Function*

```ts
function defaultPoiDesign(): PoiDesign;
```

No overrides: every category keeps its built-in dot and label colours.

#### `directionArrow`

*Function*

```ts
function directionArrow(direction: string): string;
```

Unicode arrow for a lane/maneuver direction (OSRM indication strings).
A text-only fallback; sprite-based lane icons ship with the map assets.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `direction` | `string` | required |  |

#### `directionIconSvg`

*Function* · also exported from `@mapmap/maps/direction-icons`

```ts
function directionIconSvg(type?: string, modifier?: string, drivingSide?: string): string;
```

Inline SVG markup for an OSRM-shaped maneuver (see [`iconNameForManeuver`](#iconnameformaneuver)).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | `string` | not stated |  |
| `modifier?` | `string` | not stated |  |
| `drivingSide?` | `string` | not stated |  |

#### `effectsFromStyleMetadata`

*Function*

```ts
function effectsFromStyleMetadata(metadata: unknown): {
    route: "flow";
    params?: Record<string, unknown>;
} | undefined;
```

The effects block from a compiled style's `metadata`, if the style
carries a valid one (`{ route: "flow", params? }`). Lenient by design, it reads FOREIGN styles (hosted URLs, baked packages), so anything
malformed returns `undefined` rather than throwing.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `metadata` | `unknown` | required |  |

#### `extractGuidance`

*Function*

```ts
function extractGuidance(route: ParsedRoute): StepGuidance[];
```

Extracts per-step guidance from a parsed route (requires the route to
have been requested with `steps: true` and voice/banner enabled).
Steps without instructions yield empty arrays, never holes.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `route` | [`ParsedRoute`](#parsedroute) | required |  |

Options and types: [`ParsedRoute`](#parsedroute).

#### `featuresetLayerIds`

*Function*

```ts
function featuresetLayerIds(id: FeaturesetId): readonly string[];
```

The layer ids a featureset covers (all of them, present or not).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | [`FeaturesetId`](#featuresetid) | required |  |

Options and types: [`FeaturesetId`](#featuresetid).

#### `fixSetFromCollection`

*Function*

```ts
function fixSetFromCollection(collection: unknown): FixSet;
```

The fixes in one gateway answer, keyed by vehicle id. Features without a
usable point or time are skipped.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `collection` | `unknown` | required |  |

#### `flythrough`

*Function*

```ts
function flythrough(map: MapMapMap | Map, route: RouteGeometry | ParsedRoute, options?: FlythroughOptions): FlythroughController;
```

Cinematic route replay: fly the camera along a route line with a
chase-cam bearing (eased along the shortest arc, so junctions glide
instead of spinning), at a configurable pitch/zoom/speed. Returns a
transport-style controller; nothing moves until `play()` or `seek()`.

```ts
const route = await routes.route(from, to);
const replay = flythrough(map, route, { pitch: 60, durationMs: 15000 });
replay.onProgress((t) => scrubber.value = String(t));
replay.play();
```

Drives the camera with `jumpTo` once per animation frame (the standard
MapLibre scrub pattern). Runs under any projection; on globe the ride is
unanchored but valid. Accepts a `ParsedRoute` (from `RouteLayer`) or a
bare GeoJSON LineString geometry.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `MapMapMap \| Map` | required |  |
| `route` | `RouteGeometry \| ParsedRoute` | required |  |
| `options?` | [`FlythroughOptions`](#flythroughoptions) | not stated |  |

Options and types: [`MapMapMap`](#mapmapmap), [`RouteGeometry`](#routegeometry), [`ParsedRoute`](#parsedroute), [`FlythroughOptions`](#flythroughoptions).

**Example**

```ts
import { map } from "./map.ts";
import { RouteLayer, flythrough } from "@mapmap/maps";

const route = await new RouteLayer(map).route([-0.1276, 51.5072], [-0.0877, 51.5055]);
const replay = flythrough(map, route, { pitch: 60, durationMs: 15000 });
replay.play();
```

#### `flythroughPose`

*Function*

```ts
function flythroughPose(coordinates: [number, number][], t: number, lookAheadM?: number): FlythroughPose;
```

The camera pose at progress `t` (0-1) along a route: the interpolated
point that fraction of the total distance along the line, with a
chase-cam bearing towards the point `lookAheadM` further on. Pure -
exported for tests and for apps that drive the camera themselves.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `coordinates` | `[number, number][]` | required |  |
| `t` | `number` | required |  |
| `lookAheadM?` | `number` | not stated |  |

#### `formatCoord`

*Function*

```ts
function formatCoord(point: LngLatLike): string;
```

Format one point as an OSRM `lon,lat` coordinate.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `point` | [`LngLatLike`](#lnglatlike) | required |  |

Options and types: [`LngLatLike`](#lnglatlike).

#### `formatCoords`

*Function*

```ts
function formatCoords(points: LngLatLike[]): string;
```

Format an ordered list of points as an OSRM coordinate path
(`lon,lat;lon,lat;…`). At least two points are required, matching the
gateway, which rejects single-coordinate requests.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `points` | [`LngLatLike[]`](#lnglatlike) | required |  |

Options and types: [`LngLatLike`](#lnglatlike).

#### `globeModelMatrix`

*Function*

```ts
function globeModelMatrix(lngLat: [number, number], altitudeM: number): Float64Array;
```

Local metres in the three.js frame (X east, Y up, Z south) to unit-sphere
space, column-major. A tangent plane at the anchor: the sphere curves
away from it by about 2 m at 5 km, which is sub-pixel at every zoom where
a 5 km scene fills the screen.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |
| `altitudeM` | `number` | required |  |

#### `gradeColourString`

*Function*

```ts
function gradeColourString(lut: LutBytes, value: string, strength?: number): string;
```

Grade one colour string, or return it unchanged when it is not one of
the forms the compilers grade.

Graded: `#rgb`, `#rgba`, `#rrggbb`, `#rrggbbaa` (any case) and
`rgb(r,g,b)` / `rgba(r,g,b,a)` with integer channels 0..255 (plain
spaces allowed around each value). Output: hex in gives lowercase hex out,
`#rrggbb` for the 3 and 6 digit forms and `#rrggbbaa` for the 4 and 8
digit forms (alpha kept); `rgb()`/`rgba()` in gives the same function out
with no spaces, `rgba` keeping its original alpha token verbatim. Anything
else (`hsl()`, named colours, percentages, `#12`), and everything at
strength 0, is returned as is.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lut` | [`LutBytes`](#lutbytes) | required |  |
| `value` | `string` | required |  |
| `strength?` | `number` | not stated |  |

Options and types: [`LutBytes`](#lutbytes).

#### `gradeStyleColours`

*Function*

```ts
function gradeStyleColours(style: StyleSpecification, lut: LutBytes, opts?: GradeStyleOptions): StyleSpecification;
```

Grade every colour in a compiled style through `lut`: each string under
`layers[*].paint` and `layers[*].layout` and under every entry of
`metadata["mapmap:territories"]` (the per-territory paint diffs),
recursing through expressions, so `interpolate`, `step`, `match` and
`case` stops are graded too. Strings follow
[`gradeColourString`](#gradecolourstring)'s rule.

Left untouched: layers in `skipLayerIds`, layers whose
`metadata["mapmap:use-theme"]` is `"none"` (and those layers' entries in
the territory diffs), and the root `light`, `sky` and `sprite`. That is
the one difference from Mapbox's full-frame LUT, which the runtime
[`ColourThemeLayer`](#colourthemelayer) matches instead.

Returns a new style; the input is not modified.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `style` | `StyleSpecification` | required |  |
| `lut` | [`LutBytes`](#lutbytes) | required |  |
| `opts?` | [`GradeStyleOptions`](#gradestyleoptions) | not stated |  |

Options and types: [`LutBytes`](#lutbytes), [`GradeStyleOptions`](#gradestyleoptions).

#### `hasBakedMarkers`

*Function*

```ts
function hasBakedMarkers(map: MapMapMap | Map): boolean;
```

Whether the map's current style carries compiler-baked marker layers, i.e. its markers already render in any MapLibre client, with no SDK code.

Useful for deciding whether to construct a [`MarkersLayer`](#markerslayer) at all:
you only need one to CHANGE the markers at runtime, or to render the
custom `data:` image markers a static sprite cannot carry.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `MapMapMap \| Map` | required |  |

Options and types: [`MapMapMap`](#mapmapmap).

#### `haversineDistanceM`

*Function*

```ts
function haversineDistanceM(a: {
    lat: number;
    lon: number;
}, b: {
    lat: number;
    lon: number;
}): number;
```

Straight-line (haversine) distance between two points, metres.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `a` | `{ lat: number; lon: number; }` | required |  |
| `b` | `{ lat: number; lon: number; }` | required |  |

#### `holdLayersOnTop`

*Function*

```ts
function holdLayersOnTop(map: Pick<Map, "getLayersOrder" | "getLayer" | "moveLayer" | "on" | "off">, ids: readonly string[] | (() => readonly string[])): () => void;
```

Keeps `ids` at the top of `map`'s drawing order, bottom to top in the order
given, from now on: once at the call and again on every `styledata`. Pass a
function to have the ids read afresh on every raise. Ids not on the map are
skipped, so the hold can start before its layers are added. Returns the
release, which removes the listener (calling it again does nothing).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `Pick<Map, "getLayersOrder" \| "getLayer" \| "moveLayer" \| "on" \| "off">` | required |  |
| `ids` | `readonly string[] \| (() => readonly string[])` | required |  |

**Example**

```ts
import { map } from "./map.ts";
import { holdLayersOnTop } from "@mapmap/maps";

// Keep your own layers above the basemap through style swaps and later additions.
const release = holdLayersOnTop(map.map, ["depots-circles", "depots-labels"]);
// Later, to stop holding them:
release();
```

#### `iconNameForManeuver`

*Function* · also exported from `@mapmap/maps/direction-icons`

```ts
function iconNameForManeuver(type?: string, modifier?: string, drivingSide?: string): DirectionIconName;
```

Pick the icon for an OSRM-shaped maneuver (`maneuver.type` +
`maneuver.modifier`, as returned by the MapMap gateway's OSRM endpoint
and by Mapbox-style banner instructions).

Unknown or partial input degrades gracefully: unknown types fall back to
the plain turn family and ultimately `turn_straight`, so the result is
always a renderable icon.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | `string` | not stated |  |
| `modifier?` | `string` | not stated |  |
| `drivingSide?` | `string` | There is deliberately no default: when the side is unknown a roundabout draws the plain turn glyph for its direction, and a u-turn the plain `continue_uturn`, rather than a glyph that may circulate the wrong way | The side traffic drives on at this manoeuvre (OSRM `step.driving_side`). It selects the roundabout drawing (`"left"` = clockwise `roundabout_lht_*`, `"right"` = anticlockwise `roundabout_*`) and the u-turn arm. There is deliberately no default: when the side is unknown a roundabout draws the plain turn glyph for its direction, and a u-turn the plain `continue_uturn`, rather than a glyph that may circulate the wrong way. |

#### `iconNameForStep`

*Function* · also exported from `@mapmap/maps/direction-icons`

```ts
function iconNameForStep(step: DirectionIconStep): DirectionIconName;
```

Pick the icon straight from an OSRM route step, the preferred entry
point for a single step. Reads `maneuver.type`/`maneuver.modifier`,
takes the driving side from the step's own `driving_side` (so a UK
roundabout draws clockwise and a UK u-turn `uturn_right` with no caller
configuration), and returns the ferry icon for ferry-mode legs.

Rendering a whole turn list? Use [`iconNamesForSteps`](#iconnamesforsteps) instead, a
roundabout's icon cannot be resolved from one step alone.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `step` | [`DirectionIconStep`](#directioniconstep) | required |  |

Options and types: [`DirectionIconStep`](#directioniconstep).

#### `iconNamesForSteps`

*Function* · also exported from `@mapmap/maps/direction-icons`

```ts
function iconNamesForSteps(steps: readonly DirectionIconStep[]): DirectionIconName[];
```

Icons for a whole turn list, the entry point for anything rendering
consecutive steps, because roundabouts can only be resolved in context.

A roundabout's icon should show the manoeuvre's THROUGH angle: the turn
between the road you arrive on and the road you leave on. No single step
carries that. The enter step knows the approach (`bearing_before`) and
the matching exit step knows the road taken (`bearing_after`), so this
pass pairs them and gives both the same modifier. The pair shares one
driving side too: the enter step's, or the exit step's when only it has
one.

Without it, every roundabout on a route draws the same generic ring:
the MapMap gateway sends roundabout steps with no modifier at all, and
stock OSRM sends the ENTRY tangent instead, which is "left" for every
roundabout in a left-hand-traffic country. Either way the icon stops
telling one exit from another, which is exactly what it is there for.

A supplied roundabout modifier is deliberately overridden: it describes
a different angle from the one being drawn. Everything else is left to
[`iconNameForStep`](#iconnameforstep) untouched.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `steps` | [`readonly DirectionIconStep[]`](#directioniconstep) | required |  |

Options and types: [`DirectionIconStep`](#directioniconstep).

#### `interpolateFixes`

*Function*

```ts
function interpolateFixes(prev: FixSet, next: FixSet, atMs: number): Rendered[];
```

Where every vehicle was at `atMs` (epoch milliseconds), between two
consecutive polls. Pure: same inputs, same answer.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `prev` | [`FixSet`](#fixset) | required |  |
| `next` | [`FixSet`](#fixset) | required |  |
| `atMs` | `number` | required |  |

Options and types: [`FixSet`](#fixset).

#### `isMarkerGlyphId`

*Function*

```ts
function isMarkerGlyphId(value: unknown): value is MarkerGlyphId;
```

Whether `value` is one of the 21 marker glyph ids.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `isNameTextField`

*Function*

```ts
function isNameTextField(textField: unknown): boolean;
```

True when a layer's `text-field` reads `name`/`name:*` properties, i.e. it is a place/POI/road-name label whose language can switch.
Fields reading other properties (`ref` shields, house numbers) are not.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `textField` | `unknown` | required |  |

#### `isSlot`

*Function*

```ts
function isSlot(value: unknown): value is Slot;
```

True when `value` is one of the three slot names.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `keyedStyleUrl`

*Function*

```ts
function keyedStyleUrl(url: string, apiKey: string | undefined, baseUrl?: string): string;
```

A style URL with the key added, when it names a document on the key's own
gateway that carries the key through to its tiles: a hosted style
(`/styles/{id}.json`) or a territory style (`/tiles/{territory}/style.json`).
The gateway stamps a `?api_key=` it was sent onto the tiles the document
points at, so the basemap works from any website. Any other URL, a URL
that already has an `api_key`, or no key: returned unchanged.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `apiKey` | `string \| undefined` | required |  |
| `baseUrl?` | `string` | not stated |  |

#### `keyedTilesUrl`

*Function*

```ts
function keyedTilesUrl(apiKey: string, baseUrl?: string): string;
```

The keyed basemap source URL: the gateway's TileJSON for
[`KEYED_TILES_TERRITORY`](#keyed_tiles_territory), carrying the key as `?api_key=`.

A TileJSON URL rather than a bare `{z}/{x}/{y}` template on purpose: the
gateway fills in the zoom range and bounds from the archive header, adds
an archive-version cache buster to the tile template, and stamps the same
`api_key` on it, so MapLibre stays authenticated down to every tile. The
query form (not a header) is what lets a plain style URL carry the key,
and it is accepted for browser keys restricted by `allowed_origins`.

`baseUrl` is the gateway that issued the key; a self-hosted gateway
without a `planet` archive should pass `territoryTilesUrl` instead.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `apiKey` | `string` | required |  |
| `baseUrl?` | `string` | not stated |  |

#### `laneArrowDirection`

*Function*

```ts
function laneArrowDirection(lane: LaneIndication): string;
```

The single direction a lane's arrow should show.

A lane that permits several movements ("straight or right") carries them
all in `directions`, in the gateway's bitmask order, which is NOT the
order the route cares about. Drawing `directions[0]` therefore shows a
straight-ahead arrow on the lane the driver is being told to turn right
from. `activeDirection` is the one the route actually takes, so it wins;
the first permitted direction is only the fallback for lanes where the
server could not resolve one.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lane` | [`LaneIndication`](#laneindication) | required |  |

Options and types: [`LaneIndication`](#laneindication).

#### `languageTextField`

*Function*

```ts
function languageTextField(language: string | null): DataDrivenPropertyValueSpecification<string>;
```

The `text-field` expression for labels in `language`, the requested
language first, the Latin transliteration second, the local name last.
`null` yields the compiled default (`name:en`, then `name`).

Typed as the widest value `text-field` accepts on both majors, because
MapLibre 6 types `setLayoutProperty`'s value parameter where 5.x took
`any`, and a `coalesce` of `get`s is one such value. The expressions
themselves are unchanged, so every caller keeps its current labels.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `language` | `string \| null` | required |  |

#### `layersOnTopMoves`

*Function*

```ts
function layersOnTopMoves(order: readonly string[], ids: readonly string[]): string[];
```

The ids to move to the top of `order`, in the order to move them (each one
moved to the very top, so the last moved draws over the rest). Empty when
the ids present already close `order` in their own order, or when none of
them is present. Ids not in `order` are skipped and a repeated id counts
once. Pure.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `order` | `readonly string[]` | required |  |
| `ids` | `readonly string[]` | required |  |

#### `liveTargetLayerIds`

*Function*

```ts
function liveTargetLayerIds(map: Map, target: InteractionTarget): string[];
```

The target's layer ids that exist in the live style, in style order.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `Map` | required |  |
| `target` | [`InteractionTarget`](#interactiontarget) | required |  |

Options and types: [`InteractionTarget`](#interactiontarget).

#### `lngLatToMercator`

*Function*

```ts
function lngLatToMercator(lngLat: [number, number]): [number, number];
```

Project a `[lng, lat]` to web-mercator world coordinates in [0, 1]
(MapLibre's `MercatorCoordinate` convention: x east, y south).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |

#### `locateProjectionUniforms`

*Function*

```ts
function locateProjectionUniforms(gl: WebGL2RenderingContext, program: WebGLProgram): Record<ProjectionUniform, WebGLUniformLocation | null>;
```

Uniform locations for [`PROJECTION_UNIFORMS`](#projection_uniforms). A name the variant's
prelude does not declare (the mercator prelude has no fallback matrix)
is `null`, which WebGL treats as a no-op when set.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `gl` | `WebGL2RenderingContext` | required |  |
| `program` | `WebGLProgram` | required |  |

#### `lutApply`

*Function*

```ts
function lutApply(lut: LutBytes, rgb: readonly [number, number, number], strength?: number): [number, number, number];
```

Grade one RGB colour (channels 0..255) through `lut`, blended with the
original by `strength` (0 = untouched, 1 = fully graded, default 1).

The algorithm, in this exact order (LOCKSTEP with the Rust and Studio
ports): per channel `s = v / 255 * 15`, `i0 = floor(s)`,
`i1 = min(i0 + 1, 15)`, `f = s - i0`; trilinear interpolation of the
eight lattice corners along red, then green, then blue, each step
`a + (b - a) * f`; then `round(in + (lut - in) * strength)` per channel,
clamped to 0..255. Rounding is half up; every value is non-negative, so
Rust's `f64::round` gives the same integer.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lut` | [`LutBytes`](#lutbytes) | required |  |
| `rgb` | `readonly [number, number, number]` | required |  |
| `strength?` | `number` | not stated |  |

Options and types: [`LutBytes`](#lutbytes).

#### `lutDigest`

*Function*

```ts
function lutDigest(lut: LutBytes): string;
```

FNV-1a 64-bit of the LUT bytes, as 16 lowercase hex characters.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lut` | [`LutBytes`](#lutbytes) | required |  |

Options and types: [`LutBytes`](#lutbytes).

#### `lutFromBase64`

*Function*

```ts
function lutFromBase64(s: string): LutBytes;
```

Decode a base64 LUT (standard alphabet, padded, no whitespace: the form
the Rust validator accepts). Throws `LUT_SIZE_MESSAGE` for anything
that is not valid base64 of exactly 12288 bytes.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `s` | `string` | required |  |

#### `lutFromImageData`

*Function*

```ts
function lutFromImageData(image: LutImageData): LutBytes;
```

Read a Mapbox-format colour-theme image: 256 pixels wide, 16 high; blue
picks the 16-pixel block (`x = b * 16 + r`), green is the row (`y = g`,
top row 0). Alpha is ignored. Any other size throws, naming 256x16.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `image` | [`LutImageData`](#lutimagedata) | required |  |

Options and types: [`LutImageData`](#lutimagedata).

#### `lutFromPngData`

*Function*

```ts
function lutFromPngData(data: string): Promise<LutBytes>;
```

Decode a Mapbox-format colour-theme PNG (256x16) into LUT bytes, in the
browser. `data` is what Mapbox's `color-theme.data` takes, plus a URL: a
`data:` URI, an `http(s)://` URL (served with CORS), or the raw base64 of
a PNG. The pixels are read with colour-space conversion off, so a PNG
carrying a colour profile still gives its stored values. Rejects with a
message naming the accepted forms or the 256x16 size.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `data` | `string` | required |  |

#### `lutToBase64`

*Function*

```ts
function lutToBase64(lut: LutBytes): string;
```

Encode a LUT as standard padded base64.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lut` | [`LutBytes`](#lutbytes) | required |  |

Options and types: [`LutBytes`](#lutbytes).

#### `lutToImageData`

*Function*

```ts
function lutToImageData(lut: LutBytes): {
    width: number;
    height: number;
    data: Uint8ClampedArray;
};
```

The inverse of [`lutFromImageData`](#lutfromimagedata): the LUT as 256x16 RGBA pixels
(alpha 255), Mapbox's layout. The runtime layer uploads exactly this as
its LUT texture, and it is what a PNG export writes.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lut` | [`LutBytes`](#lutbytes) | required |  |

Options and types: [`LutBytes`](#lutbytes).

#### `mapBrandSvg`

*Function*

```ts
function mapBrandSvg(theme: MapBrandTheme): string;
```

The composed v2/MapBrand SVG for one theme.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | [`MapBrandTheme`](#mapbrandtheme) | required |  |

Options and types: [`MapBrandTheme`](#mapbrandtheme).

#### `mapGroundTheme`

*Function*

```ts
function mapGroundTheme(map: GroundReadableMap, whenUnreadable: MapBrandTheme): MapBrandTheme;
```

Day or Night for the style a live map is drawing right now. Cheap enough
to run on every `styledata`: it walks layer ids only until the first
background layer (normally the first) and reads one paint value, rather
than serialising the whole style.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | [`GroundReadableMap`](#groundreadablemap) | required |  |
| `whenUnreadable` | [`MapBrandTheme`](#mapbrandtheme) | required |  |

Options and types: [`GroundReadableMap`](#groundreadablemap), [`MapBrandTheme`](#mapbrandtheme).

#### `markerImage`

*Function*

```ts
function markerImage(v: unknown): string | undefined;
```

A usable custom marker image, or `undefined`: a `data:` URI of an
accepted image type (png/jpeg/webp/svg+xml) whose decoded payload is at
most 64 KB. Oversized or foreign URIs are rejected - the marker falls
back to its glyph pin (the `puck.ts` cap precedent).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `v` | `unknown` | required |  |

#### `markerImageDataKey`

*Function*

```ts
function markerImageDataKey(dataUri: string): string;
```

`map.addImage` cache key for a custom-image marker: the data URI is
hashed (FNV-1a), so identical images share one registered copy no
matter how many markers use them.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `dataUri` | `string` | required |  |

#### `markerImageKey`

*Function*

```ts
function markerImageKey(icon: MarkerGlyphId, colour: string, size: MarkerSize): string;
```

`map.addImage` cache key for a rasterised glyph marker:
`mm-marker-<icon>-<colour>-<size>` (contract format; the colour is the
normalised lowercase `#rrggbb`). Identical markers share one image.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `icon` | [`MarkerGlyphId`](#markerglyphid) | required |  |
| `colour` | `string` | required |  |
| `size` | [`MarkerSize`](#markersize) | required |  |

Options and types: [`MarkerGlyphId`](#markerglyphid), [`MarkerSize`](#markersize).

#### `markersFromTheme`

*Function*

```ts
function markersFromTheme(theme: Theme | undefined | null): MarkersBlock | undefined;
```

Reads and parses the `extra.markers` block from a Studio theme document.
Returns `undefined` when the theme carries no markers block at all (so
callers can tell "no markers" apart from "an empty designed set"); a
present but malformed block parses leniently via [`parseMarkers`](#parsemarkers).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | [`Theme \| undefined \| null`](#theme) | required |  |

Options and types: [`Theme`](#theme).

#### `markersFromThemeUrl`

*Function*

```ts
function markersFromThemeUrl(url: string, fetchImpl?: typeof fetch): Promise<MarkersBlock | undefined>;
```

Fetches a hosted theme document and reads its `extra.markers` block -
the same endpoint contract as `navDesignFromThemeUrl`:

```ts
const markers = await markersFromThemeUrl(
  "https://api.mapmap.ai/styles/midnight-fleet-a1b2c3/theme",
);
if (markers) new MarkersLayer(map, markers);
```

Returns `undefined` when the theme carries no markers block; throws on
network failure, a non-2xx response or a non-JSON body. Bad DATA never
throws - invalid items are skipped by the lenient parser.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `fetchImpl?` | `typeof fetch` | not stated | Optional `fetch` replacement (tests, Node polyfills). |

#### `markerText`

*Function*

```ts
function markerText(value: unknown): string | undefined;
```

Normalise a short pin text: trimmed and cut to
[`MAX_MARKER_TEXT_LENGTH`](#max_marker_text_length) characters, or `undefined` when nothing
printable remains. Callers use `undefined` to mean "draw the glyph".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `markerTextImageKey`

*Function*

```ts
function markerTextImageKey(text: string, colour: string, size: MarkerSize): string;
```

`map.addImage` cache key for a rasterised SHORT-TEXT marker:
`mm-marker-text-<text>-<colour>-<size>`. Deliberately distinct from
[`markerImageKey`](#markerimagekey) so glyph pins keep their existing keys unchanged.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | required |  |
| `colour` | `string` | required |  |
| `size` | [`MarkerSize`](#markersize) | required |  |

Options and types: [`MarkerSize`](#markersize).

#### `mercatorModelMatrix`

*Function*

```ts
function mercatorModelMatrix(lngLat: [number, number], altitudeM: number): Float64Array;
```

Local metres in the three.js frame (X east, Y up, Z south) to mercator
0..1 with a conformal z, column-major: the bridge every MapMap 3D layer
has used on mercator,
`translate(MercatorCoordinate.fromLngLat(lngLat, altitudeM)) * scale(s, -s, s) * rotX(pi/2)`,
`s = meterInMercatorCoordinateUnits()` at the anchor, built with the same
operations three.js's `makeTranslation().scale().multiply()` performs.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |
| `altitudeM` | `number` | required |  |

#### `modelClipMatrices`

*Function*

```ts
function modelClipMatrices(args: ProjectionArgs, lngLat: [number, number], altitudeM: number): ClipMatrices;
```

The clip matrices for a scene anchored at `lngLat`. On a mercator frame
this is exactly `mainMatrix * mercatorModelMatrix(...)`, the matrix the
layers used before the globe existed. On a globe frame the mercator
matrix is built from MapLibre's mercator fallback, so the blend through
the globe-to-mercator zoom band never squashes elevated geometry (the
prelude's own fallback is fed metres where it expects mercator units).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `args` | [`ProjectionArgs`](#projectionargs) | required |  |
| `lngLat` | `[number, number]` | required |  |
| `altitudeM` | `number` | required |  |

Options and types: [`ProjectionArgs`](#projectionargs).

#### `multiplyMat4`

*Function*

```ts
function multiplyMat4(a: ArrayLike<number>, b: ArrayLike<number>): Float64Array;
```

`a * b` for column-major 4x4s, summed in exactly the order three.js's
`Matrix4.multiplyMatrices` sums (`a11*b11 + a12*b21 + a13*b31 + a14*b41`),
so a product computed here equals the one three.js computed bit for bit.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `a` | `ArrayLike<number>` | required |  |
| `b` | `ArrayLike<number>` | required |  |

#### `namedBase`

*Function*

```ts
function namedBase(id: string): MapMapBase | undefined;
```

The catalogue entry for an id, following [`BASE_ID_ALIASES`](#base_id_aliases).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |

#### `navAlertLeadDistanceM`

*Function*

```ts
function navAlertLeadDistanceM(preset: unknown): number;
```

Metres of lead for a preset (garbage falls back to `standard`).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `preset` | `unknown` | required |  |

#### `navAlertSoundUrl`

*Function*

```ts
function navAlertSoundUrl(sound: unknown, baseUrl?: string): string;
```

Absolute URL for a built-in earcon. An unrecognised value falls back to
`cameraCalm` rather than returning nothing: a missing alert sound is a
silent failure a driver cannot see.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `sound` | `unknown` | required |  |
| `baseUrl?` | `string` | not stated |  |

#### `navCameraKind`

*Function*

```ts
function navCameraKind(value: unknown): NavCameraKind;
```

Normalises a camera kind from the wire (or any host classifier) onto a
[`NavCameraKind`](#navcamerakind-1). Unrecognised values become `"unknown"`, never a
guess: a mislabelled camera would alert a driver about the wrong thing.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `navDesignFromTheme`

*Function*

```ts
function navDesignFromTheme(theme: Theme | undefined | null): NavDesign | undefined;
```

Reads and parses the `extra.nav` block from a Studio theme document.
Returns `undefined` when the theme carries no nav block at all (so
callers can tell "no design" apart from "default design"); a present but
malformed block parses leniently to the defaults.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | [`Theme \| undefined \| null`](#theme) | required |  |

Options and types: [`Theme`](#theme).

#### `navDesignFromThemeUrl`

*Function*

```ts
function navDesignFromThemeUrl(url: string, fetchImpl?: typeof fetch): Promise<NavDesign | undefined>;
```

Fetches a hosted theme document and reads its `extra.nav` block.

Point it at the gateway's theme endpoint - the design a Studio publish
stores alongside the compiled style:

```ts
const design = await navDesignFromThemeUrl(
  "https://api.mapmap.ai/styles/midnight-fleet-a1b2c3/theme",
);
```

The endpoint is public (no API key) and never cached, so this always
reflects the latest published version. Returns `undefined` when the
theme carries no nav block (same contract as [`navDesignFromTheme`](#navdesignfromtheme));
throws on network failure, a non-2xx response or a non-JSON body.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `fetchImpl?` | `typeof fetch` | the global `fetch` | Optional `fetch` replacement (tests, Node polyfills). Defaults to the global `fetch`. |

#### `osmAndMoreCredit`

*Function*

```ts
function osmAndMoreCredit(line: string): {
    lead: string;
    more: number;
    text: string;
};
```

The third credit line (0.22.1), for when even the short line would need
less than 6px: the OpenStreetMap credit verbatim (the ODbL condition)
followed by " and <n> more", where `n` counts the line's other entries
(`creditEntries`, one per source). A line without an OpenStreetMap
credit leads with its first entry's short form instead. Every source's
whole wording stays in the (i) panel.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `line` | `string` | required |  |

#### `parseCssColour`

*Function*

```ts
function parseCssColour(colour: string): [number, number, number, number] | undefined;
```

Parse a CSS colour to premultiplication-ready `[r, g, b, a]` (0-1
channels). Supports the hex forms and `rgb()`/`rgba()`; anything else
(named colours, `hsl()`) returns `undefined` and the caller falls back
to the default - this runs where no canvas is guaranteed, so no
browser colour parser is available.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `colour` | `string` | required |  |

#### `parseGeocodeResponse`

*Function*

```ts
function parseGeocodeResponse(body: unknown): GeocodeHit[];
```

Parse a geocode response body (either endpoint) into [`GeocodeHit`](#geocodehit)s.
Throws when the body is not a FeatureCollection. Features without a
Point geometry are skipped: the gateway only emits Points, so anything
else is not a hit this parser can represent.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `body` | `unknown` | required |  |

#### `parseHexColor`

*Function*

```ts
function parseHexColor(value: unknown): [number, number, number] | undefined;
```

Parses a CSS hex colour (`#rgb`, `#rrggbb`, `#rgba`, `#rrggbbaa`) into
0-255 sRGB channels. Alpha is accepted and ignored - the chip composites
over an opaque background, so its alpha does not describe what the
driver sees. Returns `undefined` for any other notation (named colours,
`rgb()`, gradients): unparseable means "cannot judge", never "fails".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `parseMarkers`

*Function*

```ts
function parseMarkers(value: unknown): MarkersBlock;
```

Lenient parse of an `extra.markers` value into a v1 block. A block whose
`version` is not 1 (including a missing one) parses to NOTHING: the Rust
validator requires exactly 1, so a version-less block that rendered here
could never be published, and a future v2 block must not be silently
drawn as v1 by an already-deployed SDK.

Within a v1 block, invalid ITEMS are skipped (bad/missing id, duplicate
id, out-of-range coordinates); invalid FIELDS on a valid item fall back
to their defaults (unknown glyph -> `pin`, bad colour -> the default
blue, bad size -> `m`, over-long labels truncated, unusable images
dropped). Never throws on bad data - a hand-edited theme cannot break
the map. At most 200 items survive.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `parseNavAlertsDesign`

*Function*

```ts
function parseNavAlertsDesign(value: unknown): NavAlertsDesign;
```

Lenient parse of an `extra.nav.alerts` value. Every numeric is clamped,
every enum validated, and garbage falls back per field to the defaults -
so a hand-edited or older theme can never produce an alert that is
unreadable, positioned over the speed readout, or missing its escalated
state. The escalated tokens in particular always come back set, even
when the input omits or nulls them: a driver-facing escalation is not a
removable feature.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `parseNavDesign`

*Function*

```ts
function parseNavDesign(value: unknown): NavDesign;
```

Lenient parse of an `extra.nav` value: unknown or malformed fields fall
back to the defaults, so an imported theme never fails on its nav block.
LOCKSTEP: identical semantics to `parseNavDesign` in
`website/lib/studio-nav.ts` (same clamps, same fallbacks).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `parseOsrmRoute`

*Function*

```ts
function parseOsrmRoute(body: unknown): ParsedRoute;
```

Parse an OSRM route response into a [`ParsedRoute`](#parsedroute). Throws with the
OSRM `code`/`message` when the response is not `Ok` or carries no route.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `body` | `unknown` | required |  |

#### `parsePoiDesign`

*Function*

```ts
function parsePoiDesign(value: unknown): PoiDesign;
```

Lenient parse of an `extra.poi` value: unknown category ids and invalid
colours are dropped rather than failing, so an imported theme never
breaks on its POI block. LOCKSTEP: identical semantics to
`parsePoiDesign` in `website/lib/studio-poi.ts`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

#### `parseSuggestResponse`

*Function*

```ts
function parseSuggestResponse(body: unknown): SearchSuggestion[];
```

Parse a `GET /geocode/suggest` body into rows, dropping anything that is
not a complete suggestion.

Deliberately forgiving about the envelope and strict about each row: a
dropdown that renders a row with no id offers the user something the
retrieve endpoint cannot resolve.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `body` | `unknown` | required |  |

#### `placesFromGeoJSON`

*Function*

```ts
function placesFromGeoJSON(collection: PlacesFeatureCollection): Place[];
```

Normalise a GeoJSON FeatureCollection of Points to `Place[]`. The id comes
from `feature.id`, then `properties.id`, then the feature index; the name
from `properties.name` (else `""`). Non-Point features are skipped.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `collection` | [`PlacesFeatureCollection`](#placesfeaturecollection) | required |  |

Options and types: [`PlacesFeatureCollection`](#placesfeaturecollection).

#### `poiDesignFromTheme`

*Function*

```ts
function poiDesignFromTheme(theme: Theme | undefined | null): PoiDesign | undefined;
```

Reads and parses the `extra.poi` block from a Studio theme document.
Returns `undefined` when the theme carries no poi block at all (so
callers can tell "no design" apart from "default design"); a present but
malformed block parses leniently to the defaults. Same contract as
`navDesignFromTheme`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | [`Theme \| undefined \| null`](#theme) | required |  |

Options and types: [`Theme`](#theme).

#### `poiDesignFromThemeUrl`

*Function*

```ts
function poiDesignFromThemeUrl(url: string, fetchImpl?: typeof fetch): Promise<PoiDesign | undefined>;
```

Fetches a hosted theme document and reads its `extra.poi` block - point
it at the gateway's public, never-cached theme endpoint, e.g.
`https://api.mapmap.ai/styles/midnight-fleet-a1b2c3/theme`. Returns
`undefined` when the theme carries no poi block; throws on network
failure, a non-2xx response or a non-JSON body. Same contract as
[`navDesignFromThemeUrl`](#navdesignfromthemeurl).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `fetchImpl?` | `typeof fetch` | not stated | Optional `fetch` replacement (tests, Node polyfills). |

#### `poiDesignIsDefault`

*Function*

```ts
function poiDesignIsDefault(design: PoiDesign): boolean;
```

True when the design changes nothing.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`PoiDesign`](#poidesign) | required |  |

Options and types: [`PoiDesign`](#poidesign).

#### `poiTextColorExpression`

*Function*

```ts
function poiTextColorExpression(design: PoiDesign, fallback: string): unknown;
```

The `poi-labels` `text-color` value for a design: the plain fallback
colour when no category overrides label text, otherwise a `match`
expression keyed on the tile's `class` property. Every non-services
category always gets an arm (override or fallback) so unmatched classes
- the `services` fallback arm - never inherit another category's colour.
LOCKSTEP: mirrors `poiTextColorExpression` in `website/lib/studio-poi.ts`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `design` | [`PoiDesign`](#poidesign) | required |  |
| `fallback` | `string` | required |  |

Options and types: [`PoiDesign`](#poidesign).

#### `prefersReducedMotion`

*Function*

```ts
function prefersReducedMotion(): boolean;
```

Whether the user has asked for reduced motion. Safe without a DOM.

#### `projectionVariant`

*Function*

```ts
function projectionVariant(args: ProjectionArgs): ProjectionVariant;
```

Which projection the frame is drawn with; unknown names read as mercator.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `args` | [`ProjectionArgs`](#projectionargs) | required |  |

Options and types: [`ProjectionArgs`](#projectionargs).

#### `readProjection`

*Function*

```ts
function readProjection(args: unknown): ProjectionArgs | undefined;
```

The projection-aware render arguments, or `undefined` when MapLibre did
not supply them: a bare matrix (MapLibre 4), or an object without
`shaderData` (early 5.x). Callers keep their legacy mercator path for the
`undefined` case.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `args` | `unknown` | required |  |

#### `registerPmtilesProtocol`

*Function*

```ts
function registerPmtilesProtocol(gl?: ProtocolHost): void;
```

Register the `pmtiles://` protocol on a MapLibre instance. Idempotent per
maplibre-gl instance, and safe to call before constructing any map.
Exposed for apps that build their own `maplibregl.Map` (possibly from
their own bundled maplibre-gl copy) but still want to read MapMap PMTiles.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `gl?` | [`ProtocolHost`](#protocolhost) | not stated |  |

Options and types: [`ProtocolHost`](#protocolhost).

**Example**

```ts
import * as maplibregl from "maplibre-gl";
import { registerPmtilesProtocol } from "@mapmap/maps";

// Before the first map that reads pmtiles:// sources.
registerPmtilesProtocol(maplibregl);
const map = new maplibregl.Map({
  container: "map",
  style: {
    version: 8,
    sources: { parcels: { type: "vector", url: "pmtiles://https://example.com/parcels.pmtiles" } },
    layers: [{ id: "parcels", type: "line", source: "parcels", "source-layer": "parcels" }],
  },
});
map.on("load", () => console.log("loaded"));
```

#### `relativeLuminance`

*Function*

```ts
function relativeLuminance(rgb: readonly [number, number, number]): number;
```

WCAG 2.x relative luminance of an sRGB triple (channels 0-255).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `rgb` | `readonly [number, number, number]` | required |  |

#### `removeTerrain`

*Function*

```ts
function removeTerrain(map: Map): void;
```

Turn terrain off and remove what [`applyTerrain`](#applyterrain) added.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `Map` | required |  |

#### `renderClockMs`

*Function*

```ts
function renderClockMs(recordedAtMaxMs: number, receivedAtMs: number, nowMs: number, renderBehindMs: number): number;
```

The moment to draw: one poll behind the data clock. The data clock is the
newest fix the latest answer held (`recordedAtMaxMs`), run forward by the
time since that answer arrived. Never later than the newest fix, so a
feed that stops leaves vehicles parked where they last reported.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `recordedAtMaxMs` | `number` | required |  |
| `receivedAtMs` | `number` | required |  |
| `nowMs` | `number` | required |  |
| `renderBehindMs` | `number` | required |  |

#### `renderMarkerImage`

*Function*

```ts
function renderMarkerImage(options?: RenderMarkerImageOptions): RenderedMarkerImage | null;
```

Rasterise one marker to `ImageData` for `map.addImage`, per the
rendering contract: every glyph except `dot` is the coloured pin-body
teardrop with the glyph drawn in white centred on the head (anchor the
symbol `bottom` - the tip sits exactly on the canvas's bottom edge);
`dot` is a coloured circle with a white outline (anchor `center`). The
canvas is a square of twice the preset's CSS pixel size, returned with
`pixelRatio: 2`.

Passing `text` switches on the short-text mode: the same coloured pin
body, with 1-3 white bold characters centred in the head (auto-shrunk to
fit) in place of the glyph. That is what numbered waypoints and lettered
stops are drawn with.

Returns `null` where canvas 2D or `Path2D` is unavailable (never
expected in a browser; keeps non-DOM environments safe, the
`poi-icons.ts` pattern).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `options?` | [`RenderMarkerImageOptions`](#rendermarkerimageoptions) | not stated |  |

Options and types: [`RenderMarkerImageOptions`](#rendermarkerimageoptions).

#### `resetDiagnostics`

*Function*

```ts
function resetDiagnostics(): void;
```

Reset the once-per-page dedup - for tests (and hot-reload tooling).

#### `resolveBaseId`

*Function*

```ts
function resolveBaseId(id: string): MapMapBaseId | undefined;
```

Resolve an id to its canonical form, following [`BASE_ID_ALIASES`](#base_id_aliases).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |

#### `resolveSlot`

*Function*

```ts
function resolveSlot(style: Pick<StyleSpecification, "layers"> | {
    layers?: readonly LayerLike[];
}, slot: Slot): string | undefined;
```

The `beforeId` for `slot` in `style`, or `undefined` to append on top.

```ts
map.map.addLayer(myLayer, resolveSlot(map.map.getStyle(), "bottom"));
```

`MapMapMap.slot(slot)` does the same against the live map, and
`addPersistentLayer(layer, { slot })` re-resolves on every style load.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `style` | `Pick<StyleSpecification, "layers"> \| { layers?: readonly LayerLike[]; }` | required |  |
| `slot` | [`Slot`](#slot) | required |  |

Options and types: [`Slot`](#slot).

#### `resolveTerrain`

*Function*

```ts
function resolveTerrain(input: boolean | TerrainOptions | undefined): Required<Omit<TerrainOptions, "hillshade">> & {
    hillshade: boolean;
} | null;
```

Normalise `true | TerrainOptions` into a full, clamped config.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `input` | `boolean \| TerrainOptions \| undefined` | required |  |

Options and types: [`TerrainOptions`](#terrainoptions).

#### `resolveTheme`

*Function*

```ts
function resolveTheme(date: Date, lat: number, lng: number): "light" | "dark";
```

The theme the sun dictates at `date` for `lat`/`lng`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `date` | `Date` | required |  |
| `lat` | `number` | required |  |
| `lng` | `number` | required |  |

#### `runMapDiagnostics`

*Function*

```ts
function runMapDiagnostics(input: MapDiagnosticsInput): void;
```

Run the construction-time checks and attach the 401 watcher. Called by
`MapMapMap` automatically; exported for apps wrapping a raw
`maplibregl.Map` that still want the self-diagnosis.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `input` | [`MapDiagnosticsInput`](#mapdiagnosticsinput) | required |  |

Options and types: [`MapDiagnosticsInput`](#mapdiagnosticsinput).

#### `setMapLanguage`

*Function*

```ts
function setMapLanguage(map: MapMapMap | Map, language: string | null): string[];
```

Switches every name-label layer of the map's current style to
`language` (`null` restores the style's default). Returns the ids of
the layers it rewrote. Throws on a malformed language tag. Reapply
after `setStyle`, a style swap resets label languages.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `map` | `MapMapMap \| Map` | required |  |
| `language` | `string \| null` | required |  |

Options and types: [`MapMapMap`](#mapmapmap).

#### `setProjectionUniforms`

*Function*

```ts
function setProjectionUniforms(gl: WebGL2RenderingContext, locations: Record<ProjectionUniform, WebGLUniformLocation | null>, args: ProjectionArgs): void;
```

Set the five prelude uniforms for this frame (the program must be in use).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `gl` | `WebGL2RenderingContext` | required |  |
| `locations` | `Record<ProjectionUniform, WebGLUniformLocation \| null>` | required |  |
| `args` | [`ProjectionArgs`](#projectionargs) | required |  |

Options and types: [`ProjectionUniform`](#projectionuniform), [`ProjectionArgs`](#projectionargs).

#### `severityProsody`

*Function*

```ts
function severityProsody(severity: GuidanceSeverity, baseRate?: number): SeverityProsody;
```

Prosody for a severity level at a base rate. Pure, exported for tests
and for custom voice layers that want the same house style.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `severity` | [`GuidanceSeverity`](#guidanceseverity) | required |  |
| `baseRate?` | `number` | not stated |  |

Options and types: [`GuidanceSeverity`](#guidanceseverity).

#### `shortCredit`

*Function*

```ts
function shortCredit(line: string): string;
```

The short form of a credit line: each source's own short credit
(`CREDIT_SHORT_FORMS`), everything else verbatim. A line with no
known long notice comes back with only the OpenStreetMap credit
shortened; it still gets the (i) and its full wording when it would need
less than 6px.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `line` | `string` | required |  |

#### `shortestArcDeg`

*Function*

```ts
function shortestArcDeg(fromDeg: number, toDeg: number): number;
```

The signed shortest-arc rotation from `fromDeg` to `toDeg`, in degrees.
Always in `(-180, 180]`, so a heading tween never spins the long way
round the compass (350° -> 10° is +20°, not -340°).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `fromDeg` | `number` | required |  |
| `toDeg` | `number` | required |  |

#### `shortestArcDelta`

*Function*

```ts
function shortestArcDelta(from: number, to: number): number;
```

The signed shortest-arc rotation from bearing `from` to bearing `to`,
in `(-180, 180]` degrees - the slerp-style delta the flythrough eases
along so the chase cam never spins the long way round.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `from` | `number` | required |  |
| `to` | `number` | required |  |

#### `speak`

*Function*

```ts
function speak(instruction: VoiceInstruction, options?: SpeakOptions): boolean;
```

Speaks a voice instruction through the browser's `SpeechSynthesis`.
Prefers the SSML body's text (browsers do not accept raw SSML). Returns
false when speech synthesis is unavailable (e.g. non-browser contexts).
De-duplication is the caller's responsibility: speak each instruction
once as its trigger distance is crossed.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `instruction` | [`VoiceInstruction`](#voiceinstruction) | required |  |
| `options?` | [`SpeakOptions`](#speakoptions) | not stated |  |

Options and types: [`VoiceInstruction`](#voiceinstruction), [`SpeakOptions`](#speakoptions).

#### `sphereFrame`

*Function*

```ts
function sphereFrame(lngLat: [number, number]): {
    position: Vec3;
    east: Vec3;
    south: Vec3;
    up: Vec3;
};
```

The tangent frame at a `[lng, lat]`: the prelude's
`globeGetRotationMatrix` columns (east, then south, then up), written in
closed form so it stays defined at the poles, where the prelude's
`normalize(p.z, 0, -p.x)` divides by zero. Orthonormal.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |

#### `sphereSurface`

*Function*

```ts
function sphereSurface(lngLat: [number, number]): Vec3;
```

A `[lng, lat]` on MapLibre's unit globe: `x = sin(lng) cos(lat)`,
`y = sin(lat)`, `z = cos(lng) cos(lat)` (the prelude's `projectToSphere`).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |

#### `ssmlToText`

*Function*

```ts
function ssmlToText(ssml: string): string;
```

Strips SSML tags down to speakable plain text. Entities are decoded in a
single pass (never re-scanning already-decoded output), so text whose
literal content looks like an entity, e.g. `&amp;lt;`, the gateway's
escape of a literal `&lt;`, round-trips correctly. Decodes the five
named XML entities plus decimal numeric references (`&#39;`).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `ssml` | `string` | required |  |

#### `styleFromBase`

*Function*

```ts
function styleFromBase(id: string, options?: Omit<BuildStyleOptions, "theme">): StyleSpecification;
```

Build a MapLibre style from a base name.

Throws on an unknown id, naming every accepted one: a silent fallback to
the default slate would show a map that is simply not the one asked for.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |
| `options?` | `Omit<BuildStyleOptions, "theme">` | not stated |  |

Options and types: [`BuildStyleOptions`](#buildstyleoptions).

**Example**

```ts
import * as maplibregl from "maplibre-gl";
import { MAPMAP_BASES, registerPmtilesProtocol, styleFromBase } from "@mapmap/maps";

console.log(MAPMAP_BASES.map((b) => b.id)); // the accepted ids
registerPmtilesProtocol(maplibregl);
const map = new maplibregl.Map({ container: "map", style: styleFromBase(MAPMAP_BASES[0].id, { apiKey: "snk_…" }) });
map.on("load", () => console.log("loaded"));
```

#### `styleGroundTheme`

*Function*

```ts
function styleGroundTheme(style: unknown, whenUnreadable: MapBrandTheme): MapBrandTheme;
```

Day or Night for a style document (a MapLibre StyleSpecification), or
`whenUnreadable` when it carries no readable ground.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `style` | `unknown` | required |  |
| `whenUnreadable` | [`MapBrandTheme`](#mapbrandtheme) | required |  |

Options and types: [`MapBrandTheme`](#mapbrandtheme).

#### `subdivideMercator`

*Function*

```ts
function subdivideMercator(coords: readonly [number, number][], maxSegment?: number): [number, number][];
```

Insert points so no segment exceeds `maxSegment` mercator units.

On the globe a straight mercator chord is not a straight line on screen:
the prelude projects each vertex onto the sphere and the GPU joins them
with straight lines, so a long segment cuts through the planet and
vanishes. Subdividing makes the geometry follow the same mercator path a
MapLibre `line` layer draws. A no-op (same points, same order) for input
already finer than the limit, and idempotent.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `coords` | `readonly [number, number][]` | required |  |
| `maxSegment?` | `number` | not stated |  |

#### `sunTimes`

*Function*

```ts
function sunTimes(date: Date, lat: number, lng: number): SunTimes;
```

Sunrise and sunset (standard -0.833° horizon: refraction + solar disc)
for the civil day containing `date` (UTC) at `lat`/`lng`, or a polar
marker when the sun never crosses the horizon that day.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `date` | `Date` | required |  |
| `lat` | `number` | required |  |
| `lng` | `number` | required |  |

#### `supportsTerrainDraping`

*Function*

```ts
function supportsTerrainDraping(args: unknown): false;
```

HOOK(maplibre-6.13-terrain): MapLibre 6.13 adds `renderToTerrainTile`,
`renderTerrainHeightMap` and `terrainTileRevision`, which let a custom
layer drape onto 3D terrain. The SDKs' dev pin reached 6.14.0 on 27
October 2026, but their peer ranges still admit 5.x and 6.9 to 6.12, which
lack them, and draping is not built yet, so this still answers `false`.
Implement draping here and nowhere else, gated on the installed version.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `args` | `unknown` | required |  |

#### `territoryTilesSourceUrl`

*Function*

```ts
function territoryTilesSourceUrl(options?: Pick<BuildStyleOptions, "territoryTilesUrl" | "apiKey" | "baseUrl">): string;
```

The URL a compiled style's `territory` source points at.

1. An explicit `territoryTilesUrl` always wins (`pmtiles://` added when
   missing), keyed or not.
2. Otherwise, with an `apiKey`, the keyed gateway TileJSON
   ([`keyedTilesUrl`](#keyedtilesurl)), which works from any website.
3. Otherwise the keyless [`DEFAULT_TERRITORY_TILES_URL`](#default_territory_tiles_url), which works
   on mapmap.ai and on a local development server.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `options?` | `Pick<BuildStyleOptions, "territoryTilesUrl" \| "apiKey" \| "baseUrl">` | not stated |  |

Options and types: [`BuildStyleOptions`](#buildstyleoptions).

#### `tessellateRouteRibbon`

*Function*

```ts
function tessellateRouteRibbon(coordinates: [number, number][]): RibbonMesh;
```

Tessellate a route line into a centreline triangle strip: two vertices
per point, each carrying the point's mercator position, a unit extrusion
normal (averaged between adjacent segments at joins - a bevel-free miter
that slightly thins very sharp corners, which is invisible on a soft
glow ribbon), and normalised progress along the line.

Pure: no WebGL, no map. Consecutive duplicate points are dropped; fewer
than two distinct points yield an empty mesh. Long segments are split by
[`subdivideMercator`](#subdividemercator) (no piece longer than 1/512 mercator units,
about 78 km at the equator), so on the globe the ribbon follows the same
path a MapLibre `line` layer draws instead of cutting through the planet;
a street-scale route gains no points.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `coordinates` | `[number, number][]` | required |  |

#### `tidyPlaceName`

*Function*

```ts
function tidyPlaceName(name: string): string;
```

A place name as riders read it: the feed's underscores become spaces
("Brighton_Marina" reads "Brighton Marina"), runs of spaces collapse,
and the ends are trimmed. `origin` and `destination` pass through this,
so labels, popups and `TransitVehiclesLayer.vehicles` agree.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `name` | `string` | required |  |

#### `toLngLat`

*Function*

```ts
function toLngLat(point: LngLatLike): [number, number];
```

Normalise any accepted shape to a `[lng, lat]` tuple.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `point` | [`LngLatLike`](#lnglatlike) | required |  |

Options and types: [`LngLatLike`](#lnglatlike).

#### `toPmtilesUrl`

*Function*

```ts
function toPmtilesUrl(url: string): string;
```

Ensure a tiles URL carries the `pmtiles://` protocol prefix.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |

#### `transitArchiveUrl`

*Function*

```ts
function transitArchiveUrl(url: string, indexUrl: string): string;
```

`pmtiles://` URL for an archive, resolving a path against the index.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `indexUrl` | `string` | required |  |

#### `transitFreshnessLabel`

*Function*

```ts
function transitFreshnessLabel(status: TransitFeedStatus, ageS: number | null): string;
```

The standard freshness label for a live-vehicle feed: `"Live, <n> s ago"`
while ok, `"Paused: last data <m> min ago"` once stale, `"Feed status
unknown"` when nothing vouches for the feed.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | [`TransitFeedStatus`](#transitfeedstatus) | required |  |
| `ageS` | `number \| null` | required |  |

Options and types: [`TransitFeedStatus`](#transitfeedstatus).

#### `transitStopFromProperties`

*Function*

```ts
function transitStopFromProperties(props: Record<string, unknown>): TransitStopFeature;
```

The clicked feature's properties as a [`TransitStopFeature`](#transitstopfeature).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `props` | `Record<string, unknown>` | required |  |

#### `transitTileJsonUrl`

*Function*

```ts
function transitTileJsonUrl(baseUrl: string, id: string, apiKey?: string): string;
```

The keyed TileJSON URL for a transit archive on the gateway. The key
rides as `api_key` so the gateway stamps it onto the tile URLs it
returns (MapLibre sends no Authorization header for tiles).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required |  |
| `id` | `string` | required |  |
| `apiKey?` | `string` | not stated |  |

#### `transitUrlsFromIndex`

*Function*

```ts
function transitUrlsFromIndex(index: unknown, indexUrl: string): {
    stops: string | null;
    lines: string | null;
};
```

The archive URLs from a tiles index document, or null for an archive the
index does not announce.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `index` | `unknown` | required |  |
| `indexUrl` | `string` | required |  |

#### `truncateChars`

*Function*

```ts
function truncateChars(text: string, max: number): string;
```

Cut a string to `max` UNICODE SCALARS (not UTF-16 code units). The Rust
validator counts `chars()`, so a plain `slice` both rejected valid emoji
strings and could cut a surrogate pair in half, and a lone surrogate
fails `serde_json` with a JSON parse error instead of the friendly
marker message. LOCKSTEP: the same helper exists in Studio's mirror.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | required |  |
| `max` | `number` | required |  |

#### `uploadProbeBatch`

*Function*

```ts
function uploadProbeBatch(baseUrl: string, apiKey: string, body: string, options?: UploadProbeOptions): Promise<ProbeUploadOutcome>;
```

POST one probe body to `{baseUrl}/v1/probe` with a bearer key. Transient
failures (network or `5xx`) are retried with bounded backoff; permanent ones
(`400`/`403`/`501`) are not, and neither is an aborted `signal`. Resolves
with the terminal outcome and never rejects, so a fire-and-forget
`void uploadProbeBatch(...)` is safe.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required |  |
| `apiKey` | `string` | required |  |
| `body` | `string` | required | the string returned by `GuidanceSession.finishProbeJson()`. |
| `options?` | [`UploadProbeOptions`](#uploadprobeoptions) | not stated |  |

Options and types: [`UploadProbeOptions`](#uploadprobeoptions).

#### `validateColourTheme`

*Function*

```ts
function validateColourTheme(theme: unknown): LutBytes;
```

Validate a `color_theme` block and decode its LUT. Throws the messages
`sn_style::validate_colour_theme` returns, word for word (and the same
as `website/lib/colour-theme.ts`).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `theme` | `unknown` | required |  |

#### `validateUseTheme`

*Function*

```ts
function validateUseTheme(value: unknown): asserts value is UseTheme | undefined;
```

Throw unless `value` is a valid `use_theme` (the Rust serde message).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `unknown` | required |  |

### Interfaces (@mapmap/maps)

[`AdrCheckOptions`](#adrcheckoptions), [`AdrCheckRequest`](#adrcheckrequest), [`AdrCheckResult`](#adrcheckresult), [`AdrDimensions`](#adrdimensions), [`AlertChipContent`](#alertchipcontent-1), [`AlertContrastIssue`](#alertcontrastissue), [`AlertPresentation`](#alertpresentation-1), [`BannerComponent`](#bannercomponent), [`BannerContent`](#bannercontent), [`BannerInstruction`](#bannerinstruction), [`BuildStyleOptions`](#buildstyleoptions), [`CameraAlert`](#cameraalert), [`CameraFix`](#camerafix), [`ClipMatrices`](#clipmatrices), [`ClipOptions`](#clipoptions), [`ColorThemeSpec`](#colorthemespec), [`ColourTheme`](#colourtheme), [`ColourThemeLayerOptions`](#colourthemelayeroptions), [`CreditInnerControl`](#creditinnercontrol), [`CreditOptions`](#creditoptions), [`DataLayerCategories`](#datalayercategories), [`DataLayerClusterOptions`](#datalayerclusteroptions), [`DataLayerEvent`](#datalayerevent), [`DataLayerExtrusionOptions`](#datalayerextrusionoptions), [`DataLayerGraduated`](#datalayergraduated), [`DataLayerOptions`](#datalayeroptions), [`DataLayerSimpleStyle`](#datalayersimplestyle), [`DataLayerStyle`](#datalayerstyle), [`DirectionIconStep`](#directioniconstep), [`EndpointMarker`](#endpointmarker), [`EndpointsDesign`](#endpointsdesign), [`FlythroughController`](#flythroughcontroller), [`FlythroughOptions`](#flythroughoptions), [`FlythroughPose`](#flythroughpose-1), [`GeocodeHit`](#geocodehit), [`GeocodeOptions`](#geocodeoptions), [`GeocoderOptions`](#geocoderoptions), [`GradeStyleOptions`](#gradestyleoptions), [`GroundReadableMap`](#groundreadablemap), [`GuidanceSpokenPrompt`](#guidancespokenprompt), [`Interaction`](#interaction), [`InteractionEvent`](#interactionevent), [`InteractionTarget`](#interactiontarget), [`IsochroneFeatureCollection`](#isochronefeaturecollection), [`IsochroneLayerOptions`](#isochronelayeroptions), [`LaneIndication`](#laneindication), [`LayerOverride`](#layeroverride), [`LogoOptions`](#logooptions), [`LutImageData`](#lutimagedata), [`MapDiagnosticsInput`](#mapdiagnosticsinput), [`MapMapBase`](#mapmapbase), [`MapMapOptions`](#mapmapoptions), [`MarkerItem`](#markeritem), [`MarkersBlock`](#markersblock), [`MarkersLayerIds`](#markerslayerids), [`MarkersLayerOptions`](#markerslayeroptions), [`NavAlertKindDesign`](#navalertkinddesign), [`NavAlertsDesign`](#navalertsdesign), [`NavBannerDesign`](#navbannerdesign), [`NavCameraDesign`](#navcameradesign), [`NavDesign`](#navdesign), [`NavigationCameraOptions`](#navigationcameraoptions), [`NavPuckDesign`](#navpuckdesign), [`NavRouteDesign`](#navroutedesign), [`NearestByDriveTimeOptions`](#nearestbydrivetimeoptions), [`ParsedRoute`](#parsedroute), [`PersistentIds`](#persistentids), [`PersistentLayerOptions`](#persistentlayeroptions), [`Place`](#place), [`PlacePointFeature`](#placepointfeature), [`PlacesFeatureCollection`](#placesfeaturecollection), [`PlacesIcon`](#placesicon), [`PlacesLabelOptions`](#placeslabeloptions), [`PlacesLayerIds`](#placeslayerids), [`PlacesLayerOptions`](#placeslayeroptions), [`PlacesSelectOptions`](#placesselectoptions), [`PoiCategoryDesign`](#poicategorydesign), [`PoiDesign`](#poidesign), [`PositionPuckOptions`](#positionpuckoptions), [`ProjectionArgs`](#projectionargs), [`Rendered`](#rendered), [`RenderedMarkerImage`](#renderedmarkerimage), [`RenderMarkerImageOptions`](#rendermarkerimageoptions), [`ReverseGeocodeOptions`](#reversegeocodeoptions), [`RibbonMesh`](#ribbonmesh), [`RouteEffectLayer`](#routeeffectlayer), [`RouteFlowOptions`](#routeflowoptions), [`RouteGeometry`](#routegeometry), [`RouteLayerIds`](#routelayerids), [`RouteLayerOptions`](#routelayeroptions), [`RouteLineStyle`](#routelinestyle), [`RouteOptions`](#routeoptions), [`SearchBoxMap`](#searchboxmap), [`SearchBoxOptions`](#searchboxoptions), [`SearchBoxSelectEvent`](#searchboxselectevent), [`SearchSuggestion`](#searchsuggestion), [`SetRouteEffectOptions`](#setrouteeffectoptions), [`SeverityProsody`](#severityprosody-1), [`ShowReachabilityOptions`](#showreachabilityoptions), [`SnappedOrigin`](#snappedorigin), [`SpeakOptions`](#speakoptions), [`StepGuidance`](#stepguidance), [`SuggestUrlOptions`](#suggesturloptions), [`TerrainOptions`](#terrainoptions), [`Theme`](#theme), [`ThemeEffects`](#themeeffects), [`ThemeSchedulerOptions`](#themescheduleroptions), [`TransitFeedAnswer`](#transitfeedanswer), [`TransitFeedReport`](#transitfeedreport), [`TransitFix`](#transitfix), [`TransitStopFeature`](#transitstopfeature), [`TransitStopsLayerOptions`](#transitstopslayeroptions), [`TransitVehicle`](#transitvehicle), [`TransitVehiclesLayerOptions`](#transitvehicleslayeroptions), [`TruckParams`](#truckparams), [`UploadProbeOptions`](#uploadprobeoptions), [`VoiceGuidanceOptions`](#voiceguidanceoptions), [`VoiceGuidanceUpdate`](#voiceguidanceupdate), [`VoiceInstruction`](#voiceinstruction)

#### `AdrCheckOptions`

*Interface*

```ts
interface AdrCheckOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required | Gateway base URL, e.g. `https://api.mapmap.ai`. |
| `apiKey?` | `string` | not stated | Gateway API key (`snk_…`). |

#### `AdrCheckRequest`

*Interface*

```ts
interface AdrCheckRequest
```

Input for the gateway's `POST /adr/check` compliance endpoint: the
vehicle's ADR profile plus the category of the tunnel to check. Serialised
on the wire as `{"adr": <AdrVehicleProfile>, "tunnel_category": "A".."E"}`
(see `sn-gateway` `routes/adr.rs`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `hazmat` | `boolean` | required | Carrying dangerous goods. |
| `tunnelCode?` | `string` | not stated | The *vehicle load's* ADR 8.6.4 tunnel restriction code, e.g. `"C"` or `"C/E"`. Omit when carrying dangerous goods of unknown code (the gateway then conservatively treats the load as code `B`). This is not the tunnel's category, that goes in `tunnelCategory`. |
| `dimensions?` | [`AdrDimensions`](#adrdimensions) | the EU standard artic maximums | Vehicle dimensions. Defaults to the EU standard artic maximums. |
| `tunnelCategory` | [`AdrTunnelCategory`](#adrtunnelcategory) | required | The ADR category of the *tunnel* to check, `"A"` … `"E"`. |

#### `AdrCheckResult`

*Interface*

```ts
interface AdrCheckResult
```

Parsed response from `POST /adr/check`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | `string` | required | `"allowed"` or `"blocked"`. |
| `reason?` | `string` | not stated | Human-readable justification citing ADR 8.6.4 where blocked. |
| `raw` | `Record<string, unknown>` | required | The raw response body. |

#### `AdrDimensions`

*Interface*

```ts
interface AdrDimensions
```

Physical truck dimensions for the ADR vehicle profile (mirrors
`sn_adr::TruckDimensions`). All optional; unset fields default to the EU
96/53/EC maximum-authorised articulated vehicle (4.0 m high, 2.55 m wide,
16.5 m long, 40 t gross), matching the Rust `Default`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `heightM?` | `number` | `4.0` | Vehicle height, metres. Defaults to `4.0`. |
| `widthM?` | `number` | `2.55` | Vehicle width, metres. Defaults to `2.55`. |
| `lengthM?` | `number` | `16.5` | Vehicle length, metres. Defaults to `16.5`. |
| `grossWeightT?` | `number` | `40` | Gross combination weight, tonnes. Defaults to `40`. |
| `axleLoadT?` | `number` | not stated | Heaviest single-axle load, tonnes, if known. |
| `axleCount?` | `number` | not stated | Number of axles, if known. |

#### `AlertChipContent`

*Interface*

```ts
interface AlertChipContent
```

Template-shaped content for an alert: the fields a head unit can
actually render.

CarPlay's `CPNavigationAlert` and Android Auto's `Alert` inside a
`NavigationTemplate` take a title, a subtitle and an image - the system
draws them, and none of the chip's colour, radius or position tokens
survive the trip. Hosts targeting a head unit should read this instead
of the DOM chip, so styling degrades gracefully rather than being
assumed to render.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `kind` | [`NavCameraKind`](#navcamerakind-1) | required |  |
| `title` | `string` | required | Primary line, e.g. "Average speed check". |
| `subtitle` | `string` | required | Secondary line: limit and distance when known, else empty. |
| `iconUrl?` | `string` | not stated | Override icon URL when the design sets one for this kind. |

#### `AlertContrastIssue`

*Interface*

```ts
interface AlertContrastIssue
```

A chip colour pair that falls below [`ALERT_CONTRAST_MIN`](#alert_contrast_min).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `state` | `"calm" \| "escalated"` | required | Which chip state the pair belongs to. |
| `textColor` | `string` | required | The text colour of the failing pair. |
| `background` | `string` | required | The background colour of the failing pair. |
| `ratio` | `number` | required | The measured ratio, rounded to 2 dp. |
| `minimum` | `number` | required | The floor it failed to clear. |

#### `AlertPresentation`

*Interface*

```ts
interface AlertPresentation
```

What a host should actually do with an alert, given the design and
whether the driver is currently over the limit.

The rule this encodes, and the reason it is a pure function rather than
scattered branches in a renderer: the VISUAL alert is unconditional and
calm, and AUDIO is the escalation channel. A driver within the limit
still sees the chip; only the sound waits for speeding.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `visible` | `boolean` | required | Whether to show the chip at all. |
| `escalated` | `boolean` | required | Whether the chip should render in its escalated state. |
| `audible` | `boolean` | required | Whether audio should fire now. |
| `audioMode` | [`NavAlertAudioMode`](#navalertaudiomode) | required | Which audio channel to use when `audible`. |
| `soundUrl` | `string` | required | The earcon to play for THIS state: the design's calm sound, or its escalated variant when `escalated`. Always set (even when `audible` is false) so a host can pre-load it; play it only when `audible`. |
| `leadDistanceM` | `number` | required | Metres of lead this design guarantees before the camera. |

#### `BannerComponent`

*Interface*

```ts
interface BannerComponent
```

A typed fragment of banner content.

Field names are the wire names, because these objects come straight off
`route.raw` without a mapping pass (see [`extractGuidance`](#extractguidance)).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `string` | required |  |
| `text` | `string` | required |  |
| `directions?` | `string[]` | not stated |  |
| `active?` | `boolean` | not stated |  |
| `active_direction?` | `string` | not stated | Which of `directions` guidance recommends (gateway `active_direction`). |

#### `BannerContent`

*Interface*

```ts
interface BannerContent
```

One banner line.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | required |  |
| `type?` | `string` | not stated |  |
| `modifier?` | `string` | not stated |  |
| `components?` | [`BannerComponent[]`](#bannercomponent) | not stated |  |

#### `BannerInstruction`

*Interface*

```ts
interface BannerInstruction
```

The visual banner shown while travelling a step.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `distanceAlongGeometry` | `number` | required |  |
| `primary` | [`BannerContent`](#bannercontent) | required |  |
| `secondary?` | [`BannerContent`](#bannercontent) | not stated |  |
| `sub?` | [`BannerContent`](#bannercontent) | not stated |  |

#### `BuildStyleOptions`

*Interface*

```ts
interface BuildStyleOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `labelLanguage?` | `string` | Label language: an ISO 639 code selects `name:<code>` with a local-name fallback; the literal `"local"` selects the local name outright; omitted keeps the default (English-first) | Label language: an ISO 639 code selects `name:<code>` with a local-name fallback; the literal `"local"` selects the local name outright; omitted keeps the default (English-first). The tiles carry the OSM `name:*` set, so switching involves no tile rebuild. |
| `theme?` | `MapMapTheme \| Theme` | `"light"` | Theme to build: a built-in name (`"light"` / `"dark"`) or a full [`Theme`](#theme) document. Defaults to `"light"`. |
| `territoryTilesUrl?` | `string` | not stated | PMTiles URL for the territory tiles. Accepts either a `pmtiles://…` URL or a plain `https://…` URL (the `pmtiles://` prefix is added for you). Always wins over the keyed default that `apiKey` selects. |
| `apiKey?` | `string` | not stated | Gateway API key (`snk_…`). With no `territoryTilesUrl`, the basemap reads the keyed gateway tiles ([`keyedTilesUrl`](#keyedtilesurl)) instead of the keyless CDN, which refuses browser pages outside mapmap.ai and localhost. The key is written into the source URL as `?api_key=`. |
| `baseUrl?` | `string` | [`DEFAULT_GATEWAY_URL`](#default_gateway_url) | Gateway for the keyed tiles. Defaults to [`DEFAULT_GATEWAY_URL`](#default_gateway_url). Only read when `apiKey` is set and `territoryTilesUrl` is not. |
| `roadShields?` | `boolean` | `true` | Road-number shields (`road-shields`, one layer keyed by each road's own country: M25, A 7, I-280...). Defaults to `true`; `false` leaves the layer out. Mirrors the per-territory `road_shields` block of `sn_style`, which the SDK theme does not carry. |

#### `CameraAlert`

*Interface*

```ts
interface CameraAlert
```

One camera alert to present. `kind` is normalised through
[`navCameraKind`](#navcamerakind), so a raw gateway value (`"redlight"`) is accepted.

JURISDICTION: whether a camera reaches you at all is decided server-side
before the response leaves the gateway. Nothing in this module can widen
that; it styles what the policy already permitted.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `kind` | `NavCameraKind \| string` | required | Camera regime; wire values are normalised. |
| `limitKph?` | `number` | not stated | Enforced limit in km/h, when known. |
| `distanceM?` | `number` | not stated | Metres to the camera, when known. |

#### `CameraFix`

*Interface*

```ts
interface CameraFix
```

A GPS fix to follow.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | `number` | required |  |
| `lon` | `number` | required |  |

#### `ClipMatrices`

*Interface*

```ts
interface ClipMatrices
```

The matrices a tier-2 layer draws with this frame.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `mercator` | `Float64Array` | required | Local metres to clip space through the mercator path (today's matrix on a mercator map). |
| `globe` | `Float64Array \| null` | required | Local metres to clip space on the globe; `null` on a mercator frame. |
| `transition` | `number` | required | The globe weight, MapLibre's `projectionTransition`: 0 mercator, 1 globe. |

#### `ClipOptions`

*Interface*

```ts
interface ClipOptions
```

Options for `MapMapMap.addClip`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `polygon` | `Polygon \| MultiPolygon` | required | The area to clear, in lng/lat. At most [`CLIP_MAX_VERTICES`](#clip_max_vertices) vertices in total. |
| `types?` | [`ClipType[]`](#cliptype) | `["buildings"]` | Which skeleton layers to clip. Default `["buildings"]`. Ignored when `layers` is given. |
| `layers?` | `string[]` | not stated | Explicit layer ids to clip instead of `types`. |
| `minzoom?` | `number` | [`CLIP_DEFAULT_MINZOOM`](#clip_default_minzoom) | Below this zoom the clip is not evaluated (the features draw as normal), which keeps the per-feature `distance` test off low-zoom tiles that hold thousands of buildings. Default [`CLIP_DEFAULT_MINZOOM`](#clip_default_minzoom). |

#### `ColorThemeSpec`

*Interface*

```ts
interface ColorThemeSpec
```

A runtime colour theme for `MapMapMap.setColorTheme`: a 16x16x16
LUT given one of two ways, plus a strength. Mapbox-compatible: `data`
takes what Mapbox's `color-theme.data` takes.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `data?` | `string` | not stated | A Mapbox-format colour-theme PNG (256x16): a `data:` URI, an `http(s)://` URL served with CORS, or the raw base64 of the PNG. |
| `lut?` | `LutBytes \| string` | not stated | The LUT itself: 12288 bytes (see `colour-theme.ts`) or their base64. |
| `strength?` | `number` | 1 | Blend with the ungraded map, 0..1. Default 1. |

#### `ColourTheme`

*Interface*

```ts
interface ColourTheme
```

The theme document's `color_theme` block. Mirrors `sn_style::ColourTheme`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lut` | `string` | required | Base64 of exactly 12288 bytes. |
| `size?` | `number` | not stated | Fixed at 16. |
| `strength?` | `number` | not stated | 0..1, default 1. |
| `name?` | `string` | not stated | Display name, `[a-z0-9-]{1,40}`. |

#### `ColourThemeLayerOptions`

*Interface*

```ts
interface ColourThemeLayerOptions
```

Options for [`ColourThemeLayer`](#colourthemelayer).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` | `"mapmap-colour-theme"` | Layer id. Default `"mapmap-colour-theme"`. |
| `lut` | [`LutBytes`](#lutbytes) | required | The LUT, [`LUT_BYTES`](#lut_bytes) bytes (see `colour-theme.ts`). |
| `strength?` | `number` | not stated | 0..1, default 1. |

#### `CreditInnerControl`

*Interface*

```ts
interface CreditInnerControl
```

The MapLibre control being dressed (typed structurally, so it needs no
maplibre-gl import and works with any instance the host app bundles).

**Methods**

- `onAdd(map: unknown): HTMLElement`
- `onRemove(map?: unknown): void`
- `getDefaultPosition?(): unknown`

#### `CreditOptions`

*Interface*

```ts
interface CreditOptions
```

Options for [`CreditControl`](#creditcontrol).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `position?` | `ChromeCorner` | `bottom-left` | The corner it is mounted in; sets which edges the insets apply to. Default `bottom-left`. |
| `theme?` | [`MapBrandTheme`](#mapbrandtheme) | not stated | Initial variant. [`MapMapMap`](#mapmapmap) keeps it in step with the drawn style; default `day`. |

#### `DataLayerCategories`

*Interface*

```ts
interface DataLayerCategories
```

A colour per value of one property.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `property` | `string` | required |  |
| `values` | `Record<string, string>` | required | Property value (compared as a string) to colour. |
| `fallback?` | `string` | `simple.color`, then Signal blue | Colour for any other value. Default `simple.color`, then Signal blue. |

#### `DataLayerClusterOptions`

*Interface*

```ts
interface DataLayerClusterOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `radius?` | `number` | MapLibre's default (50) when absent | Cluster radius in px. MapLibre's default (50) when absent. |
| `maxZoom?` | `number` | MapLibre's default (14) when absent | Highest zoom that clusters. MapLibre's default (14) when absent. |
| `properties?` | `Record<string, unknown>` | not stated | MapLibre `clusterProperties` (aggregates per cluster). |

#### `DataLayerEvent`

*Interface*

```ts
interface DataLayerEvent
```

What `DataLayer.on` handlers receive.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"click" \| "hover" \| "select"` | required |  |
| `feature` | `MapGeoJSONFeature \| undefined` | required | The feature clicked or hovered; `undefined` when the pointer left. |
| `lngLat` | `LngLat \| undefined` | required |  |
| `selected` | `GeoJSON.Feature[]` | required | The selection after the event. |

#### `DataLayerExtrusionOptions`

*Interface*

```ts
interface DataLayerExtrusionOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `heightProperty` | `string` | required | Property holding the height in metres. |
| `base?` | `number \| string` | 0 | Base height: metres, or the name of a property holding it. Default 0. |
| `opacity?` | `number` | 0.8 | 0 to 1. Default 0.8. |

#### `DataLayerGraduated`

*Interface*

```ts
interface DataLayerGraduated
```

A colour ramp over a numeric property.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `property` | `string` | required |  |
| `stops` | `[number, string][]` | required | `[value, colour]` pairs, values strictly ascending. |

#### `DataLayerOptions`

*Interface*

```ts
interface DataLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `data` | `GeoJSON.FeatureCollection \| string` | required | Inline GeoJSON, a GeoJSON URL, or `pmtiles://https://...` for vector tiles. |
| `sourceLayer?` | `string` | not stated | The vector layer to draw; required for a `pmtiles://` source. |
| `mode?` | [`DataLayerMode`](#datalayermode) | `"auto"`: picks by the first feature's geometry when every feature shares it | Default `"auto"`: picks by the first feature's geometry when every feature shares it; a mixed collection, a URL or tiles get fill, line and point layers, each filtered to its geometry. |
| `style?` | [`DataLayerStyle`](#datalayerstyle) | not stated |  |
| `extrusion?` | [`DataLayerExtrusionOptions`](#datalayerextrusionoptions) | height property `height` | Required for `mode: "extrusion"` (default height property `height`). |
| `cluster?` | [`DataLayerClusterOptions`](#datalayerclusteroptions) | not stated |  |
| `promoteId?` | `string` | the feature's own `id` | A property holding a unique, stable feature id. Default: the feature's own `id`; inline data without ids gets generated ones (hover and selection work, `updateData` does not). |
| `hover?` | `boolean` | `true` | Highlight the feature under the pointer. Default `true`. |
| `select?` | `"single" \| "multi" \| false` | `"single"` | Click to select. Default `"single"`. |
| `popup?` | `(feature: GeoJSON.Feature, lngLat: LngLat) => string \| HTMLElement` | not stated | Content for a popup on click (HTML string or an element). |
| `slot?` | [`Slot`](#slot) | `"middle"`: above roads, below labels | Where in the layer stack. Default `"middle"`: above roads, below labels. |
| `attribution?` | `string` | not stated | Credit for the data, shown in the map attribution. |
| `fitBounds?` | `boolean` | `false` | Fit the map to inline data once it is added. Default `false`. |
| `id?` | `string` | `mapmap-data-<n>` | Id prefix for the source and layers. Default `mapmap-data-<n>`. |

#### `DataLayerSimpleStyle`

*Interface*

```ts
interface DataLayerSimpleStyle
```

One look for every feature.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `color?` | `string` | Signal blue | Fill, line or circle colour. Default Signal blue. |
| `opacity?` | `number` | fills 0.35, lines 0.9, circles 0.9 | 0 to 1. Defaults: fills 0.35, lines 0.9, circles 0.9. |
| `radius?` | `number` | 6 | Circle radius in px. Default 6. |
| `width?` | `number` | 2 | Line width in px (and polygon outlines). Default 2. |
| `outlineColor?` | `string` | the fill colour / white | Polygon outline and circle stroke colour. Default the fill colour / white. |

#### `DataLayerStyle`

*Interface*

```ts
interface DataLayerStyle
```

Styling: `categories` wins over `graduated`, which wins over `simple`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `simple?` | [`DataLayerSimpleStyle`](#datalayersimplestyle) | not stated |  |
| `categories?` | [`DataLayerCategories`](#datalayercategories) | not stated |  |
| `graduated?` | [`DataLayerGraduated`](#datalayergraduated) | not stated |  |

#### `DirectionIconStep`

*Interface* · also exported from `@mapmap/maps/direction-icons`

```ts
interface DirectionIconStep
```

The slice of an OSRM route step that icon resolution reads.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `maneuver?` | `{ type?: string; modifier?: string; bearing_before?: number; bearing_after?: number; }` | not stated |  |
| `driving_side?` | `string` | not stated | "left" \| "right": the side traffic drives on at this step. OSRM and Mapbox routers emit it on every step; the MapMap gateway emits it where the engine establishes it (roundabout and u-turn steps). |
| `mode?` | `string` | not stated | OSRM travel mode; "ferry" selects the ferry icon. |

#### `EndpointMarker`

*Interface*

```ts
interface EndpointMarker
```

One designed endpoint marker: a coloured pin (or dot) drawn from the
shared marker rasteriser, optionally with a text label underneath.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `icon?` | [`MarkerGlyphId`](#markerglyphid) | not stated | Glyph drawn on the pin, or `"dot"` for a plain circle. |
| `text?` | `string` | not stated | Short text (1-3 characters) drawn in the pin head INSTEAD of the glyph - `"A"`, `"B"`, `"1"`. Overrides `icon`. |
| `colour?` | `string` | not stated | Marker colour, any CSS colour the canvas accepts. |
| `size?` | [`MarkerSize`](#markersize) | not stated | Size preset: `"s"` 24 px, `"m"` 32 px, `"l"` 40 px. |
| `label?` | `string` | not stated | Optional caption drawn below the marker, with a white halo. |

#### `EndpointsDesign`

*Interface*

```ts
interface EndpointsDesign
```

How a route's start, end and intermediate waypoints are drawn. Every
field is optional; `false` switches an endpoint off entirely.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `start?` | `EndpointMarker \| false` | a green `dot`, size `m` | The first route coordinate. Default a green `dot`, size `m`. |
| `end?` | `EndpointMarker \| false` | a signal-blue `pin`, size `m` | The last route coordinate. Default a signal-blue `pin`, size `m`. |
| `waypoint?` | `EndpointMarker \| false` | a signal-blue `pin`, size `s` | Every intermediate stop. Default a signal-blue `pin`, size `s`. |
| `numberWaypoints?` | `boolean` | `true` | Draw each waypoint's 1-based index in its pin head (`1`, `2`, `3`) instead of the glyph. Default `true`. An explicit `waypoint.text` wins over the number. |

#### `FlythroughController`

*Interface*

```ts
interface FlythroughController
```

The handle a [`flythrough`](#flythrough) returns.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `speed` | `number` | required | Playback-rate multiplier (`1` = real duration, `2` = double). |
| `readonly progress` | `number` | required | Current progress, 0-1. |
| `readonly playing` | `boolean` | required | Whether the replay is currently advancing. |

**Methods**

- `play(): void`: Start (or resume) the replay. Restarts from 0 when it had finished.
- `pause(): void`: Freeze the replay where it is.
- `stop(): void`: Pause and rewind to the start (the camera jumps to the start pose).
- `seek(t: number): void`: Scrub to progress `t` (0-1, clamped) and apply the pose immediately.
- `onProgress(listener: (t: number) => void): () => void`: Subscribe to progress updates; returns the unsubscribe function.
- `destroy(): void`: Pause and detach everything. The controller is dead afterwards.

#### `FlythroughOptions`

*Interface*

```ts
interface FlythroughOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `pitch?` | `number` | `60` (the cinematic chase look | Camera tilt in degrees, clamped 0-85. Default `60` (the cinematic chase look; see `NavigationCameraOptions.pitch` for caveats above 60). |
| `zoom?` | `number` | `16` | Camera zoom held through the replay. Default `16`. |
| `durationMs?` | `number` | `20000` | Total replay duration in milliseconds. Default `20000`. Ignored when `speedMps` is given. |
| `speedMps?` | `number` | not stated | Replay ground speed in metres/second along the route - the duration becomes `routeLength / speedMps`. Wins over `durationMs`. |
| `lookAheadM?` | `number` | `200` | How far up the road the chase cam looks to derive its bearing, metres. Default `200`; larger = calmer bearing on wiggly roads. |
| `bearingEase?` | `number` | `3` | Bearing smoothing rate per second (the fraction of the remaining shortest-arc turn closed each second). Default `3`; higher = snappier. |

#### `FlythroughPose`

*Interface*

```ts
interface FlythroughPose
```

A camera pose along a flythrough: where to look from, and which way.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `center` | `[number, number]` | required | Camera target on the route, `[lng, lat]`. |
| `bearing` | `number` | required | Chase-cam bearing: towards a point `lookAheadM` up the road. |

#### `GeocodeHit`

*Interface*

```ts
interface GeocodeHit
```

One geocode result, flattened from the wire FeatureCollection.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required | `[lng, lat]` - GeoJSON order, ready for a MapLibre camera or marker. |
| `name?` | `string` | not stated | Display name (`properties.name`), when the hit carries one. |
| `id?` | `string` | not stated | Stable id (`osm:…`), first-party backend only. |
| `kind?` | `string` | not stated | Document kind: `poi`, `address`, `street`, `locality`, `postcode`, … |
| `housenumber?` | `string` | not stated | Address parts, present when known. |
| `street?` | `string` | not stated |  |
| `city?` | `string` | not stated |  |
| `postcode?` | `string` | not stated |  |
| `state?` | `string` | not stated |  |
| `country?` | `string` | not stated |  |
| `countrycode?` | `string` | not stated |  |
| `categories?` | `string[]` | not stated | POI categories, e.g. `["arts_centre"]`. |
| `distanceM?` | `number` | not stated | Distance from the query point in metres - reverse hits only. |
| `details?` | `Record<string, unknown>` | not stated | Curated OSM display attributes on first-party POI hits: `opening_hours`, `phone`, `website`, `brand`, `wheelchair`, EV `socket:*` connectors and friends. Keys absent in OSM are omitted. |
| `raw` | `Record<string, unknown>` | required | The raw GeoJSON feature, for anything not modelled above. |

#### `GeocodeOptions`

*Interface*

```ts
interface GeocodeOptions
```

Options for forward geocoding (`GET /geocode`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `limit?` | `number` | not stated | Maximum results, 1-10. |
| `lang?` | `string` | not stated | Result language, e.g. `"en"`, `"de"`, `"fr"`. |
| `bias?` | [`LngLatLike`](#lnglatlike) | not stated | Location bias: reorders candidates towards this point, never excludes one. The map centre is the natural value for a search box. |
| `zoom?` | `number` | not stated | Map zoom (0-20) controlling the bias strength: zoomed in favours nearby results, zoomed out favours prominence. Only meaningful with `bias`. |
| `bbox?` | `[number, number, number, number]` | not stated | Hard bounding-box filter, `[minLon, minLat, maxLon, maxLat]` (WGS84). Unlike `bias` this excludes results outside the box outright. |

#### `GeocoderOptions`

*Interface*

```ts
interface GeocoderOptions
```

What the Geocoder needs. A `MapMapMap` satisfies this shape, so
`new Geocoder(map)` reuses the map's gateway origin and key - the same
convention as every other SDK class.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl` | `string` | required | Gateway base URL, e.g. `https://api.mapmap.ai`. |
| `apiKey?` | `string \| undefined` | not stated | Gateway API key (`snk_…`). |

#### `GradeStyleOptions`

*Interface*

```ts
interface GradeStyleOptions
```

Options for [`gradeStyleColours`](#gradestylecolours).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `strength?` | `number` | not stated | 0..1, default 1. |
| `skipLayerIds?` | `ReadonlySet<string>` | not stated | Layer ids to leave untouched (the theme's `use_theme: "none"` layers). |

#### `GroundReadableMap`

*Interface*

```ts
interface GroundReadableMap
```

The slice of a MapLibre map the live read needs (structural, so it works
with whichever maplibre-gl copy the host app bundles).

**Methods**

- `getLayersOrder?(): string[]`
- `getStyle?(): unknown`
- `getLayer(id: string): { type?: string; } | undefined`
- `getPaintProperty(id: string, name: string): unknown`
- `getLayoutProperty(id: string, name: string): unknown`

#### `GuidanceSpokenPrompt`

*Interface*

```ts
interface GuidanceSpokenPrompt
```

The spoken prompt of a core guidance update.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | required | Plain text for speech synthesis. |
| `ssml?` | `string \| null` | not stated | SSML form; browsers reject raw SSML, so it is stripped to text. |
| `triggerDistanceM` | `number` | required | Metres before the manoeuvre at which the core released the prompt. |
| `utteranceId` | `string` | required | Unique utterance id, the de-duplication key. |

#### `Interaction`

*Interface*

```ts
interface Interaction
```

One interaction, as passed to `MapMapMap.addInteraction`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | [`InteractionType`](#interactiontype) | required |  |
| `target?` | [`InteractionTarget`](#interactiontarget) | not stated | Absent: the map itself (fires with no feature). |
| `filter?` | `FilterSpecification` | not stated | Only features matching this filter count as hits. |
| `handler` | `(e: InteractionEvent) => boolean \| void` | required | Return `true` to stop propagation: interactions on features further down, and map-level interactions, do not get this event. Applies to `click` and `mousemove`; `mouseenter` and `mouseleave` always fire for every interaction listening. |

#### `InteractionEvent`

*Interface*

```ts
interface InteractionEvent
```

What an interaction handler receives.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `feature?` | `MapGeoJSONFeature` | not stated | The feature under the pointer, topmost first. `undefined` for a map-level interaction (no `target`). For `mouseleave` it is the feature the pointer just left. |
| `lngLat` | `LngLat` | required |  |
| `originalEvent` | `MouseEvent` | required |  |

#### `InteractionTarget`

*Interface*

```ts
interface InteractionTarget
```

What an interaction listens on: one layer, or a featureset.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `layerId?` | `string` | not stated |  |
| `featuresetId?` | [`FeaturesetId`](#featuresetid) | not stated |  |

#### `IsochroneFeatureCollection`

*Interface*

```ts
interface IsochroneFeatureCollection
```

The GeoJSON FeatureCollection the gateway returns, passed through.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"FeatureCollection"` | required |  |
| `snapped_origin?` | [`SnappedOrigin`](#snappedorigin) | not stated | Where the rings start: the origin snapped onto the nearest road. Also on every feature's `properties.snapped_origin`. |
| `features` | `{ type: "Feature"; geometry: { type: string; coordinates: unknown; }; properties?: Record<string, unknown> \| null; }[]` | required |  |

#### `IsochroneLayerOptions`

*Interface*

```ts
interface IsochroneLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl?` | `string` | the map's `baseUrl` when given a map | Gateway base URL. Defaults to the map's `baseUrl` when given a map. |
| `apiKey?` | `string` | the map's `apiKey` when given a map | Gateway API key. Defaults to the map's `apiKey` when given a map. |
| `id?` | `string` | `"mapmap-isochrone"` | Unique id prefix for the source/layers. Defaults to `"mapmap-isochrone"`. |

#### `LaneIndication`

*Interface*

```ts
interface LaneIndication
```

One lane entry of a banner lane diagram.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `directions` | `string[]` | required | Turn directions this lane allows (OSRM indication strings). |
| `valid?` | `boolean` | not stated | Whether the lane can be used for the maneuver. `undefined` means unknown, not unusable, banner components omit the flag for non-recommended lanes (see [`bannerLanes`](#bannerlanes)). |
| `active?` | `boolean` | not stated | Whether guidance recommends this lane. |
| `activeDirection?` | `string` | not stated | Which one of `directions` guidance actually takes from this lane, when the server resolves it (the gateway's `active_direction`, set only on active lanes whose mask decodes to a single indication). A lane may permit several movements, "straight or right" is the common one, and only this field says which of them the route uses. Draw the lane arrow from it, falling back to the first permitted direction: see [`laneArrowDirection`](#lanearrowdirection). |

#### `LayerOverride`

*Interface*

```ts
interface LayerOverride
```

Per-layer override: merged onto the skeleton layer with the same id.
Mirrors `sn_style::LayerOverride`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `visible?` | `boolean` | not stated | Hide the layer entirely (`layout.visibility = "none"`). |
| `paint?` | `Record<string, unknown>` | not stated | Paint properties merged over the skeleton's (per-key, override wins). |
| `layout?` | `Record<string, unknown>` | not stated | Layout properties merged over the skeleton's. |
| `filter?` | `unknown` | not stated | Replace the skeleton's filter (per-attribute styling hook). |
| `minzoom?` | `number` | not stated |  |
| `maxzoom?` | `number` | not stated |  |
| `use_theme?` | [`UseTheme`](#usetheme) | not stated | Whether the theme's `Theme.color_theme` grades this layer (Mapbox `*-use-theme`): `"none"` keeps its colours as written. Mirrors `LayerOverride::use_theme`. |

#### `LogoOptions`

*Interface*

```ts
interface LogoOptions
```

Options for [`LogoControl`](#logocontrol).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `position?` | [`LogoPosition`](#logoposition) | `bottom-right`, the opposite corner from the credit, which the SDK mounts bottom-left (movable with `attributionPosition`) | Corner to render in. Default `bottom-right`, the opposite corner from the credit, which the SDK mounts bottom-left (movable with `attributionPosition`). That is the canonical MapMap layout: credit small bottom-left, mark bottom-right, never both in one corner (stacking the mark on the OSM credit makes a licence-required line unreadable). [`MapMapMap`](#mapmapmap) enforces that: if this corner is the credit's corner, the mark is mounted in the opposite bottom corner instead, with one `console.warn` for that map. When you add a `LogoControl` to a map yourself, pass the same corner here as to `addControl`, so its insets land on the right edges. |
| `href?` | `string` | the MapMap site | Link target when clicked. Default the MapMap site. |
| `theme?` | [`MapBrandTheme`](#mapbrandtheme) | not stated | Initial variant: `day` (default) for light maps, `night` for dark. [`MapMapMap`](#mapmapmap) sets and updates this itself from the drawn style. |

#### `LutImageData`

*Interface*

```ts
interface LutImageData
```

The shape of `ImageData` that [`lutFromImageData`](#lutfromimagedata) reads.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `width` | `number` | required |  |
| `height` | `number` | required |  |
| `data` | `Uint8ClampedArray \| Uint8Array` | required |  |

#### `MapDiagnosticsInput`

*Interface*

```ts
interface MapDiagnosticsInput
```

What `runMapDiagnostics` needs; everything optional and structural.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `container?` | `string \| HTMLElement` | not stated | The `container` option as given: an element or an element id. |
| `map?` | `unknown` | not stated | The constructed map (or any object with a MapLibre-style `on`). Used to watch for tile-request auth failures. |
| `maplibre?` | `unknown` | not stated | The maplibre-gl module the SDK is using (duplicate detection). |
| `apiKey?` | `string \| undefined` | not stated | The gateway API key, if one was configured. |

#### `MapMapBase`

*Interface*

```ts
interface MapMapBase
```

One entry in the catalogue.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | [`MapMapBaseId`](#mapmapbaseid) | required | Stable id: what [`baseTheme`](#basetheme) takes, and what the static-map API and the MCP `create_style` tool accept by the same name. |
| `label` | `string` | required | Short display label, e.g. `"Navigator Night"`. |
| `description` | `string` | required | One line saying what the base is for. |
| `group` | [`MapMapBaseGroup`](#mapmapbasegroup) | required | `"base"` for the two stock slates, `"designed"` for a drawn style. |
| `restrictions` | `boolean` | required | True when the base is designed to be seen with MapMap's vehicle-restriction overlay. Only `fleet` sets it. **The SDK cannot draw that overlay.** It is a separate vector source the mapmap.ai website installs around the compiled style, because the render tiles carry no restriction attributes and no compiled style can express them. The flag is here so a caller building Fleet through this SDK knows it is getting the palette and nothing else, rather than shipping an industrial-looking map that appears to claim restriction data. Draw restrictions yourself from the routing API's own `check_clearance_on_route` / `check_adr_tunnel`, which answer for a real vehicle rather than colouring a basemap. |
| `notCompiledHere` | `readonly string[]` | required | Theme fields the canonical document carries that [`buildStyle`](#buildstyle) cannot honour, so a caller can see what a browser-side build of this base leaves out rather than guessing. Empty for seven of the ten. |
| `theme` | `() => Theme` | required | A fresh document, safe to edit. |

#### `MapMapOptions`

*Interface*

```ts
interface MapMapOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `logo?` | `boolean \| LogoOptions` | not stated | The MapMap wordmark on the map (like the Google Maps / Mapbox marks). Always on, at bottom-right (the attribution's opposite corner); pass `{ position, href }` to move or relink it. The mark cannot be taken off. Every displayed MapMap map carries it, which is a condition of the SDK licence, the same model as Mapbox GL JS v2+. `false` (and `true`) are still accepted so existing code keeps compiling: passing `false` is ignored, the mark is added anyway, and the SDK logs one `console.warn` for that map. `false` is soft-deprecated: it does nothing and will be dropped from the type in the next major. Use `{ position }` to get the mark out of the way instead. |
| `container` | `string \| HTMLElement` | required | The container element or its id. |
| `apiKey?` | `string` | not stated | Gateway API key (`snk_…`), used by routing and ADR helpers and by the basemap. With a key and no `territoryTilesUrl`, the default styles read the keyed gateway tiles (`{baseUrl}/tiles/planet/tiles.json?api_key=…`), which work on any website; without one they read the keyless CDN, which serves only mapmap.ai and a local development server. A `style` URL on the gateway (`/styles/…`, `/tiles/…`) gets the key added the same way. |
| `baseUrl?` | `string` | `https://api.mapmap.ai` | Gateway base URL for routing, ADR and the keyed basemap. Defaults to `https://api.mapmap.ai`. |
| `style?` | `MapMapTheme \| Theme \| StyleSpecification \| string` | `"light"` | Style to load. A theme name (`"light"` / `"dark"`), a MapMap Studio `Theme` document, a full MapLibre `StyleSpecification`, or a style URL. Defaults to `"light"`. |
| `territoryTilesUrl?` | `string` | not stated | Override the territory PMTiles URL used by the default styles. Always wins over the keyed default that `apiKey` selects. |
| `center?` | `[number, number]` | the UK | Initial centre as `[lng, lat]`. Defaults to the UK. |
| `zoom?` | `number` | `5` | Initial zoom. Defaults to `5`. |
| `terrain?` | `boolean \| TerrainOptions` | not stated | 3D terrain from a DEM. `true` uses sensible defaults; pass [`TerrainOptions`](#terrainoptions) to set exaggeration, point at your own DEM tiles, or add relief shading. The default DEM is the same dataset the gateway reports from `POST /elevation`, so a map and an elevation query agree. |
| `mapOptions?` | `Partial<Omit<MapOptions, "container" \| "style">>` | not stated | Extra MapLibre `MapOptions` merged last (escape hatch for hash, bearing, maxBounds, etc.). `container` and `style` here are ignored. `attributionControl` is honoured but never allowed to remove or hide the credit: `{ customAttribution }` works as MapLibre documents it, while `attributionControl: false` and `{ compact: true }` are ignored (the credit stays on and always expanded, with no (i) button, and the SDK logs one `console.warn` for that map). The OpenStreetMap credit is a condition of the ODbL the map data is served under, for MapMap and for you. |
| `attributionPosition?` | [`AttributionPosition`](#attributionposition) | `"bottom-left"`, which is the canonical MapMap layout: attribution small bottom-left, MapMap mark bottom-right | Corner for the OpenStreetMap attribution. Default `"bottom-left"`, which is the canonical MapMap layout: attribution small bottom-left, MapMap mark bottom-right. This chooses the credit's corner and nothing else; the credit itself stays on regardless (see `mapOptions.attributionControl` above), so there is no longer any reason to reach into `map._controls` to relocate it. The attribution and the mark never share a corner: stacking the mark on the credit makes a line the ODbL requires unreadable. If the corner requested here is the mark's corner (whether the mark's default bottom-right or an explicit `logo.position`), the mark moves to the opposite bottom corner and the SDK logs one `console.warn` for that map. |

#### `MarkerItem`

*Interface*

```ts
interface MarkerItem
```

One parsed marker of an `extra.markers` v1 block.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | Stable unique id, non-empty, at most 64 characters. |
| `lng` | `number` | required | Longitude, degrees, [-180, 180]. |
| `lat` | `number` | required | Latitude, degrees, [-90, 90]. |
| `icon` | [`MarkerGlyphId`](#markerglyphid) | `"pin"` | Glyph drawn on the pin (or the standalone `dot`). Default `"pin"`. |
| `image?` | `string` | not stated | Optional custom image (`data:` URI, png/jpeg/webp/svg+xml, at most 64 KB decoded). Overrides `icon`; an unusable image falls back to the default pin, never to nothing. |
| `colour` | `string` | `#1a6bff` | Pin colour, `#rrggbb` (normalised lowercase). Default `#1a6bff`. |
| `size` | [`MarkerSize`](#markersize) | `"m"` (32 CSS px) | Size preset. Default `"m"` (32 CSS px). |
| `label?` | `string` | not stated | Optional label drawn below the marker, at most 120 characters. |

#### `MarkersBlock`

*Interface*

```ts
interface MarkersBlock
```

A parsed `extra.markers` block.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `version` | `1` | required | Block schema version; currently always 1. |
| `items` | [`MarkerItem[]`](#markeritem) | required | The valid markers, in input order, capped at 200. |

#### `MarkersLayerIds`

*Interface*

```ts
interface MarkersLayerIds
```

The generated MapLibre ids (see `MarkersLayer.ids`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `string` | required | The GeoJSON source id (`mm-user-markers`). |
| `layer` | `string` | required | The symbol layer id (`mm-user-markers`). |

#### `MarkersLayerOptions`

*Interface*

```ts
interface MarkersLayerOptions
```

Options for [`MarkersLayer`](#markerslayer).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `onImageError?` | `(image: string, error: unknown) => void` | not stated | Called when a marker's custom image passes the schema (right media type, within the 64 KB cap) but MapLibre cannot rasterise it, most often an SVG with no intrinsic `width`/`height`, which `<img>` renders happily and `createImageBitmap` rejects. The marker keeps its glyph pin either way; this is the only way to TELL the user that the logo they can see in their editor will not be on the map. |

#### `NavAlertKindDesign`

*Interface*

```ts
interface NavAlertKindDesign
```

Per-kind surfaces and channels. The three booleans are orthogonal: a
kind can appear on the map without alerting, alert without appearing on
the map, or be listed in the route preview only.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `showOnMap` | `boolean` | required | Draw a pin for this kind while BROWSING the map (no active guidance). Kept as the browse-mode field under its original name so themes saved before the browse/navigate split round-trip unchanged. |
| `showOnMapWhileNavigating` | `boolean` | `true`, and absent in an older theme reads as `true` - the market shows cameras with or without a planned route, so gating map display behind guidance would be more restrictive than every product a driver has used | Draw a pin for this kind on the map DURING active navigation. Separate from `showOnMap` because the two are genuinely different decisions: a fleet product may want a clean driving map and a fully annotated planning map, or the reverse. Defaults to `true`, and absent in an older theme reads as `true` - the market shows cameras with or without a planned route, so gating map display behind guidance would be more restrictive than every product a driver has used. |
| `alertWhileDriving` | `boolean` | required | Show the alert chip for this kind while driving. |
| `showInRoutePreview` | `boolean` | required | List this kind in the pre-drive route preview. |
| `alertMode` | [`NavAlertMode`](#navalertmode) | required | When the AUDIBLE alert fires. The chip itself follows `alertWhileDriving`. |
| `audioMode` | [`NavAlertAudioMode`](#navalertaudiomode) | required | Which audio channel this kind uses. |

#### `NavAlertsDesign`

*Interface*

```ts
interface NavAlertsDesign
```

Safety-alert tokens (`extra.nav.alerts`): the camera alert chip, its
escalated state, per-kind icons and surfaces, and the average-speed
corridor highlight.

The whole block is optional since v1 - themes saved without it load
unchanged and hosts keep their built-in look - but a block that IS
present is always complete: every field, including both escalated
tokens, parses to a value. The escalated state can be restyled, never
removed.

Two states only, calm and escalated. There is no amber tier: no shipping
navigation product uses a three-colour alert state and drivers have no
learned mapping for one.

NOT STYLEABLE HERE: whether cameras may be shown at all. That is a
server-side jurisdiction policy applied before any camera leaves the
gateway. A design styles what the policy permits and nothing more.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `background` | `string` | required | Chip background in the calm (default) state. |
| `textColor` | `string` | required | Chip text and icon colour in the calm state. |
| `outlineColor` | `string` | required | Chip outline colour (both states). |
| `cornerRadius` | `number` | required | Chip corner radius in px, 0-24. |
| `outlineWidth` | `number` | required | Chip outline width in px, 0-4. |
| `escalatedBackground` | `string` | required | Chip background when escalated (driver over the limit). |
| `escalatedTextColor` | `string` | required | Chip text and icon colour when escalated. |
| `iconSet` | [`NavAlertIconSet`](#navalerticonset) | required | Curated icon set for the chip and map pins. |
| `iconUrlOverrides?` | `Partial<Record<NavCameraKind, string>>` | not stated | Per-kind icon overrides (`https:` or `data:` URIs only). Any kind left out keeps `iconSet`'s icon. Absent when no kind is overridden. |
| `alertSound` | [`NavAlertSound`](#navalertsound) | required | The earcon played for a calm alert. One sound for the whole design, never one per kind - see [`NAV_ALERT_SOUNDS`](#nav_alert_sounds). |
| `alertSoundUrl?` | `string` | not stated | Optional replacement for the calm earcon (`https:` or `data:` URIs only, same rule as `iconUrlOverrides`). Absent = the built-in `alertSound`. |
| `escalatedSound` | [`NavAlertSound`](#navalertsound) | required | The earcon played when the driver is over the limit. The escalated state is not removable - it can be restyled and its sound chosen, but a design cannot silence the escalation itself (set the kind's `audioMode` to `off` if you want no audio at all). |
| `escalatedSoundUrl?` | `string` | not stated | Optional replacement for the escalated earcon; same URL rule. |
| `mapIconSize` | `number` | required | Map pin size as a multiplier of the default ramp, 0.75-1.5. |
| `mapMinZoom` | `number` | required | Lowest zoom at which map pins are drawn, 9-14. |
| `leadDistance` | [`NavAlertLeadDistance`](#navalertleaddistance) | required | Lead-distance preset; see [`NAV_ALERT_LEAD_DISTANCE_M`](#nav_alert_lead_distance_m). |
| `chipPosition` | [`NavAlertChipPosition`](#navalertchipposition) | required | Which constrained slot the chip occupies. |
| `corridorColor` | `string` | required | Colour of the average-speed corridor highlight on the route line. |
| `corridorOpacity` | `number` | required | Corridor highlight opacity, 0-1. |
| `kinds` | `Record<NavCameraKind, NavAlertKindDesign>` | required | Per-kind surfaces and channels; always all five kinds. |

#### `NavBannerDesign`

*Interface*

```ts
interface NavBannerDesign
```

Banner-instruction tokens (defaults match [`GuidanceBanner`](#guidancebanner)).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `background` | `string` | required | Banner background colour. |
| `textColor` | `string` | required | Banner text (and arrow) colour. |
| `fontSize` | `number` | required | Primary text size in px. |
| `showLanes` | `boolean` | required | Whether the lane diagram row is shown when lanes are present. |
| `padding?` | `number` | not stated | Inner padding in px, 6-24. Optional since v1; default 10. |
| `cornerRadius?` | `number` | not stated | Corner radius in px, 0-24. Optional since v1; default 10. |
| `maxWidth?` | `number` | not stated | Maximum banner width in px, 200-520. Optional since v1; default 340. |
| `height?` | `number` | not stated | Banner height in px, 40-120. Optional since v1; unset means auto - the banner takes its natural content height (old themes keep theirs). |

#### `NavCameraDesign`

*Interface*

```ts
interface NavCameraDesign
```

Drive-camera tokens: how the chase cam frames a turn-by-turn drive.
The whole block is optional since v1 - themes saved without it load
unchanged and the SDK keeps its built-in framing. Every field is itself
optional (absent = the documented default). `NavigationCamera` reads
`pitch`/`zoom` as its option defaults; `speedMps` drives Studio's demo
drive (and any route simulator) - a camera following real GPS fixes
ignores it.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `pitch?` | `number` | 60 | Chase-cam tilt in degrees, 0-85. Default 60. |
| `zoom?` | `number` | 17.5 | Chase-cam zoom, 14-20. Default 17.5. |
| `speedMps?` | `number` | 12 (~43 km/h) | Drive speed in metres per second, 2-40. Default 12 (~43 km/h). |

#### `NavDesign`

*Interface*

```ts
interface NavDesign
```

The `extra.nav` block: navigation design tokens for the SDKs.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `version` | `1` | required | Block schema version; currently always 1. |
| `route` | [`NavRouteDesign`](#navroutedesign) | required |  |
| `puck` | [`NavPuckDesign`](#navpuckdesign) | required |  |
| `banner` | [`NavBannerDesign`](#navbannerdesign) | required |  |
| `camera?` | [`NavCameraDesign`](#navcameradesign) | not stated | Optional since v1: drive-camera framing (pitch/zoom) and speed. Absent means "SDK built-ins" - the block stays `version: 1` either way. |
| `alerts?` | [`NavAlertsDesign`](#navalertsdesign) | not stated | Optional since v1: safety-alert chip, per-kind surfaces and corridor highlight. Absent means "SDK built-ins"; the block stays `version: 1`. |

#### `NavigationCameraOptions`

*Interface*

```ts
interface NavigationCameraOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `pitch?` | `number` | `60` - MapLibre's non-experimental ceiling | Camera tilt in degrees, clamped to 0-85. Default `60` - MapLibre's non-experimental ceiling; values above 60 may show rendering artefacts and flatten DOM-marker pucks. When the requested pitch exceeds the map's `maxPitch`, the limit is raised to match. Precedence: this option > the Studio design's `camera.pitch` (when the map is a `MapMapMap` whose theme carried `extra.nav.camera`) > the built-in default. |
| `zoom?` | `number` | `17` | Follow-mode zoom. Default `17`. Same precedence as `pitch`: option > Studio design `camera.zoom` > built-in default. |
| `anchorY?` | `number` | `0.72` - the puck rides low so the camera looks up the road ahead | Where the fix (and puck) sits vertically on screen, as a fraction of the container height from the top, clamped to 0-1. Default `0.72` - the puck rides low so the camera looks up the road ahead. |
| `easeMs?` | `number` | `900`, and always capped by the observed fix interval so a fast fix cadence never queues up lagging animations | Ease duration per `NavigationCamera.follow` call, milliseconds. Default `900`, and always capped by the observed fix interval so a fast fix cadence never queues up lagging animations. |
| `autoRecentreMs?` | `number` | `6000` | Idle time after the last user gesture before the camera recentres itself back to follow mode, milliseconds. Default `6000`; `0` disables auto-recentre (the app must call `NavigationCamera.resume`). |

#### `NavPuckDesign`

*Interface*

```ts
interface NavPuckDesign
```

Current-position puck tokens.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `string` | required | Puck fill colour. |
| `size` | `number` | required | Puck diameter in px. |
| `headingArrow` | `boolean` | required | Whether the heading arrow is drawn on the puck. |
| `imageUrl?` | `string` | not stated | Optional custom puck image (`https:` or `data:` URI, PNG/SVG). When set it replaces the built-in dot + arrow; `size` still scales it. Optional since v1 - themes saved without it load unchanged. |

#### `NavRouteDesign`

*Interface*

```ts
interface NavRouteDesign
```

Route-line tokens (defaults match [`RouteLayer`](#routelayer)'s built-in look).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `color` | `string` | MapMap signal blue (`SIGNAL_BLUE`) | Line colour. Default is MapMap signal blue (`SIGNAL_BLUE`). |
| `width` | `number` | required | Line width in px at mid zoom. |
| `opacity` | `number` | required | Line opacity, 0-1. |
| `casingColor` | `string` | required | Casing (outline) colour drawn under the line. |
| `casingWidth?` | `number` | `width + 4` | Casing width in px. Defaults to `width + 4`. Optional because the casing was previously always `width + 4`, which cannot express a design whose casing is not exactly four wider than its line (the playground's is 9 over a 4.5 line). |
| `casingOpacity?` | `number` | `NavRouteDesign.opacity` | Casing opacity, 0-1. Defaults to `NavRouteDesign.opacity`. Optional because casing and line previously shared one opacity, which cannot express a translucent casing under a solid line. |
| `dash?` | `number[] \| null` | not stated | Line dash pattern, in line-widths, as MapLibre's `line-dasharray`. Omitted or `null` draws a solid line. |

#### `NearestByDriveTimeOptions`

*Interface*

```ts
interface NearestByDriveTimeOptions
```

Options for `PlacesLayer.nearestByDriveTime`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `n?` | `number` | `1` | How many places to return. Defaults to `1`. |
| `costing?` | `string` | not stated | Gateway costing model, e.g. `"auto"` (default) or `"truck"` (Premium price class - see `POST /matrix` in the API docs). |
| `costingOptions?` | `Record<string, unknown>` | not stated | Valhalla-style costing options forwarded verbatim, e.g. `{ truck: { height: 4.0 } }`. |

#### `ParsedRoute`

*Interface*

```ts
interface ParsedRoute
```

A parsed OSRM route, normalised to the fields callers actually use.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `distanceM` | `number` | required | Total distance, metres. |
| `durationS` | `number` | required | Total duration, seconds. |
| `geometry` | [`RouteGeometry`](#routegeometry) | required | Route line as GeoJSON, ready to hand to a MapLibre source. |
| `raw` | `Record<string, unknown>` | required | The raw OSRM `routes[0]` object, for callers that need leg/step detail. |

#### `PersistentIds`

*Interface*

```ts
interface PersistentIds
```

The ids `MapMapMap.persistentIds` reports, in registry order.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `sources` | `string[]` | required |  |
| `layers` | `string[]` | required |  |

#### `PersistentLayerOptions`

*Interface*

```ts
interface PersistentLayerOptions
```

Where `MapMapMap.addPersistentLayer` puts a layer.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `slot?` | [`Slot`](#slot) | not stated | The insertion slot (`bottom`, `middle`, `top`), re-resolved on every style load. Layers in the same slot keep the order they were added in, the latest on top. |
| `beforeId?` | `string` | not stated | Insert below this layer instead. Wins over `slot` while the layer exists; when it does not (another style, or it was removed), `slot` applies, and with no slot the layer is appended on top. |

#### `Place`

*Interface*

```ts
interface Place
```

One place to show on the map - a store, depot, branch, POI.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | Stable unique id, e.g. your store number. |
| `name` | `string` | required | Display name. |
| `lat` | `number` | required | Latitude, degrees. |
| `lon` | `number` | required | Longitude, degrees. |
| `properties?` | `Record<string, unknown>` | not stated | Arbitrary extra data (opening hours, phone, …). Every key is copied verbatim onto the top level of the GeoJSON feature's `properties`, alongside the reserved `id`, `name` and `__mapmapIndex` keys (which win on collision) - so scalar values are directly addressable from a data-driven `color` expression, e.g. `["get", "category"]`. Nested objects and arrays behave differently on each MapLibre major, and the peer range admits both. On **MapLibre 5** they are JSON-stringified at render time, so `["get", "details"]` and a `String(...)` in your own code both see JSON text. On **MapLibre 6** they are encoded and parsed back, so the same key is a real object or array: a `["get", …]` bound to it renders nothing where it used to render the JSON text, a `["match", ["get", …], …]` type-errors in the expression evaluator instead of falling through to its default, and `String(...)` gives `"[object Object]"`. So: keep anything you want to style on, or print, as a top-level string/number/boolean, and if you do read a nested value back off a feature, handle both shapes (`typeof v === "string" ? JSON.parse(v) : v`). The whole object also comes back on the place handed to `onPlaceClick` / `popup`, and that path is the same on both majors: it reads the place you passed in, not round-tripped feature properties. |

#### `PlacePointFeature`

*Interface*

```ts
interface PlacePointFeature
```

A GeoJSON Point feature, as accepted in a [`PlacesFeatureCollection`](#placesfeaturecollection).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"Feature"` | required |  |
| `id?` | `string \| number` | not stated |  |
| `geometry` | `{ type: "Point"; coordinates: number[]; }` | required |  |
| `properties?` | `Record<string, unknown> \| null` | not stated |  |

#### `PlacesFeatureCollection`

*Interface*

```ts
interface PlacesFeatureCollection
```

A plain GeoJSON FeatureCollection of Points, accepted anywhere a `Place[]`
is (structurally compatible with `@types/geojson`'s
`FeatureCollection<Point>`). See [`placesFromGeoJSON`](#placesfromgeojson) for how ids and
names are derived.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"FeatureCollection"` | required |  |
| `features` | [`PlacePointFeature[]`](#placepointfeature) | required |  |

#### `PlacesIcon`

*Interface*

```ts
interface PlacesIcon
```

A custom pin image for unclustered places.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required | Image URL (any format `Map#loadImage` accepts, e.g. PNG). |
| `size?` | `number` | `1` | MapLibre `icon-size` scale factor. Defaults to `1`. |

#### `PlacesLabelOptions`

*Interface*

```ts
interface PlacesLabelOptions
```

Options for the per-place name labels (see `PlacesLayerOptions.label`).

The colour keys accept BOTH spellings: `colour`/`haloColour` (British,
as first shipped) and `color`/`haloColor` (matching the sibling
`color`/`clusterColor` options on [`PlacesLayerOptions`](#placeslayeroptions)). A
dynamically built options object should not lose its labels' colours to
a spelling mismatch. British wins if somehow both are given.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `property?` | `string` | `"name"` - every place carries its `name` there | Feature property holding the label text (see `Place.properties` for what is addressable). Defaults to `"name"` - every place carries its `name` there. |
| `size?` | `number` | `12` | Text size in px. Defaults to `12`. |
| `colour?` | `string` | a dark ink (`#333333`) | Text colour. Defaults to a dark ink (`#333333`). Alias: `color`. |
| `color?` | `string` | not stated | Text colour, American spelling of `PlacesLabelOptions.colour`. |
| `haloColour?` | `string` | white | Halo colour behind the text. Defaults to white. Alias: `haloColor`. |
| `haloColor?` | `string` | not stated | Halo colour, American spelling of `PlacesLabelOptions.haloColour`. |

#### `PlacesLayerIds`

*Interface*

```ts
interface PlacesLayerIds
```

The generated MapLibre source/layer ids (see `PlacesLayer.ids`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `string` | required | The GeoJSON source id. |
| `points` | `string` | required | The unclustered-points layer id (circle, or symbol with `icon`). |
| `clusters` | `string` | required | The cluster circles layer id (only installed with `cluster: true`). |
| `clusterCounts` | `string` | required | The cluster count badge layer id (only installed with `cluster: true`). |
| `labels` | `string` | required | The labels symbol layer id (only installed with a `label` option). |
| `pointsFallback` | `string` | required | The circle layer drawn under places without a loaded per-place icon (only installed when `icon` is a record of images). |

#### `PlacesLayerOptions`

*Interface*

```ts
interface PlacesLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `places?` | [`PlacesInput`](#placesinput) | not stated | Initial places. Also settable later via `PlacesLayer.setPlaces`. |
| `cluster?` | `boolean` | `true` | Cluster nearby places into count badges. Defaults to `true`. |
| `clusterRadius?` | `number` | MapLibre's `50` | Cluster radius in pixels. Defaults to MapLibre's `50`. |
| `clusterMaxZoom?` | `number` | MapLibre's `14` | Max zoom to cluster at. Defaults to MapLibre's `14`. |
| `color?` | `string \| ExpressionSpecification` | MapMap signal blue (`#3a86ff`) | Pin colour: a CSS colour string, or a MapLibre expression evaluated against each feature's `properties` for per-category pins, e.g. `["match", ["get", "category"], "food", "#e63946", "#3a86ff"]` (see `Place.properties` for what an expression can `get`). Defaults to MapMap signal blue (`#3a86ff`). Only styles the default circle pins - ignored with a custom `icon`. A cluster mixes categories, so an expression never applies to clusters: they use `clusterColor`. |
| `clusterColor?` | `string` | `color` when that is a plain string, else to MapMap signal blue (a cluster mixes categories, so a data-driven `color` expression cannot apply to it) | Cluster circle colour. Defaults to `color` when that is a plain string, else to MapMap signal blue (a cluster mixes categories, so a data-driven `color` expression cannot apply to it). |
| `icon?` | `PlacesIcon \| Record<string, PlacesIcon>` | `"kind"` | Custom pin image for unclustered places (a symbol layer instead of the default circle). If the image fails to load, the circle look is used. Also accepts a record of images keyed by a feature-property value for per-place icons - e.g. `{ cafe: { url: … }, fuel: { url: … } }` picks an image per place from the property named by `iconProperty` (default `"kind"`). Places whose property value has no entry - or whose entry's image fails to load - keep the default circle look. |
| `iconProperty?` | `string` | `"kind"` | Feature property that selects a per-place icon when `icon` is a record (ignored for a single icon). Defaults to `"kind"`. Set place `properties` values to the record's keys. |
| `label?` | `boolean \| PlacesLabelOptions` | not stated | Per-place name labels beside the points. `true` labels every place with its `name`; an options object customises the property, size and colours (see [`PlacesLabelOptions`](#placeslabeloptions)). Labels are drawn in the map font with a halo, hide before they collide (`text-optional`), and only ever appear on unclustered points. Omit for no labels (today's behaviour). |
| `fitBounds?` | `boolean` | `false` (the map view is left alone) | Fit the map to the places the first time they are set. Defaults to `false` (the map view is left alone). |
| `id?` | `string` | `"mapmap-places"` | Unique id prefix for the source/layers. Defaults to `"mapmap-places"`. |
| `baseUrl?` | `string` | the map's `baseUrl` when given a map | Gateway base URL. Defaults to the map's `baseUrl` when given a map. |
| `apiKey?` | `string` | the map's `apiKey` when given a map | Gateway API key. Defaults to the map's `apiKey` when given a map. |
| `onPlaceClick?` | `(place: Place, lngLat: { lng: number; lat: number; }) => void` | not stated | Called when an individual (unclustered) place is clicked. |
| `popup?` | `(place: Place) => string \| HTMLElement` | not stated | Built-in popup: return HTML (a string is set with `setHTML`, an element with `setDOMContent`) and a `maplibregl.Popup` opens at the place on click. Omit for no popup. |

#### `PlacesSelectOptions`

*Interface*

```ts
interface PlacesSelectOptions
```

Options for `PlacesLayer.select`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `popup?` | `boolean` | `true` | Open the layer's configured `popup` at the place. Defaults to `true`. |
| `flyTo?` | `boolean` | `true` | Ease the camera to the place. Defaults to `true`. |
| `zoom?` | `number` | not stated | Zoom for the camera move (with `flyTo`). Keeps the current zoom if omitted. |

#### `PoiCategoryDesign`

*Interface*

```ts
interface PoiCategoryDesign
```

One category's overrides. Both fields optional: absent = built-in.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `color?` | `string` | not stated | Dot fill colour (replaces the built-in category brand colour). |
| `textColor?` | `string` | not stated | Label text colour for this category (replaces `textSecondary`). |

#### `PoiDesign`

*Interface*

```ts
interface PoiDesign
```

The `extra.poi` block: POI category design.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `version` | `1` | required | Block schema version; currently always 1. |
| `categories` | `Record<string, PoiCategoryDesign>` | required | Overrides by category id; empty = all built-in. |

#### `PositionPuckOptions`

*Interface*

```ts
interface PositionPuckOptions
```

Options for [`PositionPuck`](#positionpuck).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `interpolate?` | `boolean` | `true` | Animate between fixes instead of snapping (default `true`). Position lerps linearly and heading tweens along the shortest arc, over a duration adapted to the observed fix interval (like the `NavigationCamera` glide, so puck and camera arrive together). Falls back to instant placement where `requestAnimationFrame` is missing. |
| `now?` | `() => number` | Test seam: clock override, defaults to `Date.now` | Test seam: clock override, defaults to `Date.now`. |
| `requestFrame?` | `(callback: () => void) => number` | Test seam: frame scheduler override, defaults to rAF | Test seam: frame scheduler override, defaults to rAF. |
| `cancelFrame?` | `(handle: number) => void` | Test seam: frame canceller override, defaults to cancelAnimationFrame | Test seam: frame canceller override, defaults to cancelAnimationFrame. |

#### `ProjectionArgs`

*Interface*

```ts
interface ProjectionArgs
```

The subset of MapLibre's `CustomRenderMethodInput` this helper reads.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `shaderData` | `{ variantName: string; vertexShaderPrelude: string; define: string; }` | required |  |
| `defaultProjectionData` | `{ mainMatrix: ArrayLike<number>; fallbackMatrix: ArrayLike<number>; tileMercatorCoords: [number, number, number, number]; clippingPlane: [number, number, number, number]; projectionTransition: number; }` | required |  |

#### `Rendered`

*Interface*

```ts
interface Rendered
```

Where to draw one vehicle at the requested moment.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |
| `lng` | `number` | required |  |
| `lat` | `number` | required |  |
| `bearing` | `number \| null` | required | Heading in degrees clockwise from north, or `null` when unknown. |
| `moving` | `boolean` | required | True while the vehicle is between two different fixes. |
| `vehicle` | [`TransitVehicle`](#transitvehicle) | required | The newest fix's vehicle record (for labels and popups). |

#### `RenderedMarkerImage`

*Interface*

```ts
interface RenderedMarkerImage
```

What [`renderMarkerImage`](#rendermarkerimage) returns, ready for `map.addImage`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `image` | `ImageData` | required | The rasterised marker, drawn at 2x. |
| `pixelRatio` | `2` | required | Pass to `addImage` so the marker displays at its CSS pixel size. |

#### `RenderMarkerImageOptions`

*Interface*

```ts
interface RenderMarkerImageOptions
```

Options for [`renderMarkerImage`](#rendermarkerimage).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `icon?` | [`MarkerGlyphId`](#markerglyphid) | `"pin"` | Glyph to draw. Default `"pin"`. |
| `colour?` | `string` | the marker blue | Pin (or dot) colour. Default the marker blue. |
| `size?` | [`MarkerSize`](#markersize) | `"m"` (32 CSS px) | Size preset. Default `"m"` (32 CSS px). |
| `text?` | `string` | not stated | Short text (1-3 characters) drawn in white in the pin head INSTEAD of the glyph - numbered waypoints (`"1"`, `"2"`) and lettered stops (`"A"`, `"B"`). Longer strings are cut to [`MAX_MARKER_TEXT_LENGTH`](#max_marker_text_length); blank strings fall back to the glyph. Text always draws the pin body, so `icon: "dot"` is ignored when text is given (a dot has no head to write in). |

#### `ReverseGeocodeOptions`

*Interface*

```ts
interface ReverseGeocodeOptions
```

Options for reverse geocoding (`GET /geocode/reverse`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `limit?` | `number` | not stated | Maximum results. |
| `kinds?` | [`GeocodeKind[]`](#geocodekind) | not stated | Restrict hits to these kinds. `["poi"]` is the "what place did the user tap" lookup. First-party backend only. |
| `categories?` | `string[]` | not stated | Category browse: the nearest places of these categories, nearest first (`["cafe"]`, `["fuel", "charging_station"]`). Implies `kinds=poi`. First-party backend only. |
| `name?` | `string` | not stated | Restrict hits to places of this name or brand, nearest first - the "nearest Lloyds bank" question. First-party backend only. |

#### `RibbonMesh`

*Interface*

```ts
interface RibbonMesh
```

A tessellated route ribbon, ready for a `TRIANGLE_STRIP` draw.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `vertices` | `Float32Array` | required | Interleaved vertex data, [`RIBBON_FLOATS_PER_VERTEX`](#ribbon_floats_per_vertex) floats per vertex: mercator position, the unit extrusion normal (the shader extrudes `pos + normal * halfWidth`, so width stays zoom-correct without re-tessellating), and progress along the route (0 at the start, 1 at the end - the flowing gradient's coordinate). |
| `vertexCount` | `number` | required | Number of vertices (two per route point kept). |

#### `RouteEffectLayer`

*Interface*

```ts
interface RouteEffectLayer extends CustomLayerInterface
```

A route effect layer: a custom layer that can retarget its geometry.

Extends `CustomLayerInterface`.

**Methods**

- `setGeometry(geometry: RouteGeometry): void`

#### `RouteFlowOptions`

*Interface*

```ts
interface RouteFlowOptions
```

Options for the `"flow"` route effect (all optional).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `color?` | `string` | MapMap signal blue | Ribbon colour (hex or rgb()/rgba()). Defaults to MapMap signal blue. |
| `width?` | `number` | `10` | Ribbon width in CSS pixels. Defaults to `10`. |
| `speed?` | `number` | `0.6` | Flow speed in pulse cycles per second. Defaults to `0.6`. |
| `id?` | `string` | `"mapmap-route-effect"` | MapLibre layer id. Defaults to `"mapmap-route-effect"`. |

#### `RouteGeometry`

*Interface*

```ts
interface RouteGeometry
```

A parsed GeoJSON LineString geometry (`[lng, lat]` positions).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `type` | `"LineString"` | required |  |
| `coordinates` | `[number, number][]` | required |  |

#### `RouteLayerIds`

*Interface*

```ts
interface RouteLayerIds
```

The MapLibre ids a [`RouteLayer`](#routelayer) owns (see `RouteLayer.ids`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `string` | required | GeoJSON source holding the route line. |
| `casing` | `string` | required | Line layer drawn under the route (the darker casing). |
| `line` | `string` | required | Line layer for the route itself. |
| `maneuverSource` | `string` | required | GeoJSON source for the manoeuvre arrow. |
| `maneuver` | `string` | required | Symbol layer for the manoeuvre arrow. |
| `corridorSource` | `string` | required | GeoJSON source for the average-speed corridors. |
| `corridor` | `string` | required | Line layer tinting the average-speed corridors. |
| `endpointsSource` | `string` | required | GeoJSON source for the start/end/waypoint markers. |
| `endpoints` | `string` | required | Symbol layer drawing the start/end/waypoint markers. |

#### `RouteLayerOptions`

*Interface*

```ts
interface RouteLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `baseUrl?` | `string` | the map's `baseUrl` when given a map | Gateway base URL. Defaults to the map's `baseUrl` when given a map. |
| `apiKey?` | `string` | the map's `apiKey` when given a map | Gateway API key. Defaults to the map's `apiKey` when given a map. |
| `id?` | `string` | `"mapmap-route"` | Unique id prefix for the source/layers. Defaults to `"mapmap-route"`. |
| `design?` | [`NavRouteDesign`](#navroutedesign) | the map's `navDesign.route` when given a `MapMapMap` whose theme carried one | Studio route design (`extra.nav` `route` block). Defaults to the map's `navDesign.route` when given a `MapMapMap` whose theme carried one; without a design the layer keeps its built-in signal-blue look. |
| `alertsDesign?` | [`NavAlertsDesign`](#navalertsdesign) | the map's `navDesign.alerts` when given a `MapMapMap` whose theme carried one | Studio safety-alert design (`extra.nav` `alerts` block), used for the average-speed corridor highlight drawn by `RouteLayer.setAlertCorridors`. Defaults to the map's `navDesign.alerts` when given a `MapMapMap` whose theme carried one. |
| `progressColor?` | `string` | a dimmed grey | Colour of the already-travelled part of the line once `RouteLayer.setProgress` is used (the Google-style "vanishing route line"). Defaults to a dimmed grey. |
| `alternativeCasingColor?` | `string` | not stated | Casing colour for unselected alternative routes. |
| `alternativeColor?` | `string` | not stated | Line colour for unselected alternative routes. |
| `ferryColor?` | `string` | not stated | Colour of the ferry dashes drawn over the selected route. |
| `endpoints?` | `boolean \| EndpointsDesign` | Off by default: a layer built without this option behaves exactly as it always has | Branded start/end/waypoint markers on the drawn route. Off by default: a layer built without this option behaves exactly as it always has. `true` uses the brand defaults (green `dot` at the start, blue `pin` at the end, smaller numbered blue pins at each intermediate stop); an [`EndpointsDesign`](#endpointsdesign) overrides any of them, and `false` on one endpoint drops just that one. The coordinates come from the route itself: first coordinate = start, last = end. Intermediate stops are remembered from `RouteLayer.routePath`'s via points, or set explicitly with `RouteLayer.setWaypoints` when drawing a route parsed elsewhere. These are symbol-layer markers, not DOM `maplibregl.Marker`s, so they appear in canvas exports and match the native SDKs. |

#### `RouteLineStyle`

*Interface*

```ts
interface RouteLineStyle
```

Runtime overrides for the drawn line, applied over the design or the
built-in look. Every field is optional; omitted ones are left alone.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `color?` | `string` | not stated | Line colour. |
| `dash?` | `number[] \| null` | not stated | Dash pattern in line-widths, or `null` for solid. |
| `casingOpacity?` | `number` | not stated | Casing opacity, 0-1. Set 0 to hide the casing entirely. |

#### `RouteOptions`

*Interface*

```ts
interface RouteOptions
```

Options for a single route request.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `profile?` | [`RouteProfile`](#routeprofile) | `"driving"` | Routing profile. Defaults to `"driving"`. |
| `truck?` | [`TruckParams`](#truckparams) | not stated | Truck / ADR parameters (only meaningful with `profile: "truck"`). |
| `voice?` | `boolean` | not stated | Request spoken `voiceInstructions` on each step (see `guidance.ts`). |
| `banner?` | `boolean` | not stated | Request visual `bannerInstructions` (and lane data) on each step. |
| `language?` | `string` | not stated | BCP 47 narration language, e.g. `"en-GB"`. |

#### `SearchBoxMap`

*Interface*

```ts
interface SearchBoxMap
```

The slice of a MapLibre map this control uses. Declared structurally so
the module never imports maplibre-gl, a `maplibregl.Map` and a
`MapMapMap`'s `.map` both satisfy it, and so does a test fake.

**Methods**

- `getCenter(): { lng: number; lat: number; }`
- `flyTo(options: { center: [number, number]; zoom?: number; }): unknown`
- `getSource(id: string): unknown`
- `addSource(id: string, source: unknown): unknown`
- `getLayer(id: string): unknown`
- `addLayer(layer: unknown): unknown`
- `removeLayer(id: string): unknown`
- `removeSource(id: string): unknown`
- `isStyleLoaded(): boolean`
- `on(type: string, listener: () => void): unknown`
- `off(type: string, listener: () => void): unknown`

#### `SearchBoxOptions`

*Interface*

```ts
interface SearchBoxOptions
```

Options for [`SearchBox`](#searchbox).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `apiKey?` | `string` | not stated | Gateway API key (`snk_…`). Omit on a deployment serving the keyless demo lane. |
| `gatewayUrl?` | `string` | `https://api.mapmap.ai` | Gateway base URL. Default `https://api.mapmap.ai`. |
| `placeholder?` | `string` | `"Search places"` | Placeholder text for the input. Default `"Search places"`. |
| `limit?` | `number` | 5 | Maximum suggestions per request, 1–10. Default 5. |
| `debounceMs?` | `number` | [`DEFAULT_DEBOUNCE_MS`](#default_debounce_ms) | Milliseconds of quiet before a keystroke becomes a request. Default [`DEFAULT_DEBOUNCE_MS`](#default_debounce_ms). This is a billing control as well as a UX one: every request that reaches the gateway is a metered call. |
| `bias?` | [`SearchBoxBias`](#searchboxbias) | not stated | Location bias. `"map-centre"` (the default) reads the map's centre on each request, so results follow the view; an explicit `{ lat, lon }` pins it; `false` sends no bias at all. |
| `flyTo?` | `boolean` | `true` | Fly the map to the picked result. Default `true`. |
| `marker?` | `boolean` | `true` | Drop a marker on the picked result. Default `true`. |

#### `SearchBoxSelectEvent`

*Interface*

```ts
interface SearchBoxSelectEvent
```

What [`SearchBox`](#searchbox) emits when the user picks a result.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `suggestion` | [`SearchSuggestion`](#searchsuggestion) | required | The row the user picked, exactly as the suggest endpoint sent it. |
| `hit?` | [`GeocodeHit`](#geocodehit) | not stated | The full feature from `GET /geocode/retrieve`, or `undefined` when the retrieve failed. The suggestion's own coordinates are always present, so a failed retrieve still leaves the caller with a usable point. |
| `query` | `string` | required | The query text that produced the suggestion. |

#### `SearchSuggestion`

*Interface*

```ts
interface SearchSuggestion
```

One row of the suggestion dropdown, as `GET /geocode/suggest` sends it.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | Stable document id, what `GET /geocode/retrieve` takes. |
| `name` | `string` | required | Primary line: the place's name. |
| `context` | `string` | required | Secondary line: street, city, region, postcode, country. |
| `kind` | `string` | required | Document kind: `poi`, `address`, `street`, `postcode`, `city`, … |
| `lat` | `number` | required | Latitude (WGS84). |
| `lon` | `number` | required | Longitude (WGS84). |

#### `SetRouteEffectOptions`

*Interface*

```ts
interface SetRouteEffectOptions extends RouteFlowOptions
```

Options for `MapMapMap.setRouteEffect`.

Extends [`RouteFlowOptions`](#routeflowoptions).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `geometry?` | [`RouteGeometry`](#routegeometry) | not stated | The route line to attach the effect to. Optional: when omitted the effect is ARMED and applies as soon as a `RouteLayer` draws a route (RouteLayer feeds every drawn geometry to the map automatically). |

#### `SeverityProsody`

*Interface*

```ts
interface SeverityProsody
```

Speech parameters for one severity level.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `rate` | `number` | required | Utterance rate. |
| `pitch` | `number` | required | Utterance pitch (0–2, 1 = neutral). |
| `interrupt` | `boolean` | required | Whether pending speech is cancelled before this prompt. |

#### `ShowReachabilityOptions`

*Interface*

```ts
interface ShowReachabilityOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `origin` | [`LngLatLike`](#lnglatlike) | required | Where the rings radiate from. |
| `mode?` | [`ReachabilityMode`](#reachabilitymode) | `"walk"` - these are walkability rings | Travel mode. Defaults to `"walk"` - these are walkability rings. |
| `minutes` | `number[]` | required | Ring contours in minutes, e.g. `[5, 10, 15]`. At least one. |
| `color?` | `string` | MapMap signal blue | Ring colour. Defaults to MapMap signal blue. |
| `costingOptions?` | `Record<string, unknown>` | not stated | Valhalla-style costing options forwarded verbatim, e.g. `{ pedestrian: { use_lit: 1.0 } }`. |

#### `SnappedOrigin`

*Interface*

```ts
interface SnappedOrigin
```

Where the gateway moved the origin to: the nearest usable road.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | `number` | required |  |
| `lon` | `number` | required |  |
| `distance_m` | `number` | required | Great-circle distance from the requested origin, in metres. |

#### `SpeakOptions`

*Interface*

```ts
interface SpeakOptions
```

Options for [`speak`](#speak).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lang?` | `string` | not stated | BCP 47 language for the utterance, e.g. `en-GB`. |
| `rate?` | `number` | not stated | Speech rate (0.1–10, default 1). |

#### `StepGuidance`

*Interface*

```ts
interface StepGuidance
```

Guidance data for one step of the route.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `stepIndex` | `number` | required | Step index within the flattened route. |
| `distanceM` | `number` | required | Step length in metres. |
| `instruction?` | `string` | not stated | Written instruction for the step's upcoming maneuver. Taken from `maneuver.instruction` when a server emits it (Mapbox-style); the MapMap gateway does not, so it falls back to the step's final voice announcement (the pre-transition instruction) and then to the banner primary text. Undefined only when the step carries no guidance at all. |
| `voice` | [`VoiceInstruction[]`](#voiceinstruction) | required | Spoken announcements, ordered by descending trigger distance. |
| `banners` | [`BannerInstruction[]`](#bannerinstruction) | required | Visual banners for the upcoming maneuver. |
| `mode?` | `string` | not stated | OSRM travel mode of the step (`driving`, `ferry`, …) where the server emits it. Pass the guidance entry as `GuidanceBanner.update`'s second argument so ferry legs render the ferry icon. |
| `drivingSide?` | `string` | not stated | `left`/`right`: the side traffic drives on at this step's own manoeuvre, where the server emits it. The MapMap gateway sets it on roundabout enter/exit steps and u-turn steps (read from the engine, never assumed); OSRM and Mapbox routers set it on every step. |
| `bannerIcon?` | [`DirectionIconName`](#directioniconname) | not stated | The direction glyph for the manoeuvre this step's `banners` describe. A banner on step N describes the manoeuvre that STARTS step N+1 (Mapbox convention), so this is resolved from that next step, not this one: its own `driving_side`, and for a roundabout the enter/exit pair's through angle (see `iconNamesForSteps`). [`GuidanceBanner`](#guidancebanner) draws it when you pass this entry as `update`'s second argument. Undefined on a leg's last step, which has no banner. |

#### `SuggestUrlOptions`

*Interface*

```ts
interface SuggestUrlOptions
```

Query parameters [`buildSuggestUrl`](#buildsuggesturl) understands.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `limit?` | `number` | not stated | Maximum suggestions, 1–10. |
| `bias?` | `[number, number]` | not stated | Bias point as `[lon, lat]`. |
| `zoom?` | `number` | not stated | Map zoom, scaling the bias. |

#### `TerrainOptions`

*Interface*

```ts
interface TerrainOptions
```

Terrain configuration. `true` means "on, with sensible defaults".

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `exaggeration?` | `number` | not stated | Vertical exaggeration. 1 is true-to-life; 1.2–1.5 reads better on gentle terrain. Clamped to 0–8. |
| `url?` | `string` | the Mapzen terrarium set on AWS Open Data, the same dataset the MapMap gateway reports as `mapzen-terrain-tiles` from `POST /elevation`, so a map and an elevation query agree with each other | DEM tile template. Defaults to the Mapzen terrarium set on AWS Open Data, the same dataset the MapMap gateway reports as `mapzen-terrain-tiles` from `POST /elevation`, so a map and an elevation query agree with each other. Self-hosters: point this at your own terrarium tiles. |
| `encoding?` | `"terrarium" \| "mapbox"` | `"terrarium"` | Encoding of the DEM tiles. Defaults to `"terrarium"`. |
| `maxzoom?` | `number` | 15 | Max zoom of the DEM tile set. Defaults to 15. |
| `tileSize?` | `number` | 256 | Tile size in px. Defaults to 256. |
| `attribution?` | `string` | the Mapzen/AWS credit | Attribution for the DEM. Defaults to the Mapzen/AWS credit. |
| `hillshade?` | `boolean` | not stated | Add a `hillshade` layer as well, for relief shading that reads even with the camera pointing straight down. Uses its own source (see 3). |

#### `Theme`

*Interface*

```ts
interface Theme
```

A MapMap Studio theme: the unit users and agents edit, store and publish.
Mirrors `sn_style::Theme` (all fields carry serde defaults, so everything
is optional here).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `name?` | `string` | not stated | Human-readable theme name, e.g. `"midnight-fleet"`. |
| `base?` | [`MapMapTheme`](#mapmaptheme) | `"light"` | Built-in palette to start from. Defaults to `"light"`. |
| `palette?` | `Record<string, string>` | not stated | Palette overrides by slot name (see [`PALETTE_SLOTS`](#palette_slots)). |
| `layers?` | `Record<string, LayerOverride>` | not stated | Per-layer overrides by skeleton layer id. |
| `buildings_3d?` | `boolean` | Web-safe; native navigation SDKs should leave this off by default until MapLibre Native's fill-extrusion memory use at street zooms is fixed (maplibre-native#4107) | Render buildings as 3D extrusions. Adds a `building-3d` fill-extrusion layer (zoom 15+) driven by the tiles' `render_height` / `render_min_height` attributes, and caps the flat `building` footprint layer at that zoom so the two hand off cleanly (the extrusion rises from flat as you zoom in). Web-safe; native navigation SDKs should leave this off by default until MapLibre Native's fill-extrusion memory use at street zooms is fixed (maplibre-native#4107). Mirrors `Theme::buildings_3d`. |
| `fonts?` | `{ regular?: string; }` | Every fontstack must exist on the glyph server; until that ships (P2), leave this at its default (`"MapMap Sans Regular"`) | Label fonts. Every fontstack must exist on the glyph server; until that ships (P2), leave this at its default (`"MapMap Sans Regular"`). |
| `glyphs?` | `string` | [`DEFAULT_GLYPHS_URL`](#default_glyphs_url) | Override the glyph endpoint (defaults to [`DEFAULT_GLYPHS_URL`](#default_glyphs_url)). |
| `sprite?` | `string` | not stated | Optional sprite URL. Absent, the style uses `DEFAULT_SPRITE_URL` (which carries the road-shield blanks); a sheet that is not a MapMap `/sprite/sprite` drops the road-shield layer. |
| `sources?` | `Record<string, ThemeSource>` | not stated | Customer-hosted sources by id: GeoJSON, vector, raster or image, each by `https://` or `pmtiles://https://` URL (MapMap stores the URL, never the data). At most [`MAX_THEME_SOURCES`](#max_theme_sources). Compiled into the style's `sources` beside `territory`. Mirrors `Theme::sources`. |
| `extra_layers?` | `JsonObject[]` | not stated | Extra fully-formed MapLibre layers. Each draws from the `territory` source (with a known source-layer) or from one of `Theme.sources`, and may name a `slot` (`"bottom"`, `"middle"` or `"top"`, see `slots.ts`). Without a slot a layer goes in `middle`: above the base map and 3D buildings, below labels. |
| `effects?` | [`ThemeEffects`](#themeeffects) | not stated | Optional visual effects (route ribbon etc.). Compiled into `metadata["mapmap:effects"]` on the style, see [`ThemeEffects`](#themeeffects). Mirrors `Theme::effects`. |
| `extra?` | `{ nav?: unknown; poi?: unknown; }` | not stated | Non-style extras carried by the THEME DOCUMENT, not the compiled style. Studio stores its navigation design block here (`extra.nav`; see `nav-design.ts`) and its POI category design (`extra.poi`; see `poi-design.ts`). [`buildStyle`](#buildstyle) ignores it entirely - the compiled `style.json` is byte-identical with or without it. Mirrors `Theme::extra` in the canonical Rust crate: the hosted styles API stores the block verbatim (bounded at 256 KB serialised) and serves it back from `GET /styles/{id}/theme`, so a Studio design survives publishing - consume it via `createMap` with a theme document or `navDesignFromThemeUrl` / `poiDesignFromThemeUrl` with the hosted theme URL. |
| `color_theme?` | [`ColourTheme`](#colourtheme) | not stated | A colour theme: a 16x16x16 LUT (base64 of 12288 bytes) that grades every colour of the compiled style, the compile-time twin of `map.setColorTheme` (Mapbox root `color-theme`). Absent, the style compiles byte-identically. Mirrors `Theme::color_theme`. |

#### `ThemeEffects`

*Interface*

```ts
interface ThemeEffects
```

The optional visual-effects block, mirroring `sn_style::Effects`. Unlike
`extra`, this IS carried into the compiled style, as
`metadata["mapmap:effects"]`, so any consumer of a compiled `style.json`
(hosted style URL, baked territory package) can honour it without the
theme document. Renderers that do not know the effects engine ignore the
metadata and render the style unchanged.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `route?` | `"flow" \| "none"` | not stated | Route-line effect: `"flow"` (animated energy ribbon) or `"none"`. |
| `params?` | `Record<string, unknown>` | not stated | Effect parameters, validated per effect. For `flow`: `color` (CSS colour), `width` (px, 0.5-40), `speed` (cycles/s, 0-10). |

#### `ThemeSchedulerOptions`

*Interface*

```ts
interface ThemeSchedulerOptions
```

Options for [`ThemeScheduler`](#themescheduler).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lat` | `number` | required | Latitude used for the solar calculation. |
| `lng` | `number` | required | Longitude used for the solar calculation. |
| `onLight` | `() => void` | required | Called (immediately on construction, then at each sunrise) for light. |
| `onDark` | `() => void` | required | Called (immediately on construction, then at each sunset) for dark. |
| `now?` | `() => number` | Test seam: clock override, defaults to `Date.now` | Test seam: clock override, defaults to `Date.now`. |
| `setTimeoutFn?` | `(callback: () => void, ms: number) => ReturnType<typeof setTimeout>` | Test seam: timer override, defaults to `setTimeout` | Test seam: timer override, defaults to `setTimeout`. |
| `clearTimeoutFn?` | `(handle: ReturnType<typeof setTimeout>) => void` | Test seam: timer canceller, defaults to `clearTimeout` | Test seam: timer canceller, defaults to `clearTimeout`. |

#### `TransitFeedAnswer`

*Interface*

```ts
interface TransitFeedAnswer
```

The counts of the last successful answer (0.22.1).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `count` | `number` | required | Vehicles in the answer (the gateway's `count`). |
| `matched` | `number \| null` | required | Vehicles the gateway matched before its limit (`matched`), null when the answer omits it. |
| `truncated` | `boolean` | required | True when the answer was cut at the limit (`truncated`). |
| `receivedAt` | `number` | required | When the answer arrived, epoch milliseconds. |

#### `TransitFeedReport`

*Interface*

```ts
interface TransitFeedReport
```

What `onFeedStatus` reports after every poll (0.22.1: `answer` and `error` added).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | [`TransitFeedStatus`](#transitfeedstatus) | required | The gateway's freshness state, unchanged. |
| `ageS` | `number \| null` | required | Seconds since the feed's newest data, or null when unknown. |
| `label` | `string` | required | The standard freshness label ([`transitFreshnessLabel`](#transitfreshnesslabel)). |
| `answer` | [`TransitFeedAnswer \| null`](#transitfeedanswer) | required | The last successful answer's counts; null before the first answer. |
| `error` | `boolean` | required | True when the LAST poll failed (HTTP error or network); the drawn vehicles are the previous answer's and the label ages honestly. |

#### `TransitFix`

*Interface*

```ts
interface TransitFix
```

One reported position, ready to interpolate.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |
| `lng` | `number` | required |  |
| `lat` | `number` | required |  |
| `bearing` | `number \| null` | required | The feed's heading, or `null`. |
| `recordedAtMs` | `number` | required | `recorded_at` in epoch milliseconds. |
| `vehicle` | [`TransitVehicle`](#transitvehicle) | required | The vehicle this fix belongs to, as the gateway described it. |

#### `TransitStopFeature`

*Interface*

```ts
interface TransitStopFeature
```

One clicked stop, as the tiles carry it.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | NaPTAN ATCO code: pass it to `GET /v1/transit/departures?stop=`. |
| `name` | `string` | required |  |
| `indicator?` | `string` | not stated |  |
| `locality?` | `string` | not stated |  |
| `bearing?` | `string` | not stated | NaPTAN compass point the stop faces, e.g. `NE`. |
| `lines` | `string[]` | required | Line names the timetable has calling here. |

#### `TransitStopsLayerOptions`

*Interface*

```ts
interface TransitStopsLayerOptions
```

Options for [`TransitStopsLayer`](#transitstopslayer).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `stops?` | `boolean` | true | Draw the stops. Default true. |
| `lines?` | `boolean` | true | Draw the lines. Default true. |
| `slot?` | [`Slot`](#slot) | `"middle"` (above roads, below labels) | Where in the style the layers sit. Default `"middle"` (above roads, below labels). |
| `onStopClick?` | `(stop: TransitStopFeature, lngLat: { lng: number; lat: number; }) => void` | not stated | Called with the stop under a click. |
| `source?` | `"auto" \| "api" \| "tiles-host"` | not stated | Where the tiles come from. `"auto"` (default): the keyed tile API when the map has an `apiKey` (the tiles host refuses other sites' browser origins), else the tiles host. `"api"` or `"tiles-host"` force one. |
| `tilesIndexUrl?` | `string` | [`TRANSIT_TILES_INDEX_URL`](#transit_tiles_index_url) | The tiles index to read. Default [`TRANSIT_TILES_INDEX_URL`](#transit_tiles_index_url). |
| `stopsUrl?` | `string` | not stated | Archive URLs to use instead of the index (a self-hosted copy): `https://...pmtiles` or `pmtiles://https://...`. |
| `linesUrl?` | `string` | not stated |  |
| `lineColour?` | `string` | not stated | Line colour where the timetable gives none. |
| `stopColour?` | `string` | the MapMap marker blue | Stop badge colour. Default the MapMap marker blue. |
| `id?` | `string` | `"mapmap-transit"` | Id prefix for the sources and layers. Default `"mapmap-transit"`. |

#### `TransitVehicle`

*Interface*

```ts
interface TransitVehicle
```

A vehicle as the gateway describes it (one feature's properties).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | Stable per vehicle: `<operator>:<vehicle>`. |
| `vehicle` | `string` | required | The operator's vehicle reference. |
| `operator` | `string` | required | National Operator Code, e.g. `TFLO`. |
| `line` | `string \| null` | required | Rider-facing line name, e.g. `88`. |
| `direction` | `"outbound" \| "inbound" \| null` | required | `outbound`, `inbound` or `null`. |
| `origin` | `string \| null` | required |  |
| `destination` | `string \| null` | required |  |
| `bearing` | `number \| null` | required | The feed's heading in degrees, or `null`. |
| `recordedAt` | `string \| null` | required | When the vehicle reported this position (RFC 3339), or `null`. |
| `ageS` | `number \| null` | required | Age of `recordedAt` when the gateway answered, seconds. |
| `journey` | `string \| null` | required | The feed's journey reference, or `null`. |
| `vehicleType?` | [`BusVehicleType \| null`](#busvehicletype-1) | not stated | The body type the feed names (`vehicle_type`, or `deck`), or `null`/absent when it names none. |
| `lng` | `number` | required | Reported position. |
| `lat` | `number` | required |  |

#### `TransitVehiclesLayerOptions`

*Interface*

```ts
interface TransitVehiclesLayerOptions
```

Options for [`TransitVehiclesLayer`](#transitvehicleslayer).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `bbox?` | `[number, number, number, number] \| "viewport"` | not stated | The area to poll: a fixed `[west, south, east, north]`, or `"viewport"` (default) for the area on screen, refetched when the map stops moving. Areas over the gateway's 4 square degree cap are shrunk around their centre. |
| `operator?` | `string` | not stated | Only this operator's vehicles (National Operator Code, e.g. `TFLO`). |
| `line?` | `string` | not stated | Only this line, as riders see it (e.g. `88`). |
| `pollMs?` | `number` | 30,000 | Poll interval in milliseconds. Default 30,000; at least 15,000. |
| `renderBehindMs?` | `number` | `pollMs`: one poll behind, so there is always a newer fix to move towards | How far behind the data to draw, milliseconds. Default `pollMs`: one poll behind, so there is always a newer fix to move towards. |
| `icon?` | `{ url: string; size?: number; } \| "arrow"` | not stated | Vehicle marker: `"arrow"` (default, a heading arrow, a dot when the heading is unknown) or your own image, rotated to the heading. |
| `marker?` | `"arrow" \| "bus"` | not stated | How a vehicle is drawn. `"arrow"` (default): the signal-blue heading arrow, as before. `"bus"`: neutral ink (#0f1115, 1.5 px white halo), a 7 px dot z8 to 12, an 18 px puck with the line number and a heading tick z12 to 15.5, and a top-down bus rotated to the heading from z15.5 (32 px, four times that by z20) with an upright line label. A vehicle whose position is over 90 s behind the feed's newest draws at 50 %. A custom `icon` image still wins when set. |
| `defaultVehicleType?` | [`BusVehicleType`](#busvehicletype-1) | `"generic"`: a deliberately neutral bus, so an unknown vehicle is never drawn as a type it may not be | What a bus is drawn as (the `marker: "bus"` sprite, and the 3D layer) when the feed names no body type (`vehicle_type`, or `deck`). Default `"generic"`: a deliberately neutral bus, so an unknown vehicle is never drawn as a type it may not be. |
| `slot?` | [`TransitSlot`](#transitslot) | `"top"` (above POI labels, below place names) | Where in the style the layer sits. Default `"top"` (above POI labels, below place names). |
| `popup?` | `(vehicle: TransitVehicle) => string \| HTMLElement` | not stated | A popup for a clicked vehicle: HTML string or element. Omit for none. |
| `onFeedStatus?` | `(report: TransitFeedReport) => void` | not stated | Called after every poll with the feed's freshness, its label, the last answer's counts and whether this poll failed. |
| `id?` | `string` | `"mapmap-transit-vehicles"` | Id prefix for the source and layers. Default `"mapmap-transit-vehicles"`. |
| `limit?` | `number` | the gateway's default (2,000) | Most vehicles per poll (gateway `limit`, 1 to 5,000). Default: the gateway's default (2,000). |

#### `TruckParams`

*Interface*

```ts
interface TruckParams
```

Truck / ADR vehicle parameters forwarded to the gateway's OSRM truck
vendor extensions. All optional; only `truck`-profile requests honour them.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `heightM?` | `number` | not stated | Vehicle height, metres. |
| `widthM?` | `number` | not stated | Vehicle width, metres. |
| `lengthM?` | `number` | not stated | Vehicle length, metres. |
| `weightT?` | `number` | not stated | Gross weight, tonnes. |
| `hazmat?` | `boolean` | not stated | Carrying dangerous goods (ADR). |
| `tunnelCode?` | `string` | not stated | ADR 8.6.4 tunnel restriction code, e.g. `"C"` or `"B/D"`. The slash is URL-encoded automatically. |

#### `UploadProbeOptions`

*Interface*

```ts
interface UploadProbeOptions
```

Options for [`uploadProbeBatch`](#uploadprobebatch). All optional.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `fetch?` | `typeof fetch` | the global | Injectable fetch (defaults to the global). |
| `maxAttempts?` | `number` | 4 | Total attempts including the first (default 4). |
| `backoffMs?` | `(attempt: number) => number` | `attempt*1000` | Attempt number → delay before the next try, ms (default `attempt*1000`). |
| `sleep?` | `(ms: number) => Promise<void>` | `setTimeout` | Injectable delay, for deterministic tests (default `setTimeout`). |
| `signal?` | `AbortSignal` | not stated | Abort signal forwarded to fetch. Once it has aborted the retry loop stops immediately (an abort is a caller decision, not a transient failure) and the upload resolves `"gaveUp"`. |

#### `VoiceGuidanceOptions`

*Interface*

```ts
interface VoiceGuidanceOptions
```

Options for [`VoiceGuidance`](#voiceguidance).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lang?` | `string` | not stated | BCP 47 language for utterances, e.g. `en-GB`. An update's `voiceLocale` takes precedence when present. |
| `rate?` | `number` | not stated | Base speech rate (0.1–10, default 1). Critical prompts speak slightly faster than this base. |
| `volume?` | `number` | 1 | Volume for speech and earcons, 0–1 (default 1). |
| `muted?` | `boolean` | not stated | Start muted. |
| `earcons?` | `Partial<Record<GuidanceSeverity, string>>` | not stated | Severity → earcon audio URL. Absent entries play nothing. |

#### `VoiceGuidanceUpdate`

*Interface*

```ts
interface VoiceGuidanceUpdate
```

The subset of a core guidance update that [`VoiceGuidance`](#voiceguidance) reads.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `state` | `"navigating" \| "arrived" \| "offRoute"` | required | Guidance state, as emitted by the wasm `GuidanceSession`. |
| `spoken?` | [`GuidanceSpokenPrompt \| null`](#guidancespokenprompt) | not stated | Spoken prompt currently due (repeated until superseded). |
| `severity?` | [`GuidanceSeverity`](#guidanceseverity) | `info` | Urgency of the current step's instruction (default `info`). |
| `voiceLocale?` | `string` | not stated | BCP 47 locale of the instruction text, when the payload carries one. |

#### `VoiceInstruction`

*Interface*

```ts
interface VoiceInstruction
```

A spoken instruction with its trigger distance before the maneuver.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `distanceAlongGeometry` | `number` | required | Metres before the end of the step at which to announce. |
| `announcement` | `string` | required | Plain text for speech synthesis. |
| `ssmlAnnouncement?` | `string` | not stated | SSML form, preferred by engines that support it. |

### Types (@mapmap/maps)

[`AdrTunnelCategory`](#adrtunnelcategory), [`AttributionPosition`](#attributionposition), [`BusVehicleType`](#busvehicletype-1), [`ClipType`](#cliptype), [`CreditTier`](#credittier-1), [`DataLayerMode`](#datalayermode), [`DiagnosticIssue`](#diagnosticissue), [`DirectionIconName`](#directioniconname), [`FeaturesetId`](#featuresetid), [`FixSet`](#fixset), [`GeocodeKind`](#geocodekind), [`GuidanceSeverity`](#guidanceseverity), [`InteractionType`](#interactiontype), [`LngLatLike`](#lnglatlike), [`LogoPosition`](#logoposition), [`LutBytes`](#lutbytes), [`MapBrandTheme`](#mapbrandtheme), [`MapDisplayMode`](#mapdisplaymode), [`MapMapBaseGroup`](#mapmapbasegroup), [`MapMapBaseId`](#mapmapbaseid), [`MapMapTheme`](#mapmaptheme), [`MarkerGlyphId`](#markerglyphid), [`MarkerSize`](#markersize), [`NavAlertAudioMode`](#navalertaudiomode), [`NavAlertChipPosition`](#navalertchipposition), [`NavAlertIconSet`](#navalerticonset), [`NavAlertLeadDistance`](#navalertleaddistance), [`NavAlertMode`](#navalertmode), [`NavAlertSound`](#navalertsound), [`NavCameraKind`](#navcamerakind-1), [`NavigationCameraMode`](#navigationcameramode), [`PlacesInput`](#placesinput), [`PlaceWithDistance`](#placewithdistance), [`PlaceWithDriveTime`](#placewithdrivetime), [`ProbeUploadOutcome`](#probeuploadoutcome), [`ProjectionUniform`](#projectionuniform), [`ProjectionVariant`](#projectionvariant-1), [`ProtocolHost`](#protocolhost), [`ReachabilityMode`](#reachabilitymode), [`RouteEffectName`](#routeeffectname), [`RouteProfile`](#routeprofile), [`SearchBoxBias`](#searchboxbias), [`SearchBoxSelectListener`](#searchboxselectlistener), [`Slot`](#slot), [`SunTimes`](#suntimes-1), [`ThemeSource`](#themesource), [`TransitDrawnVehicle`](#transitdrawnvehicle), [`TransitFeedStatus`](#transitfeedstatus), [`TransitSlot`](#transitslot), [`UseTheme`](#usetheme), [`Vec3`](#vec3)

#### `AdrTunnelCategory`

*Type*

```ts
type AdrTunnelCategory = "A" | "B" | "C" | "D" | "E";
```

The ADR category of a tunnel, `"A"` (least restrictive) … `"E"`.

#### `AttributionPosition`

*Type*

```ts
type AttributionPosition = "bottom-left" | "bottom-right" | "top-left" | "top-right";
```

Map corners an enforced control (attribution, mark) can be mounted in.

#### `BusVehicleType`

*Type*

```ts
type BusVehicleType = "generic" | "double" | "single" | "mini";
```

A bus body type: `"generic"` (a deliberately neutral bus of single-deck
proportions, for a vehicle whose body type is not known), `"double"`
(double-decker), `"single"` (single-decker), `"mini"` (midi or minibus).

#### `ClipType`

*Type*

```ts
type ClipType = keyof typeof CLIP_TARGETS;
```

#### `CreditTier`

*Type*

```ts
type CreditTier = "full" | "short" | "osm-and-more";
```

Which line a credit shows (0.22.1). `"full"`: the whole wording, fitted
to one line at 5px or more. `"short"`: each source's own short credit
plus the (i), fitted the same way and never under 6px. `"osm-and-more"`:
when even the short line would need less than 5px, the OpenStreetMap
credit verbatim followed by the other sources' names, as many as fit at
5px or more, then " and <n> more" for the rest, plus the (i). No line is
ever set under `infoBelow`; every source's whole wording is in the panel.

#### `DataLayerMode`

*Type*

```ts
type DataLayerMode = "auto" | "point" | "line" | "polygon" | "cluster" | "heatmap" | "extrusion";
```

How a DataLayer draws its features.

#### `DiagnosticIssue`

*Type*

```ts
type DiagnosticIssue = "container-zero-height" | "container-detached" | "duplicate-maplibre" | "webgl-unavailable" | "invalid-api-key" | "basemap-not-found" | "basemap-forbidden";
```

The distinct issues the diagnostics can report.

#### `DirectionIconName`

*Type* · also exported from `@mapmap/maps/direction-icons`

```ts
type DirectionIconName = keyof typeof directionIcons;
```

A valid icon name (`turn_left`, `roundabout_right`, `ferry`, ...).

#### `FeaturesetId`

*Type*

```ts
type FeaturesetId = "poi" | "place-labels" | "buildings";
```

A named group of basemap layers, as in Mapbox Standard featuresets.

#### `FixSet`

*Type*

```ts
type FixSet = ReadonlyMap<string, TransitFix>;
```

One poll's fixes, by vehicle id.

#### `GeocodeKind`

*Type*

```ts
type GeocodeKind = "address" | "street" | "locality" | "poi" | "postcode";
```

The document kinds the reverse endpoint can filter by.

#### `GuidanceSeverity`

*Type*

```ts
type GuidanceSeverity = "info" | "warning" | "critical";
```

Instruction urgency, as emitted by the core (`InstructionSeverity`).

#### `InteractionType`

*Type*

```ts
type InteractionType = "click" | "mouseenter" | "mouseleave" | "mousemove";
```

The mouse events an interaction can listen for.

#### `LngLatLike`

*Type*

```ts
type LngLatLike = [number, number] | {
    lng: number;
    lat: number;
} | {
    lon: number;
    lat: number;
};
```

A longitude/latitude pair, accepted in the three common shapes.

#### `LogoPosition`

*Type*

```ts
type LogoPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
```

Corner positions accepted by the logo control.

#### `LutBytes`

*Type*

```ts
type LutBytes = Uint8Array;
```

A colour-theme LUT: exactly [`LUT_BYTES`](#lut_bytes) bytes, layout as above.

#### `MapBrandTheme`

*Type*

```ts
type MapBrandTheme = "day" | "night";
```

Day (dark ink on light maps) or Night (white on dark maps).

#### `MapDisplayMode`

*Type*

```ts
type MapDisplayMode = "browse" | "navigating";
```

Whether the map is being browsed or actively navigated.

#### `MapMapBaseGroup`

*Type*

```ts
type MapMapBaseGroup = "base" | "designed";
```

Which half of the catalogue an entry belongs to.

#### `MapMapBaseId`

*Type*

```ts
type MapMapBaseId = "light" | "dark" | "streets" | "midnight" | "navigator-day" | "navigator-night" | "fleet" | "outdoor" | "dataviz" | "backdrop" | "print";
```

Every first-party base id.

#### `MapMapTheme`

*Type*

```ts
type MapMapTheme = "light" | "dark";
```

Which built-in palette a theme starts from.

#### `MarkerGlyphId`

*Type*

```ts
type MarkerGlyphId = (typeof MARKER_GLYPH_IDS)[number];
```

One marker glyph id (`"pin"`, `"star"`, `"fuel"`, ...).

#### `MarkerSize`

*Type*

```ts
type MarkerSize = "s" | "m" | "l";
```

One marker size preset.

#### `NavAlertAudioMode`

*Type*

```ts
type NavAlertAudioMode = (typeof NAV_ALERT_AUDIO_MODES)[number];
```

One audio mode.

#### `NavAlertChipPosition`

*Type*

```ts
type NavAlertChipPosition = (typeof NAV_ALERT_CHIP_POSITIONS)[number];
```

One chip position slot.

#### `NavAlertIconSet`

*Type*

```ts
type NavAlertIconSet = (typeof NAV_ALERT_ICON_SETS)[number];
```

One curated icon set. `"brand"` means "use my `iconUrlOverrides`".

#### `NavAlertLeadDistance`

*Type*

```ts
type NavAlertLeadDistance = (typeof NAV_ALERT_LEAD_DISTANCES)[number];
```

One lead-distance preset.

#### `NavAlertMode`

*Type*

```ts
type NavAlertMode = (typeof NAV_ALERT_MODES)[number];
```

One alert mode.

#### `NavAlertSound`

*Type*

```ts
type NavAlertSound = (typeof NAV_ALERT_SOUNDS)[number];
```

One built-in earcon.

#### `NavCameraKind`

*Type*

```ts
type NavCameraKind = (typeof NAV_CAMERA_KINDS)[number];
```

One safety-camera kind.

#### `NavigationCameraMode`

*Type*

```ts
type NavigationCameraMode = "follow" | "overview" | "free";
```

The camera's current behaviour.

#### `PlacesInput`

*Type*

```ts
type PlacesInput = Place[] | PlacesFeatureCollection;
```

Either input shape for [`PlacesLayer`](#placeslayer) / `setPlaces`.

#### `PlaceWithDistance`

*Type*

```ts
type PlaceWithDistance = Place & {
    distanceM: number;
};
```

A place with its straight-line distance attached (see `nearest`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `distanceM` | `number` | required | Straight-line (haversine) distance from the origin, metres. |

#### `PlaceWithDriveTime`

*Type*

```ts
type PlaceWithDriveTime = Place & {
    durationS: number;
    distanceM: number | null;
};
```

A place with gateway drive time attached (see `nearestByDriveTime`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `durationS` | `number` | required | Drive time from the origin, seconds. |
| `distanceM` | `number \| null` | required | Driven distance from the origin, metres (`null` when not returned). |

#### `ProbeUploadOutcome`

*Type*

```ts
type ProbeUploadOutcome = "accepted" | "refused" | "notEnabled" | "rejected" | "gaveUp";
```

The outcome of an upload attempt.

#### `ProjectionUniform`

*Type*

```ts
type ProjectionUniform = (typeof PROJECTION_UNIFORMS)[number];
```

#### `ProjectionVariant`

*Type*

```ts
type ProjectionVariant = "mercator" | "globe";
```

The `shaderData.variantName` values MapLibre has used from 5.6 to 6.11.
Anything else is treated as `"mercator"` by [`projectionVariant`](#projectionvariant),
with one console warning.

#### `ProtocolHost`

*Type*

```ts
type ProtocolHost = Pick<typeof maplibregl, "addProtocol">;
```

The part of a maplibre-gl module this SDK needs to install a protocol on.
Structural on purpose: a MapLibre 5 host passes its default import and a
MapLibre 6 host passes the namespace (6.x has no default export), and the
two are different TypeScript types. Both carry `addProtocol`.

#### `ReachabilityMode`

*Type*

```ts
type ReachabilityMode = "walk" | "cycle" | "drive" | "truck" | (string & {});
```

Travel mode for the rings. The friendly names map onto gateway costings
(`walk` → `pedestrian`, `cycle` → `bicycle`, `drive` → `auto`); any
other string is passed through as a raw Valhalla costing (e.g.
`"truck"`, `"motor_scooter"`).

#### `RouteEffectName`

*Type*

```ts
type RouteEffectName = "flow";
```

The names the effects registry knows. Grows as effects land.

#### `RouteProfile`

*Type*

```ts
type RouteProfile = "driving" | "walking" | "truck" | (string & {});
```

OSRM routing profile. MapMap ships `driving`, `walking` and `truck`.

#### `SearchBoxBias`

*Type*

```ts
type SearchBoxBias = "map-centre" | {
    lat: number;
    lon: number;
} | false;
```

Where a suggest request should bias its results.

#### `SearchBoxSelectListener`

*Type*

```ts
type SearchBoxSelectListener = (event: SearchBoxSelectEvent) => void;
```

The listener signature for the `"select"` event.

#### `Slot`

*Type*

```ts
type Slot = "bottom" | "middle" | "top";
```

A named insertion point in the MapMap layer stack.

#### `SunTimes`

*Type*

```ts
type SunTimes = {
    sunrise: Date;
    sunset: Date;
} | "polarDay" | "polarNight";
```

Sun-times result: either both events, or a polar day/night marker.

#### `ThemeSource`

*Type*

```ts
type ThemeSource = {
    type: "geojson";
    data: string;
    attribution?: string;
    maxzoom?: number;
    promoteId?: string;
    cluster?: boolean;
    clusterRadius?: number;
    clusterMaxZoom?: number;
} | {
    type: "vector";
    url?: string;
    tiles?: string[];
    minzoom?: number;
    maxzoom?: number;
    bounds?: [number, number, number, number];
    attribution?: string;
    promoteId?: string | Record<string, string>;
} | {
    type: "raster";
    url?: string;
    tiles?: string[];
    tileSize?: number;
    minzoom?: number;
    maxzoom?: number;
    bounds?: [number, number, number, number];
    attribution?: string;
} | {
    type: "image";
    url: string;
    coordinates: [
        [number, number],
        [number, number],
        [number, number],
        [number, number]
   ];
};
```

A customer-hosted source on a theme. MapMap stores the URL, never the
data: the compiled style lists the source and the viewer's map fetches
it directly. Every URL is `https://` (or `pmtiles://https://` for an
archive `url`). Mirrors `sn_style::ThemeSource`.

#### `TransitDrawnVehicle`

*Type*

```ts
type TransitDrawnVehicle = TransitVehicle & {
    stale: boolean;
    followed: boolean;
    drawAs: BusVehicleType;
    fade: number;
};
```

A vehicle as drawn, with the marker's per-vehicle state (`TransitVehiclesLayer.onDraw`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `stale` | `boolean` | required | Its last fix is over 90 s behind the feed's newest. |
| `followed` | `boolean` | required | It is the vehicle passed to `setFollowed`. |
| `drawAs` | [`BusVehicleType`](#busvehicletype-1) | required | The body type it is drawn as: its feed type, else the layer's `defaultVehicleType`. |
| `fade` | `number` | required | 1 normally; below 1 while the vehicle fades out after leaving the feed. |

#### `TransitFeedStatus`

*Type*

```ts
type TransitFeedStatus = "ok" | "stale" | "unknown";
```

The gateway's freshness states for the feed.

#### `TransitSlot`

*Type*

```ts
type TransitSlot = "bottom" | "middle" | "top";
```

Where in the style the layer sits (the SDK slot names).

#### `UseTheme`

*Type*

```ts
type UseTheme = "default" | "none";
```

`LayerOverride.use_theme` (Mapbox `*-use-theme`). Absent means `"default"`.

#### `Vec3`

*Type*

```ts
type Vec3 = [number, number, number];
```

### Constants (@mapmap/maps)

[`ALERT_CONTRAST_MIN`](#alert_contrast_min), [`BAKED_MARKERS_GLYPH_ID`](#baked_markers_glyph_id), [`BASE_ID_ALIASES`](#base_id_aliases), [`BODS_SHORT_CREDIT`](#bods_short_credit), [`BUS_DEFAULT_VEHICLE_TYPE`](#bus_default_vehicle_type), [`BUS_IMAGES`](#bus_images), [`BUS_INK`](#bus_ink), [`BUS_RING_IMAGES`](#bus_ring_images), [`BUS_SPRITE_IMAGES`](#bus_sprite_images), [`BUS_STALE_AFTER_S`](#bus_stale_after_s), [`BUS_VEHICLE_TYPES`](#bus_vehicle_types), [`CLIP_DEFAULT_MINZOOM`](#clip_default_minzoom), [`CLIP_MAX_VERTICES`](#clip_max_vertices), [`CLIP_TARGETS`](#clip_targets), [`COLOUR_THEME_LAYER_ID`](#colour_theme_layer_id), [`COLOUR_THEME_METADATA_KEY`](#colour_theme_metadata_key), [`DEFAULT_DEBOUNCE_MS`](#default_debounce_ms), [`DEFAULT_GATEWAY_URL`](#default_gateway_url), [`DEFAULT_GLYPHS_URL`](#default_glyphs_url), [`DEFAULT_MAX_MERCATOR_SEGMENT`](#default_max_mercator_segment), [`DEFAULT_TERRAIN_ATTRIBUTION`](#default_terrain_attribution), [`DEFAULT_TERRAIN_URL`](#default_terrain_url), [`DEFAULT_TERRITORY_TILES_URL`](#default_territory_tiles_url), [`directionIcons`](#directionicons), [`EFFECTS_METADATA_KEY`](#effects_metadata_key), [`FEATURESETS`](#featuresets), [`FLOW_DEFAULTS`](#flow_defaults), [`FULL_ATTRIBUTION`](#full_attribution), [`GLOBE_BLEND_GLSL`](#globe_blend_glsl), [`GLOBE_RADIUS_M`](#globe_radius_m), [`IDENTITY_LUT`](#identity_lut), [`KEYED_TILES_TERRITORY`](#keyed_tiles_territory), [`LOGO_SVG`](#logo_svg), [`LUT_BYTES`](#lut_bytes), [`LUT_SIZE`](#lut_size), [`MAPMAP_BASES`](#mapmap_bases), [`MARKER_DEFAULT_COLOUR`](#marker_default_colour), [`MARKER_GLYPH_IDS`](#marker_glyph_ids), [`MARKER_GLYPH_VIEWBOX`](#marker_glyph_viewbox), [`MARKER_LABEL_TEXT_SIZES`](#marker_label_text_sizes), [`MARKER_PIN_BODY_PATH`](#marker_pin_body_path), [`MARKER_SIZES`](#marker_sizes), [`MARKER_SIZES_PX`](#marker_sizes_px), [`markerGlyphPaths`](#markerglyphpaths), [`MARKERS_ID`](#markers_id), [`MAX_MARKER_ID_LENGTH`](#max_marker_id_length), [`MAX_MARKER_IMAGE_BYTES`](#max_marker_image_bytes), [`MAX_MARKER_ITEMS`](#max_marker_items), [`MAX_MARKER_LABEL_LENGTH`](#max_marker_label_length), [`MAX_MARKER_TEXT_LENGTH`](#max_marker_text_length), [`MAX_PUCK_IMAGE_BYTES`](#max_puck_image_bytes), [`MAX_THEME_GEOJSON_SOURCES`](#max_theme_geojson_sources), [`MAX_THEME_SOURCES`](#max_theme_sources), [`NAPTAN_SHORT_CREDIT`](#naptan_short_credit), [`NAV_ALERT_AUDIO_MODES`](#nav_alert_audio_modes), [`NAV_ALERT_CHIP_POSITIONS`](#nav_alert_chip_positions), [`NAV_ALERT_ICON_SETS`](#nav_alert_icon_sets), [`NAV_ALERT_LEAD_DISTANCE_M`](#nav_alert_lead_distance_m), [`NAV_ALERT_LEAD_DISTANCES`](#nav_alert_lead_distances), [`NAV_ALERT_MODES`](#nav_alert_modes), [`NAV_ALERT_SOUND_BASE_URL`](#nav_alert_sound_base_url), [`NAV_ALERT_SOUND_FILES`](#nav_alert_sound_files), [`NAV_ALERT_SOUNDS`](#nav_alert_sounds), [`NAV_CAMERA_DEFAULTS`](#nav_camera_defaults), [`NAV_CAMERA_KINDS`](#nav_camera_kinds), [`NO_ROADS_NEAR_ORIGIN`](#no_roads_near_origin), [`OPENMAPTILES_ATTRIBUTION`](#openmaptiles_attribution), [`OSM_ATTRIBUTION`](#osm_attribution), [`PALETTE_SLOTS`](#palette_slots), [`POI_CATEGORY_COLORS`](#poi_category_colors), [`POI_CATEGORY_IDS`](#poi_category_ids), [`POI_CLASS_CATEGORIES`](#poi_class_categories), [`PROJECTION_UNIFORMS`](#projection_uniforms), [`RIBBON_FLOATS_PER_VERTEX`](#ribbon_floats_per_vertex), [`ROUTE_EFFECTS`](#route_effects), [`SIGNAL_BLUE`](#signal_blue), [`SLOT_ANCHORS`](#slot_anchors), [`SLOTS`](#slots), [`SOURCE_LAYERS`](#source_layers), [`TRANSIT_LINE_FALLBACK_COLOUR`](#transit_line_fallback_colour), [`TRANSIT_TILES_INDEX_URL`](#transit_tiles_index_url)

#### `ALERT_CONTRAST_MIN`

*Constant*

```ts
const ALERT_CONTRAST_MIN = 4.5;
```

Minimum acceptable contrast ratio for alert-chip text on its background.

#### `BAKED_MARKERS_GLYPH_ID`

*Constant*

```ts
const BAKED_MARKERS_GLYPH_ID = "mm-user-markers-glyph";
```

The SECOND symbol layer a BAKED style carries, the glyph-and-label layer
the theme compiler appends next to `mm-user-markers` when it translates
`extra.markers` into style constructs (`sn_style::MARKERS_GLYPH_LAYER_ID`).

This layer is the reliable "already baked" signal: [`MarkersLayer`](#markerslayer)
never creates it, so finding it means the style itself is drawing the
markers and a naive install would draw them TWICE.

#### `BASE_ID_ALIASES`

*Constant*

```ts
const BASE_ID_ALIASES: Readonly<Record<string, MapMapBaseId>>;
```

Retired ids, mapped to the entry that replaced them. The Navigator pair
shipped as "Calm Cab Day"/"Calm Cab Night"; the old ids are on disk in
every document saved while those labels shipped, so [`baseTheme`](#basetheme)
keeps resolving them. Mirrors `sn_style::BASE_ID_ALIASES`.

#### `BODS_SHORT_CREDIT`

*Constant*

```ts
const BODS_SHORT_CREDIT = "Bus data: DfT Bus Open Data Service, OGL v3.0";
```

The short BODS credit the layer adds to the map's credit line. The full
OGL wording is in every API answer's `attribution` and in
THIRD-PARTY-NOTICES.md.

#### `BUS_DEFAULT_VEHICLE_TYPE`

*Constant*

```ts
const BUS_DEFAULT_VEHICLE_TYPE: BusVehicleType;
```

What a vehicle with no sourced body type is drawn as, unless `defaultVehicleType` says otherwise.

#### `BUS_IMAGES`

*Constant*

```ts
const BUS_IMAGES: {
    readonly puck: "mapmap-transit-bus-puck";
    readonly puckFollowed: "mapmap-transit-bus-puck-followed";
    readonly tick: "mapmap-transit-bus-tick";
    readonly sprite: "mapmap-transit-bus";
    readonly ring: "mapmap-transit-bus-ring";
    readonly spriteDouble: "mapmap-transit-bus-double";
    readonly ringDouble: "mapmap-transit-bus-double-ring";
    readonly spriteSingle: "mapmap-transit-bus-single";
    readonly ringSingle: "mapmap-transit-bus-single-ring";
    readonly spriteMini: "mapmap-transit-bus-mini";
    readonly ringMini: "mapmap-transit-bus-mini-ring";
};
```

Image ids the bus marker adds to the style (shared by every layer
instance). `sprite` and `ring` are the generic bus (the default look);
each body type has its own.

#### `BUS_INK`

*Constant*

```ts
const BUS_INK = "#0f1115";
```

The bus marker's neutral ink.

#### `BUS_RING_IMAGES`

*Constant*

```ts
const BUS_RING_IMAGES: Record<BusVehicleType, string>;
```

The followed ring's under-image id for each body type.

#### `BUS_SPRITE_IMAGES`

*Constant*

```ts
const BUS_SPRITE_IMAGES: Record<BusVehicleType, string>;
```

The top-down sprite's image id for each body type.

#### `BUS_STALE_AFTER_S`

*Constant*

```ts
const BUS_STALE_AFTER_S = 90;
```

A vehicle whose last fix is this far behind the feed's newest fix
(`recorded_at_max`) reads stale (drawn at 50 %), seconds.

#### `BUS_VEHICLE_TYPES`

*Constant*

```ts
const BUS_VEHICLE_TYPES: readonly BusVehicleType[];
```

Every [`BusVehicleType`](#busvehicletype-1).

#### `CLIP_DEFAULT_MINZOOM`

*Constant*

```ts
const CLIP_DEFAULT_MINZOOM = 13;
```

Default `ClipOptions.minzoom`: the street zooms where buildings draw.

#### `CLIP_MAX_VERTICES`

*Constant*

```ts
const CLIP_MAX_VERTICES = 500;
```

The vertex cap for one clip polygon. The `distance` expression is
evaluated for every feature in every tile the clipped layers draw, against
every segment of the polygon, so its cost grows with both.

#### `CLIP_TARGETS`

*Constant*

```ts
const CLIP_TARGETS: {
    readonly buildings: readonly ["building", "building-3d", "building-outline"];
    readonly symbols: readonly ["poi-labels", "housenumber"];
};
```

The MapMap skeleton layers each clip type hides (the compiled style's ids,
CONTRACT.md 2.1). `building-3d` is the runtime extrusion `setBuildings3d`
adds.

#### `COLOUR_THEME_LAYER_ID`

*Constant*

```ts
const COLOUR_THEME_LAYER_ID = "mapmap-colour-theme";
```

The default layer id `map.setColorTheme` uses.

#### `COLOUR_THEME_METADATA_KEY`

*Constant*

```ts
const COLOUR_THEME_METADATA_KEY = "mapmap:color-theme";
```

The compiled style's metadata key for the theme summary.

#### `DEFAULT_DEBOUNCE_MS`

*Constant*

```ts
const DEFAULT_DEBOUNCE_MS = 250;
```

Default debounce between the last keystroke and the suggest request.

#### `DEFAULT_GATEWAY_URL`

*Constant*

```ts
const DEFAULT_GATEWAY_URL = "https://api.mapmap.ai";
```

The MapMap gateway, the default for `MapMapOptions.baseUrl`.

#### `DEFAULT_GLYPHS_URL`

*Constant*

```ts
const DEFAULT_GLYPHS_URL = "https://fonts.mapmap.ai/{fontstack}/{range}.pbf";
```

Default glyph endpoint (PBF fontstacks), mirroring `sn_style::DEFAULT_GLYPHS_URL`.

#### `DEFAULT_MAX_MERCATOR_SEGMENT`

*Constant*

```ts
const DEFAULT_MAX_MERCATOR_SEGMENT: number;
```

Default [`subdivideMercator`](#subdividemercator) limit: 1/512 mercator units, about 78 km at the equator.

#### `DEFAULT_TERRAIN_ATTRIBUTION`

*Constant*

```ts
const DEFAULT_TERRAIN_ATTRIBUTION: string;
```

#### `DEFAULT_TERRAIN_URL`

*Constant*

```ts
const DEFAULT_TERRAIN_URL = "https://mapmap.ai/terrain/{z}/{x}/{y}.png";
```

#### `DEFAULT_TERRITORY_TILES_URL`

*Constant*

```ts
const DEFAULT_TERRITORY_TILES_URL = "pmtiles://https://tiles.mapmap.ai/planet.pmtiles";
```

The keyless default territory tiles URL (overridable per map).

`tiles.mapmap.ai` serves this archive only to mapmap.ai, `localhost`,
`127.0.0.1` and callers that send no `Origin`/`Referer` (QGIS, curl, a
native app). A browser page on any other site gets a 403, so a style
built with an API key reads the same archive through the keyed gateway
instead: see [`keyedTilesUrl`](#keyedtilesurl) and [`territoryTilesSourceUrl`](#territorytilessourceurl).

#### `directionIcons`

*Constant* · also exported from `@mapmap/maps/direction-icons`

```ts
const directionIcons: {
    readonly arrive: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M4.991 4.000C4.596 4.000 4.209 3.883 3.880 3.663C3.551 3.443 3.295 3.131 3.144 2.765C2.992 2.400 2.953 1.998 3.030 1.610C3.107 1.222 3.297 0.865 3.577 0.586C3.857 0.306 4.213 0.116 4.601 0.038C4.989 -0.039 5.391 0.001 5.757 0.152C6.122 0.304 6.435 0.560 6.654 0.889C6.874 1.218 6.991 1.604 6.991 2.000C6.991 2.530 6.781 3.039 6.406 3.414C6.031 3.789 5.522 4.000 4.991 4.000Z M9.904 12.351C9.904 12.351 5.001 6.008 5.001 6.008C5.001 6.008 0.097 12.351 0.097 12.351C0.041 12.415 0.007 12.495 0.001 12.580C-0.005 12.664 0.017 12.748 0.063 12.819C0.110 12.890 0.179 12.944 0.259 12.972C0.339 13.000 0.426 13.001 0.506 12.975C0.506 12.975 3.458 11.631 3.458 11.631C3.517 11.593 3.585 11.573 3.655 11.574C3.725 11.575 3.793 11.597 3.851 11.636C3.909 11.675 3.954 11.731 3.980 11.796C4.006 11.860 4.013 11.931 3.999 12.000C3.999 12.000 3.991 12.000 3.991 12.000C3.991 12.000 3.991 17.500 3.991 17.500C3.991 17.566 4.004 17.631 4.029 17.691C4.055 17.752 4.091 17.807 4.138 17.854C4.184 17.900 4.239 17.937 4.300 17.962C4.361 17.987 4.426 18.000 4.491 18.000C4.491 18.000 5.491 18.000 5.491 18.000C5.557 18.000 5.622 17.987 5.683 17.962C5.743 17.937 5.799 17.900 5.845 17.854C5.891 17.807 5.928 17.752 5.953 17.691C5.978 17.631 5.991 17.566 5.991 17.500C5.991 17.500 5.991 12.008 5.991 12.008C5.991 12.008 6.001 12.008 6.001 12.008C5.985 11.939 5.991 11.867 6.017 11.801C6.043 11.735 6.088 11.679 6.146 11.638C6.204 11.598 6.272 11.576 6.343 11.574C6.414 11.573 6.483 11.593 6.543 11.631C6.543 11.631 9.495 12.975 9.495 12.975C9.575 13.001 9.662 13.000 9.742 12.972C9.823 12.944 9.891 12.890 9.938 12.819C9.984 12.748 10.006 12.664 10.000 12.580C9.994 12.495 9.960 12.415 9.904 12.351Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(5.009 1)\"/></svg>";
    readonly arrive_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.000 6.955C1.604 6.955 1.218 6.837 0.889 6.618C0.560 6.398 0.304 6.086 0.152 5.720C0.001 5.355 -0.039 4.952 0.038 4.565C0.116 4.177 0.306 3.820 0.586 3.540C0.865 3.261 1.222 3.070 1.610 2.993C1.998 2.916 2.400 2.956 2.765 3.107C3.131 3.258 3.443 3.515 3.663 3.844C3.883 4.172 4.000 4.559 4.000 4.955C4.000 5.485 3.789 5.994 3.414 6.369C3.039 6.744 2.530 6.955 2.000 6.955Z M12.003 5.991C13.070 6.010 14.087 6.447 14.833 7.210C15.580 7.972 15.996 8.998 15.992 10.065C15.992 10.065 16.000 10.065 16.000 10.065C16.000 10.065 16.000 11.442 16.000 11.442C16.000 11.510 16.013 11.576 16.039 11.638C16.065 11.701 16.102 11.757 16.150 11.805C16.198 11.852 16.254 11.890 16.316 11.916C16.378 11.941 16.445 11.955 16.512 11.955C16.512 11.955 17.488 11.955 17.488 11.955C17.555 11.955 17.622 11.941 17.684 11.916C17.746 11.890 17.802 11.852 17.850 11.805C17.898 11.757 17.935 11.701 17.961 11.638C17.987 11.576 18.000 11.510 18.000 11.442C18.000 11.442 18.000 9.955 18.000 9.955C18.000 9.955 17.986 9.955 17.986 9.955C17.962 8.377 17.323 6.871 16.206 5.758C15.088 4.644 13.580 4.010 12.003 3.991C12.003 3.991 11.992 3.990 11.992 3.990C11.992 3.990 11.992 4.010 11.992 4.010C11.992 4.010 11.000 4.010 11.000 4.010C11.000 4.010 11.000 4.001 11.000 4.001C10.931 4.016 10.859 4.010 10.793 3.984C10.727 3.958 10.670 3.914 10.630 3.855C10.590 3.797 10.567 3.729 10.566 3.658C10.565 3.587 10.584 3.518 10.623 3.458C10.623 3.458 11.966 0.506 11.966 0.506C11.993 0.426 11.992 0.339 11.964 0.259C11.935 0.179 11.882 0.110 11.811 0.063C11.740 0.017 11.656 -0.005 11.571 0.001C11.487 0.007 11.406 0.041 11.343 0.097C11.343 0.097 5.000 5.001 5.000 5.001C5.000 5.001 11.343 9.904 11.343 9.904C11.406 9.960 11.487 9.994 11.571 10.000C11.656 10.006 11.740 9.984 11.811 9.938C11.882 9.891 11.935 9.823 11.964 9.743C11.992 9.662 11.993 9.575 11.966 9.495C11.966 9.495 10.623 6.543 10.623 6.543C10.584 6.483 10.565 6.414 10.566 6.343C10.567 6.272 10.590 6.204 10.630 6.146C10.670 6.088 10.727 6.043 10.793 6.017C10.859 5.991 10.931 5.985 11.000 6.001C11.000 6.001 11.000 6.010 11.000 6.010C11.000 6.010 12.000 6.010 12.000 6.010Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(1 5.045)\"/></svg>";
    readonly arrive_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M14.000 4.955C14.000 4.559 14.117 4.172 14.337 3.844C14.557 3.515 14.869 3.258 15.235 3.107C15.600 2.956 16.002 2.916 16.390 2.993C16.778 3.070 17.135 3.261 17.414 3.540C17.694 3.820 17.884 4.177 17.962 4.565C18.039 4.952 17.999 5.355 17.848 5.720C17.696 6.086 17.440 6.398 17.111 6.618C16.782 6.837 16.396 6.955 16.000 6.955C15.470 6.955 14.961 6.744 14.586 6.369C14.211 5.994 14.000 5.485 14.000 4.955Z M6.000 6.010C6.000 6.010 7.000 6.010 7.000 6.010C7.000 6.010 7.000 6.001 7.000 6.001C7.069 5.985 7.141 5.991 7.207 6.017C7.273 6.043 7.330 6.088 7.370 6.146C7.410 6.204 7.433 6.272 7.434 6.343C7.435 6.414 7.416 6.483 7.377 6.543C7.377 6.543 6.034 9.495 6.034 9.495C6.007 9.575 6.008 9.662 6.036 9.743C6.065 9.823 6.118 9.891 6.189 9.938C6.260 9.984 6.344 10.006 6.429 10.000C6.513 9.994 6.594 9.960 6.657 9.904C6.657 9.904 13.000 5.001 13.000 5.001C13.000 5.001 6.657 0.097 6.657 0.097C6.594 0.041 6.513 0.007 6.429 0.001C6.344 -0.005 6.260 0.017 6.189 0.063C6.118 0.110 6.065 0.179 6.036 0.259C6.008 0.339 6.007 0.426 6.034 0.506C6.034 0.506 7.377 3.458 7.377 3.458C7.416 3.518 7.435 3.587 7.434 3.658C7.433 3.729 7.410 3.797 7.370 3.855C7.330 3.914 7.273 3.958 7.207 3.984C7.141 4.010 7.069 4.016 7.000 4.001C7.000 4.001 7.000 4.010 7.000 4.010C7.000 4.010 6.008 4.010 6.008 4.010C6.008 4.010 6.008 3.990 6.008 3.990C6.008 3.990 5.997 3.991 5.997 3.991C4.420 4.010 2.912 4.644 1.794 5.758C0.677 6.871 0.038 8.377 0.014 9.955C0.014 9.955 0.000 9.955 0.000 9.955C0.000 9.955 0.000 11.442 0.000 11.442C-0.000 11.510 0.013 11.576 0.039 11.638C0.065 11.701 0.102 11.757 0.150 11.805C0.198 11.852 0.254 11.890 0.316 11.916C0.378 11.941 0.445 11.955 0.512 11.955C0.512 11.955 1.488 11.955 1.488 11.955C1.555 11.955 1.622 11.941 1.684 11.916C1.746 11.890 1.802 11.852 1.850 11.805C1.898 11.757 1.935 11.701 1.961 11.638C1.987 11.576 2.000 11.510 2.000 11.442C2.000 11.442 2.000 10.065 2.000 10.065C2.000 10.065 2.008 10.065 2.008 10.065C2.004 8.998 2.420 7.972 3.167 7.210C3.913 6.447 4.930 6.010 5.997 5.991Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(1 5.045)\"/></svg>";
    readonly arrive_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M4.991 4.000C4.596 4.000 4.209 3.883 3.880 3.663C3.551 3.443 3.295 3.131 3.144 2.765C2.992 2.400 2.953 1.998 3.030 1.610C3.107 1.222 3.297 0.865 3.577 0.586C3.857 0.306 4.213 0.116 4.601 0.038C4.989 -0.039 5.391 0.001 5.757 0.152C6.122 0.304 6.435 0.560 6.654 0.889C6.874 1.218 6.991 1.604 6.991 2.000C6.991 2.530 6.781 3.039 6.406 3.414C6.031 3.789 5.522 4.000 4.991 4.000Z M9.904 12.351C9.904 12.351 5.001 6.008 5.001 6.008C5.001 6.008 0.097 12.351 0.097 12.351C0.041 12.415 0.007 12.495 0.001 12.580C-0.005 12.664 0.017 12.748 0.063 12.819C0.110 12.890 0.179 12.944 0.259 12.972C0.339 13.000 0.426 13.001 0.506 12.975C0.506 12.975 3.458 11.631 3.458 11.631C3.517 11.593 3.585 11.573 3.655 11.574C3.725 11.575 3.793 11.597 3.851 11.636C3.909 11.675 3.954 11.731 3.980 11.796C4.006 11.860 4.013 11.931 3.999 12.000C3.999 12.000 3.991 12.000 3.991 12.000C3.991 12.000 3.991 17.500 3.991 17.500C3.991 17.566 4.004 17.631 4.029 17.691C4.055 17.752 4.091 17.807 4.138 17.854C4.184 17.900 4.239 17.937 4.300 17.962C4.361 17.987 4.426 18.000 4.491 18.000C4.491 18.000 5.491 18.000 5.491 18.000C5.557 18.000 5.622 17.987 5.683 17.962C5.743 17.937 5.799 17.900 5.845 17.854C5.891 17.807 5.928 17.752 5.953 17.691C5.978 17.631 5.991 17.566 5.991 17.500C5.991 17.500 5.991 12.008 5.991 12.008C5.991 12.008 6.001 12.008 6.001 12.008C5.985 11.939 5.991 11.867 6.017 11.801C6.043 11.735 6.088 11.679 6.146 11.638C6.204 11.598 6.272 11.576 6.343 11.574C6.414 11.573 6.483 11.593 6.543 11.631C6.543 11.631 9.495 12.975 9.495 12.975C9.575 13.001 9.662 13.000 9.742 12.972C9.823 12.944 9.891 12.890 9.938 12.819C9.984 12.748 10.006 12.664 10.000 12.580C9.994 12.495 9.960 12.415 9.904 12.351Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(5.009 1)\"/></svg>";
    readonly close: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M14.857 1.560C14.857 1.560 9.267 7.150 9.267 7.150C9.173 7.243 9.121 7.369 9.121 7.501C9.121 7.633 9.173 7.759 9.267 7.853C9.267 7.853 14.857 13.443 14.857 13.443C14.950 13.536 15.002 13.662 15.002 13.794C15.002 13.926 14.950 14.052 14.857 14.146C14.857 14.146 14.146 14.857 14.146 14.857C14.052 14.950 13.926 15.002 13.794 15.002C13.662 15.002 13.536 14.950 13.443 14.857C13.443 14.857 7.853 9.267 7.853 9.267C7.759 9.173 7.633 9.121 7.501 9.121C7.369 9.121 7.243 9.173 7.150 9.267C7.150 9.267 1.560 14.857 1.560 14.857C1.466 14.950 1.340 15.002 1.208 15.002C1.076 15.002 0.950 14.950 0.857 14.857C0.857 14.857 0.146 14.146 0.146 14.146C0.052 14.052 0.000 13.926 0.000 13.794C0.000 13.662 0.052 13.536 0.146 13.443C0.146 13.443 5.736 7.853 5.736 7.853C5.829 7.759 5.881 7.633 5.881 7.501C5.881 7.369 5.829 7.243 5.736 7.150C5.736 7.150 0.146 1.560 0.146 1.560C0.052 1.466 0.000 1.340 0.000 1.208C-0.000 1.076 0.052 0.950 0.146 0.857C0.146 0.857 0.857 0.146 0.857 0.146C0.950 0.052 1.076 0.000 1.208 0.000C1.340 0.000 1.466 0.052 1.560 0.146C1.560 0.146 7.150 5.736 7.150 5.736C7.243 5.829 7.369 5.881 7.501 5.881C7.633 5.881 7.759 5.829 7.853 5.736C7.853 5.736 13.443 0.146 13.443 0.146C13.536 0.052 13.662 -0.000 13.794 0.000C13.926 -0.000 14.052 0.052 14.146 0.146C14.146 0.146 14.857 0.857 14.857 0.857C14.903 0.903 14.939 0.958 14.964 1.018C14.989 1.078 15.002 1.143 15.002 1.208C15.002 1.273 14.989 1.338 14.964 1.398C14.939 1.459 14.903 1.513 14.857 1.560Z\" fill=\"currentColor\" transform=\"translate(2.499 2.499)\"/></svg>";
    readonly continue: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly continue_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M7.992 3.999C7.992 3.999 8.003 4.001 8.003 4.001C9.600 4.019 11.125 4.668 12.247 5.805C13.368 6.942 13.995 8.477 13.992 10.074C13.992 10.074 13.992 14.019 13.992 14.019C13.992 14.019 13.989 14.019 13.989 14.019C13.989 14.019 13.991 15.522 13.991 15.522C13.991 15.587 13.978 15.652 13.953 15.712C13.928 15.772 13.892 15.827 13.845 15.873C13.799 15.919 13.745 15.956 13.684 15.981C13.624 16.006 13.560 16.019 13.494 16.019C13.494 16.019 12.488 16.019 12.488 16.019C12.356 16.019 12.230 15.967 12.137 15.874C12.044 15.780 11.992 15.654 11.992 15.523C11.992 15.523 11.992 10.074 11.992 10.074C11.996 9.007 11.580 7.981 10.833 7.219C10.087 6.456 9.070 6.019 8.003 6.001C8.003 6.001 6.004 6.001 6.004 6.001C5.935 5.985 5.863 5.991 5.797 6.017C5.731 6.043 5.675 6.088 5.634 6.146C5.594 6.204 5.572 6.272 5.570 6.343C5.569 6.414 5.589 6.483 5.627 6.543C5.627 6.543 6.971 9.495 6.971 9.495C6.997 9.576 6.996 9.663 6.968 9.743C6.940 9.823 6.886 9.891 6.815 9.938C6.744 9.984 6.660 10.006 6.575 10.000C6.491 9.994 6.411 9.960 6.347 9.904C6.347 9.904 0.000 5.019 0.000 5.019C0.000 5.019 6.347 0.097 6.347 0.097C6.411 0.041 6.491 0.007 6.575 0.001C6.660 -0.005 6.744 0.017 6.815 0.063C6.886 0.110 6.940 0.179 6.968 0.259C6.996 0.339 6.997 0.426 6.971 0.506C6.971 0.506 5.627 3.458 5.627 3.458C5.589 3.518 5.569 3.587 5.570 3.658C5.572 3.729 5.594 3.797 5.634 3.855C5.675 3.914 5.731 3.958 5.797 3.984C5.863 4.010 5.935 4.016 6.004 4.001C6.004 4.001 7.992 4.001 7.992 4.001Z\" fill=\"currentColor\" transform=\"translate(2.008 1.981)\"/></svg>";
    readonly continue_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M5.989 6.001C4.922 6.019 3.905 6.456 3.159 7.219C2.412 7.981 1.996 9.007 2.000 10.074C2.000 10.074 2.000 15.523 2.000 15.523C2.000 15.654 1.948 15.780 1.855 15.874C1.762 15.967 1.635 16.019 1.504 16.019C1.504 16.019 0.497 16.019 0.497 16.019C0.432 16.019 0.368 16.006 0.307 15.981C0.247 15.956 0.192 15.919 0.146 15.873C0.100 15.827 0.064 15.772 0.039 15.712C0.014 15.652 0.001 15.587 0.001 15.522C0.001 15.522 0.003 14.019 0.003 14.019C0.003 14.019 0.000 14.019 0.000 14.019C0.000 14.019 0.000 10.074 0.000 10.074C-0.004 8.477 0.624 6.942 1.745 5.805C2.867 4.668 4.392 4.019 5.989 4.001C5.989 4.001 6.000 3.999 6.000 3.999C6.000 3.999 6.000 4.001 6.000 4.001C6.000 4.001 7.988 4.001 7.988 4.001C8.057 4.016 8.129 4.010 8.195 3.984C8.260 3.958 8.317 3.914 8.358 3.855C8.398 3.797 8.420 3.729 8.422 3.658C8.423 3.587 8.403 3.518 8.365 3.458C8.365 3.458 7.021 0.506 7.021 0.506C6.995 0.426 6.996 0.339 7.024 0.259C7.052 0.179 7.106 0.110 7.177 0.063C7.247 0.017 7.332 -0.005 7.416 0.001C7.501 0.007 7.581 0.041 7.645 0.097C7.645 0.097 13.992 5.019 13.992 5.019C13.992 5.019 7.645 9.904 7.645 9.904C7.581 9.960 7.501 9.994 7.416 10.000C7.332 10.006 7.247 9.984 7.177 9.938C7.106 9.891 7.052 9.823 7.024 9.743C6.996 9.663 6.995 9.576 7.021 9.495C7.021 9.495 8.365 6.543 8.365 6.543C8.403 6.483 8.423 6.414 8.422 6.343C8.420 6.272 8.398 6.204 8.358 6.146C8.317 6.088 8.260 6.043 8.195 6.017C8.129 5.991 8.057 5.985 7.988 6.001C7.988 6.001 5.989 6.001 5.989 6.001Z\" fill=\"currentColor\" transform=\"translate(4 1.981)\"/></svg>";
    readonly continue_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.986 11.043C9.986 11.043 9.986 14.504 9.986 14.504C9.986 14.635 9.934 14.762 9.841 14.855C9.748 14.948 9.621 15.000 9.490 15.000C9.490 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.443 5.979 9.067 6.878 9.472 7.881C9.877 8.883 10.053 9.964 9.986 11.043Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly continue_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.354 5.249C2.354 5.249 4.811 3.175 4.811 3.175C4.873 3.141 4.924 3.089 4.957 3.027C4.990 2.964 5.003 2.893 4.996 2.823C4.988 2.752 4.960 2.686 4.915 2.631C4.869 2.577 4.809 2.538 4.741 2.518C4.741 2.518 1.792 1.167 1.792 1.167C1.719 1.124 1.663 1.057 1.632 0.978C1.601 0.900 1.596 0.813 1.619 0.731C1.642 0.649 1.692 0.578 1.760 0.527C1.827 0.476 1.910 0.449 1.995 0.450C1.995 0.450 10.000 0.000 10.000 0.000C10.000 0.000 8.419 7.860 8.419 7.860C8.408 7.944 8.369 8.022 8.309 8.082C8.249 8.142 8.171 8.181 8.087 8.192C8.003 8.203 7.918 8.186 7.844 8.144C7.771 8.102 7.713 8.037 7.680 7.959C7.680 7.959 6.762 4.849 6.762 4.849C6.752 4.778 6.721 4.713 6.674 4.660C6.626 4.608 6.564 4.571 6.496 4.553C6.427 4.536 6.355 4.539 6.288 4.563C6.222 4.586 6.163 4.629 6.121 4.686C6.121 4.686 3.673 6.751 3.673 6.751C3.106 7.276 2.663 7.920 2.375 8.636C2.087 9.353 1.962 10.125 2.008 10.895C2.008 10.895 2.013 14.503 2.013 14.503C2.013 14.568 2.000 14.633 1.975 14.693C1.950 14.754 1.914 14.808 1.868 14.854C1.821 14.901 1.767 14.937 1.707 14.962C1.646 14.987 1.582 15.000 1.516 15.000C1.516 15.000 0.510 15.000 0.510 15.000C0.378 15.000 0.252 14.948 0.159 14.855C0.066 14.762 0.014 14.635 0.014 14.504C0.014 14.504 0.014 11.043 0.014 11.043C-0.053 9.964 0.123 8.883 0.528 7.881C0.932 6.878 1.556 5.979 2.354 5.249Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly continue_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly continue_uturn: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M15.010 5.500C15.010 5.500 15.010 14.997 15.010 14.997C15.010 15.063 14.997 15.128 14.971 15.189C14.946 15.250 14.909 15.306 14.862 15.353C14.815 15.399 14.760 15.436 14.699 15.462C14.638 15.487 14.572 15.500 14.506 15.500C14.506 15.500 13.513 15.500 13.513 15.500C13.447 15.500 13.382 15.487 13.321 15.462C13.259 15.436 13.204 15.399 13.157 15.353C13.110 15.306 13.073 15.250 13.048 15.189C13.023 15.128 13.010 15.063 13.010 14.997C13.010 14.997 13.010 5.500 13.010 5.500C13.010 4.572 12.641 3.682 11.985 3.025C11.328 2.369 10.438 2.000 9.510 2.000C8.581 2.000 7.691 2.369 7.035 3.025C6.378 3.682 6.010 4.572 6.010 5.500C6.010 5.500 6.010 9.500 6.010 9.500C6.010 9.500 6.001 9.500 6.001 9.500C5.985 9.569 5.991 9.641 6.017 9.707C6.043 9.773 6.088 9.830 6.146 9.870C6.204 9.910 6.272 9.933 6.343 9.934C6.414 9.935 6.483 9.916 6.543 9.877C6.543 9.877 9.495 8.534 9.495 8.534C9.575 8.507 9.663 8.508 9.743 8.536C9.823 8.565 9.891 8.618 9.938 8.689C9.984 8.760 10.006 8.844 10.000 8.929C9.994 9.013 9.960 9.094 9.904 9.157C9.904 9.157 5.001 15.500 5.001 15.500C5.001 15.500 0.097 9.157 0.097 9.157C0.041 9.094 0.007 9.013 0.001 8.929C-0.005 8.844 0.017 8.760 0.063 8.689C0.110 8.618 0.179 8.565 0.259 8.536C0.339 8.508 0.426 8.507 0.506 8.534C0.506 8.534 3.458 9.877 3.458 9.877C3.518 9.916 3.587 9.935 3.658 9.934C3.729 9.933 3.797 9.910 3.855 9.870C3.914 9.830 3.958 9.773 3.984 9.707C4.010 9.641 4.016 9.569 4.001 9.500C4.001 9.500 4.010 9.500 4.010 9.500C4.010 9.500 4.010 5.500 4.010 5.500C4.010 4.041 4.589 2.642 5.621 1.611C6.652 0.579 8.051 0.000 9.510 0.000C10.968 0.000 12.367 0.579 13.399 1.611C14.430 2.642 15.010 4.041 15.010 5.500Z\" fill=\"currentColor\" transform=\"translate(1.999 2.5)\"/></svg>";
    readonly depart: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M4.991 18.000C4.596 18.000 4.209 17.883 3.880 17.663C3.551 17.443 3.295 17.131 3.144 16.765C2.992 16.400 2.953 15.998 3.030 15.610C3.107 15.222 3.297 14.865 3.577 14.586C3.857 14.306 4.213 14.116 4.601 14.038C4.989 13.961 5.391 14.001 5.757 14.152C6.122 14.304 6.435 14.560 6.654 14.889C6.874 15.218 6.991 15.604 6.991 16.000C6.991 16.530 6.781 17.039 6.406 17.414C6.031 17.789 5.522 18.000 4.991 18.000Z M9.904 6.343C9.904 6.343 5.001 0.000 5.001 0.000C5.001 0.000 0.097 6.343 0.097 6.343C0.041 6.406 0.007 6.487 0.001 6.571C-0.005 6.656 0.017 6.740 0.063 6.811C0.110 6.882 0.179 6.935 0.259 6.964C0.339 6.992 0.426 6.993 0.506 6.966C0.506 6.966 3.458 5.623 3.458 5.623C3.517 5.585 3.585 5.565 3.655 5.566C3.725 5.567 3.793 5.588 3.851 5.628C3.909 5.667 3.954 5.723 3.980 5.787C4.006 5.852 4.013 5.923 3.999 5.992C3.999 5.992 3.991 5.992 3.991 5.992C3.991 5.992 3.991 11.492 3.991 11.492C3.991 11.557 4.004 11.622 4.029 11.683C4.055 11.744 4.091 11.799 4.138 11.845C4.184 11.892 4.239 11.929 4.300 11.954C4.361 11.979 4.426 11.992 4.491 11.992C4.491 11.992 5.491 11.992 5.491 11.992C5.557 11.992 5.622 11.979 5.683 11.954C5.743 11.929 5.799 11.892 5.845 11.845C5.891 11.799 5.928 11.744 5.953 11.683C5.978 11.622 5.991 11.557 5.991 11.492C5.991 11.492 5.991 6.000 5.991 6.000C5.991 6.000 6.001 6.000 6.001 6.000C5.985 5.931 5.991 5.859 6.017 5.793C6.043 5.727 6.088 5.670 6.146 5.630C6.204 5.590 6.272 5.567 6.343 5.566C6.414 5.565 6.483 5.584 6.543 5.623C6.543 5.623 9.495 6.966 9.495 6.966C9.575 6.993 9.662 6.992 9.743 6.964C9.823 6.935 9.891 6.882 9.938 6.811C9.984 6.740 10.006 6.656 10.000 6.571C9.994 6.487 9.960 6.406 9.904 6.343Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(4.99 1)\"/></svg>";
    readonly depart_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M12.000 16.955C11.604 16.955 11.218 16.837 10.889 16.618C10.560 16.398 10.304 16.086 10.152 15.720C10.001 15.355 9.961 14.952 10.038 14.565C10.116 14.177 10.306 13.820 10.586 13.540C10.865 13.261 11.222 13.070 11.610 12.993C11.998 12.916 12.400 12.956 12.765 13.107C13.131 13.258 13.443 13.515 13.663 13.844C13.883 14.172 14.000 14.559 14.000 14.955C14.000 15.485 13.789 15.994 13.414 16.369C13.039 16.744 12.530 16.955 12.000 16.955Z M7.003 5.991C8.070 6.010 9.087 6.447 9.833 7.210C10.580 7.972 10.996 8.998 10.992 10.065C10.992 10.065 11.000 10.065 11.000 10.065C11.000 10.065 11.000 11.442 11.000 11.442C11.000 11.510 11.013 11.576 11.039 11.638C11.065 11.701 11.102 11.757 11.150 11.805C11.198 11.852 11.254 11.890 11.316 11.916C11.378 11.941 11.445 11.955 11.512 11.955C11.512 11.955 12.488 11.955 12.488 11.955C12.555 11.955 12.622 11.941 12.684 11.916C12.746 11.890 12.802 11.852 12.850 11.805C12.898 11.757 12.935 11.701 12.961 11.638C12.987 11.576 13.000 11.510 13.000 11.442C13.000 11.442 13.000 9.955 13.000 9.955C13.000 9.955 12.986 9.955 12.986 9.955C12.962 8.377 12.323 6.871 11.206 5.758C10.088 4.644 8.580 4.010 7.003 3.991C7.003 3.991 6.992 3.990 6.992 3.990C6.992 3.990 6.992 4.010 6.992 4.010C6.992 4.010 6.000 4.010 6.000 4.010C6.000 4.010 6.000 4.001 6.000 4.001C5.931 4.016 5.859 4.010 5.793 3.984C5.727 3.958 5.670 3.914 5.630 3.855C5.590 3.797 5.567 3.729 5.566 3.658C5.565 3.587 5.584 3.518 5.623 3.458C5.623 3.458 6.966 0.506 6.966 0.506C6.993 0.426 6.992 0.339 6.964 0.259C6.935 0.179 6.882 0.110 6.811 0.063C6.740 0.017 6.656 -0.005 6.571 0.001C6.487 0.007 6.406 0.041 6.343 0.097C6.343 0.097 0.000 5.001 0.000 5.001C0.000 5.001 6.343 9.904 6.343 9.904C6.406 9.960 6.487 9.994 6.571 10.000C6.656 10.006 6.740 9.984 6.811 9.938C6.882 9.891 6.935 9.823 6.964 9.743C6.992 9.662 6.993 9.575 6.966 9.495C6.966 9.495 5.623 6.543 5.623 6.543C5.584 6.483 5.565 6.414 5.566 6.343C5.567 6.272 5.590 6.204 5.630 6.146C5.670 6.088 5.727 6.043 5.793 6.017C5.859 5.991 5.931 5.985 6.000 6.001C6.000 6.001 6.000 6.010 6.000 6.010C6.000 6.010 7.000 6.010 7.000 6.010Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(3 2.045)\"/></svg>";
    readonly depart_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M0.000 14.955C0.000 14.559 0.117 14.172 0.337 13.844C0.557 13.515 0.869 13.258 1.235 13.107C1.600 12.956 2.002 12.916 2.390 12.993C2.778 13.070 3.135 13.261 3.414 13.540C3.694 13.820 3.884 14.177 3.962 14.565C4.039 14.952 3.999 15.355 3.848 15.720C3.696 16.086 3.440 16.398 3.111 16.618C2.782 16.837 2.396 16.955 2.000 16.955C1.470 16.955 0.961 16.744 0.586 16.369C0.211 15.994 0.000 15.485 0.000 14.955Z M7.000 6.010C7.000 6.010 8.000 6.010 8.000 6.010C8.000 6.010 8.000 6.001 8.000 6.001C8.069 5.985 8.141 5.991 8.207 6.017C8.273 6.043 8.330 6.088 8.370 6.146C8.410 6.204 8.433 6.272 8.434 6.343C8.435 6.414 8.416 6.483 8.377 6.543C8.377 6.543 7.034 9.495 7.034 9.495C7.007 9.575 7.008 9.662 7.036 9.743C7.065 9.823 7.118 9.891 7.189 9.938C7.260 9.984 7.344 10.006 7.429 10.000C7.513 9.994 7.594 9.960 7.657 9.904C7.657 9.904 14.000 5.001 14.000 5.001C14.000 5.001 7.657 0.097 7.657 0.097C7.594 0.041 7.513 0.007 7.429 0.001C7.344 -0.005 7.260 0.017 7.189 0.063C7.118 0.110 7.065 0.179 7.036 0.259C7.008 0.339 7.007 0.426 7.034 0.506C7.034 0.506 8.377 3.458 8.377 3.458C8.416 3.518 8.435 3.587 8.434 3.658C8.433 3.729 8.410 3.797 8.370 3.855C8.330 3.914 8.273 3.958 8.207 3.984C8.141 4.010 8.069 4.016 8.000 4.001C8.000 4.001 8.000 4.010 8.000 4.010C8.000 4.010 7.008 4.010 7.008 4.010C7.008 4.010 7.008 3.990 7.008 3.990C7.008 3.990 6.997 3.991 6.997 3.991C5.420 4.010 3.912 4.644 2.794 5.758C1.677 6.871 1.038 8.377 1.014 9.955C1.014 9.955 1.000 9.955 1.000 9.955C1.000 9.955 1.000 11.442 1.000 11.442C1.000 11.510 1.013 11.576 1.039 11.638C1.065 11.701 1.102 11.757 1.150 11.805C1.198 11.852 1.254 11.890 1.316 11.916C1.378 11.941 1.445 11.955 1.512 11.955C1.512 11.955 2.488 11.955 2.488 11.955C2.555 11.955 2.622 11.941 2.684 11.916C2.746 11.890 2.802 11.852 2.850 11.805C2.898 11.757 2.935 11.701 2.961 11.638C2.987 11.576 3.000 11.510 3.000 11.442C3.000 11.442 3.000 10.065 3.000 10.065C3.000 10.065 3.008 10.065 3.008 10.065C3.004 8.998 3.420 7.972 4.167 7.210C4.913 6.447 5.930 6.010 6.997 5.991Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(3 2.045)\"/></svg>";
    readonly depart_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M4.991 18.000C4.596 18.000 4.209 17.883 3.880 17.663C3.551 17.443 3.295 17.131 3.144 16.765C2.992 16.400 2.953 15.998 3.030 15.610C3.107 15.222 3.297 14.865 3.577 14.586C3.857 14.306 4.213 14.116 4.601 14.038C4.989 13.961 5.391 14.001 5.757 14.152C6.122 14.304 6.435 14.560 6.654 14.889C6.874 15.218 6.991 15.604 6.991 16.000C6.991 16.530 6.781 17.039 6.406 17.414C6.031 17.789 5.522 18.000 4.991 18.000Z M9.904 6.343C9.904 6.343 5.001 0.000 5.001 0.000C5.001 0.000 0.097 6.343 0.097 6.343C0.041 6.406 0.007 6.487 0.001 6.571C-0.005 6.656 0.017 6.740 0.063 6.811C0.110 6.882 0.179 6.935 0.259 6.964C0.339 6.992 0.426 6.993 0.506 6.966C0.506 6.966 3.458 5.623 3.458 5.623C3.517 5.585 3.585 5.565 3.655 5.566C3.725 5.567 3.793 5.588 3.851 5.628C3.909 5.667 3.954 5.723 3.980 5.787C4.006 5.852 4.013 5.923 3.999 5.992C3.999 5.992 3.991 5.992 3.991 5.992C3.991 5.992 3.991 11.492 3.991 11.492C3.991 11.557 4.004 11.622 4.029 11.683C4.055 11.744 4.091 11.799 4.138 11.845C4.184 11.892 4.239 11.929 4.300 11.954C4.361 11.979 4.426 11.992 4.491 11.992C4.491 11.992 5.491 11.992 5.491 11.992C5.557 11.992 5.622 11.979 5.683 11.954C5.743 11.929 5.799 11.892 5.845 11.845C5.891 11.799 5.928 11.744 5.953 11.683C5.978 11.622 5.991 11.557 5.991 11.492C5.991 11.492 5.991 6.000 5.991 6.000C5.991 6.000 6.001 6.000 6.001 6.000C5.985 5.931 5.991 5.859 6.017 5.793C6.043 5.727 6.088 5.670 6.146 5.630C6.204 5.590 6.272 5.567 6.343 5.566C6.414 5.565 6.483 5.584 6.543 5.623C6.543 5.623 9.495 6.966 9.495 6.966C9.575 6.993 9.662 6.992 9.743 6.964C9.823 6.935 9.891 6.882 9.938 6.811C9.984 6.740 10.006 6.656 10.000 6.571C9.994 6.487 9.960 6.406 9.904 6.343Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(5.009 1)\"/></svg>";
    readonly end_of_road_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M14.495 4.019C14.495 4.019 4.992 4.019 4.992 4.019C4.992 4.019 4.992 6.019 4.992 6.019C4.992 6.019 14.495 6.019 14.495 6.019C14.560 6.019 14.625 6.006 14.685 5.981C14.745 5.956 14.800 5.920 14.846 5.873C14.892 5.827 14.929 5.773 14.954 5.712C14.979 5.652 14.992 5.588 14.992 5.522C14.992 5.522 14.992 4.515 14.992 4.515C14.992 4.450 14.979 4.386 14.954 4.325C14.929 4.265 14.892 4.210 14.846 4.164C14.800 4.118 14.745 4.082 14.685 4.057C14.625 4.032 14.560 4.019 14.495 4.019Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(2.008 1.981)\"/><path d=\"M7.992 4.001C7.992 4.001 6.004 4.001 6.004 4.001C5.935 4.016 5.863 4.010 5.797 3.984C5.731 3.958 5.675 3.914 5.634 3.855C5.594 3.797 5.572 3.729 5.570 3.658C5.569 3.587 5.589 3.518 5.627 3.458C5.627 3.458 6.971 0.506 6.971 0.506C6.997 0.426 6.996 0.339 6.968 0.259C6.940 0.179 6.886 0.110 6.815 0.063C6.744 0.017 6.660 -0.005 6.575 0.001C6.491 0.007 6.411 0.041 6.347 0.097C6.347 0.097 0.000 5.019 0.000 5.019C0.000 5.019 6.347 9.904 6.347 9.904C6.411 9.960 6.491 9.994 6.575 10.000C6.660 10.006 6.744 9.984 6.815 9.938C6.886 9.891 6.940 9.823 6.968 9.743C6.996 9.663 6.997 9.575 6.971 9.495C6.971 9.495 5.627 6.543 5.627 6.543C5.589 6.483 5.569 6.414 5.570 6.343C5.572 6.272 5.594 6.204 5.634 6.146C5.675 6.088 5.731 6.043 5.797 6.017C5.863 5.991 5.935 5.985 6.004 6.001C6.004 6.001 8.003 6.001 8.003 6.001C9.070 6.019 10.087 6.456 10.833 7.219C11.580 7.981 11.996 9.007 11.992 10.074C11.992 10.074 11.992 15.523 11.992 15.523C11.992 15.654 12.044 15.780 12.137 15.874C12.230 15.967 12.356 16.019 12.488 16.019C12.488 16.019 13.494 16.019 13.494 16.019C13.560 16.019 13.624 16.006 13.684 15.981C13.745 15.956 13.799 15.919 13.845 15.873C13.892 15.827 13.928 15.772 13.953 15.712C13.978 15.652 13.991 15.587 13.991 15.522C13.991 15.522 13.989 14.019 13.989 14.019C13.989 14.019 13.992 14.019 13.992 14.019C13.992 14.019 13.992 10.074 13.992 10.074C13.995 8.477 13.368 6.942 12.247 5.805C11.125 4.668 9.600 4.019 8.003 4.001C8.003 4.001 7.992 3.999 7.992 3.999Z\" fill=\"currentColor\" transform=\"translate(2.008 1.981)\"/></svg>";
    readonly end_of_road_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M11.000 4.019C11.000 4.019 0.501 4.019 0.501 4.019C0.368 4.019 0.241 4.072 0.147 4.166C0.053 4.260 0.000 4.387 0.000 4.520C0.000 4.520 0.000 5.518 0.000 5.518C0.000 5.651 0.053 5.778 0.147 5.872C0.241 5.966 0.368 6.019 0.501 6.019C0.501 6.019 11.000 6.019 11.000 6.019C11.000 6.019 11.000 4.019 11.000 4.019Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(2 1.981)\"/><path d=\"M8.000 6.001C8.000 6.001 9.988 6.001 9.988 6.001C10.057 5.985 10.129 5.991 10.195 6.017C10.260 6.043 10.317 6.088 10.358 6.146C10.398 6.204 10.420 6.272 10.422 6.343C10.423 6.414 10.403 6.483 10.365 6.543C10.365 6.543 9.021 9.495 9.021 9.495C8.995 9.576 8.996 9.663 9.024 9.743C9.052 9.823 9.106 9.891 9.177 9.938C9.247 9.984 9.332 10.006 9.416 10.000C9.501 9.994 9.581 9.960 9.645 9.904C9.645 9.904 15.992 5.019 15.992 5.019C15.992 5.019 9.645 0.097 9.645 0.097C9.581 0.041 9.501 0.007 9.416 0.001C9.332 -0.005 9.247 0.017 9.177 0.063C9.106 0.110 9.052 0.179 9.024 0.259C8.996 0.339 8.995 0.426 9.021 0.506C9.021 0.506 10.365 3.458 10.365 3.458C10.403 3.518 10.423 3.587 10.421 3.658C10.420 3.729 10.398 3.797 10.357 3.855C10.317 3.914 10.260 3.958 10.195 3.984C10.129 4.010 10.057 4.016 9.988 4.001C9.988 4.001 8.000 4.001 8.000 4.001C8.000 4.001 8.000 3.999 8.000 3.999C8.000 3.999 7.989 4.001 7.989 4.001C6.392 4.019 4.867 4.668 3.745 5.805C2.624 6.942 1.996 8.477 2.000 10.074C2.000 10.074 2.000 14.019 2.000 14.019C2.000 14.019 2.003 14.019 2.003 14.019C2.003 14.019 2.001 15.522 2.001 15.522C2.001 15.587 2.014 15.652 2.039 15.712C2.064 15.772 2.100 15.827 2.146 15.873C2.192 15.919 2.247 15.956 2.307 15.981C2.368 16.006 2.432 16.019 2.497 16.019C2.497 16.019 3.504 16.019 3.504 16.019C3.635 16.019 3.762 15.967 3.855 15.874C3.948 15.780 4.000 15.654 4.000 15.523C4.000 15.523 4.000 10.074 4.000 10.074C3.996 9.007 4.412 7.981 5.159 7.219C5.905 6.456 6.922 6.019 7.989 6.001C7.989 6.001 8.000 6.001 8.000 6.001Z\" fill=\"currentColor\" transform=\"translate(2 1.981)\"/></svg>";
    readonly ferry: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M2.6 16.2 C4.1 14.9 5.9 14.9 7.4 16.2 C8.9 17.5 11.1 17.5 12.6 16.2 C14.1 14.9 15.9 14.9 17.4 16.2\" fill=\"none\" stroke=\"currentColor\" stroke-opacity=\"0.35\" stroke-width=\"1.8\" stroke-linecap=\"round\"/><path d=\"M6.7 3.6 H13.3 C13.6 3.6 13.8 3.8 13.8 4.1 V7 H6.2 V4.1 C6.2 3.8 6.4 3.6 6.7 3.6 Z\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"0.7\" stroke-linejoin=\"round\"/><path d=\"M3.4 8.2 H16.6 C17 8.2 17.2 8.6 17 9 L15.3 12.2 C15 12.8 14.4 13.2 13.7 13.2 H6.3 C5.6 13.2 5 12.8 4.7 12.2 L3 9 C2.8 8.6 3 8.2 3.4 8.2 Z\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"0.7\" stroke-linejoin=\"round\"/></svg>";
    readonly flag: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M0.500 15.000C0.367 15.000 0.240 14.947 0.146 14.854C0.053 14.760 0.000 14.633 0.000 14.500C0.000 14.500 0.000 0.500 0.000 0.500C0.000 0.367 0.053 0.240 0.146 0.146C0.240 0.053 0.367 -0.000 0.500 0.000C0.633 0.000 0.760 0.053 0.854 0.146C0.947 0.240 1.000 0.367 1.000 0.500C1.000 0.500 1.000 14.500 1.000 14.500C1.000 14.633 0.947 14.760 0.854 14.854C0.760 14.947 0.633 15.000 0.500 15.000Z M10.333 4.000C10.333 4.000 14.000 0.000 14.000 0.000C14.000 0.000 3.000 0.000 3.000 0.000C3.000 0.000 3.000 8.000 3.000 8.000C3.000 8.000 14.000 8.000 14.000 8.000C14.000 8.000 10.333 4.000 10.333 4.000Z\" fill=\"currentColor\" fill-rule=\"evenodd\" transform=\"translate(3 3)\"/></svg>";
    readonly fork: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M18.000 0.000C18.000 0.000 16.420 7.860 16.420 7.860C16.408 7.944 16.370 8.022 16.310 8.082C16.250 8.142 16.172 8.181 16.088 8.192C16.004 8.203 15.918 8.186 15.845 8.144C15.771 8.102 15.713 8.037 15.680 7.959C15.680 7.959 14.762 4.849 14.762 4.849C14.752 4.778 14.721 4.713 14.674 4.660C14.627 4.608 14.565 4.571 14.496 4.553C14.428 4.536 14.355 4.539 14.289 4.563C14.222 4.586 14.164 4.629 14.122 4.686C14.122 4.686 11.673 6.751 11.673 6.751C11.106 7.276 10.663 7.920 10.375 8.636C10.087 9.353 9.962 10.125 10.008 10.895C10.008 10.895 10.013 14.503 10.013 14.503C10.013 14.568 10.000 14.633 9.975 14.693C9.951 14.754 9.914 14.808 9.868 14.854C9.822 14.901 9.767 14.937 9.707 14.962C9.647 14.987 9.582 15.000 9.517 15.000C9.517 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.183 5.720 8.640 6.275 9.000 6.891C9.360 6.275 9.817 5.720 10.354 5.249C10.354 5.249 12.811 3.175 12.811 3.175C12.874 3.141 12.924 3.089 12.957 3.027C12.990 2.964 13.003 2.893 12.996 2.823C12.988 2.752 12.960 2.686 12.915 2.631C12.870 2.577 12.809 2.538 12.741 2.518C12.741 2.518 9.792 1.167 9.792 1.167C9.720 1.124 9.663 1.057 9.632 0.978C9.601 0.900 9.597 0.813 9.620 0.731C9.643 0.649 9.692 0.578 9.760 0.527C9.828 0.476 9.910 0.449 9.995 0.450C9.995 0.450 18.000 0.000 18.000 0.000Z\" fill=\"currentColor\" transform=\"translate(1 3)\"/></svg>";
    readonly fork_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M16.727 4.396C16.608 4.396 16.493 4.438 16.402 4.515C16.402 4.515 14.325 6.267 14.325 6.267C13.528 6.998 12.904 7.897 12.499 8.900C12.094 9.902 11.918 10.983 11.985 12.062C11.985 12.062 11.985 15.523 11.985 15.523C11.985 15.654 12.037 15.780 12.130 15.874C12.223 15.967 12.350 16.019 12.481 16.019C12.481 16.019 13.488 16.019 13.488 16.019C13.553 16.019 13.618 16.006 13.678 15.981C13.738 15.956 13.793 15.919 13.839 15.873C13.885 15.827 13.921 15.772 13.946 15.712C13.971 15.652 13.984 15.587 13.984 15.522C13.984 15.522 13.979 11.914 13.979 11.914C13.933 11.143 14.058 10.372 14.346 9.655C14.634 8.939 15.077 8.295 15.644 7.770C15.644 7.770 17.702 6.034 17.702 6.034C17.753 5.991 17.795 5.939 17.825 5.879C17.856 5.820 17.874 5.755 17.879 5.688C17.884 5.621 17.876 5.554 17.855 5.491C17.835 5.428 17.801 5.369 17.757 5.318C17.757 5.318 17.108 4.570 17.108 4.570C17.061 4.515 17.003 4.471 16.937 4.441C16.871 4.411 16.799 4.396 16.727 4.396Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(1.009 1.981)\"/><path d=\"M7.992 4.001C7.992 4.001 6.004 4.001 6.004 4.001C5.935 4.016 5.863 4.010 5.797 3.984C5.731 3.958 5.675 3.914 5.634 3.855C5.594 3.797 5.572 3.729 5.570 3.658C5.569 3.587 5.589 3.518 5.627 3.458C5.627 3.458 6.971 0.506 6.971 0.506C6.997 0.426 6.996 0.339 6.968 0.259C6.940 0.179 6.886 0.110 6.815 0.063C6.744 0.017 6.660 -0.005 6.575 0.001C6.491 0.007 6.411 0.041 6.347 0.097C6.347 0.097 0.000 5.019 0.000 5.019C0.000 5.019 6.347 9.904 6.347 9.904C6.411 9.960 6.491 9.994 6.575 10.000C6.660 10.006 6.744 9.984 6.815 9.938C6.886 9.891 6.940 9.823 6.968 9.743C6.996 9.663 6.997 9.576 6.971 9.495C6.971 9.495 5.627 6.543 5.627 6.543C5.589 6.483 5.569 6.414 5.570 6.343C5.572 6.273 5.594 6.204 5.634 6.146C5.675 6.088 5.731 6.043 5.797 6.017C5.863 5.991 5.935 5.985 6.004 6.001C6.004 6.001 8.003 6.001 8.003 6.001C9.070 6.019 10.087 6.456 10.833 7.219C11.580 7.981 11.996 9.007 11.992 10.074C11.992 10.074 11.992 15.523 11.992 15.523C11.992 15.588 12.005 15.652 12.029 15.713C12.054 15.773 12.091 15.827 12.137 15.874C12.183 15.920 12.238 15.956 12.298 15.981C12.358 16.006 12.423 16.019 12.488 16.019C12.488 16.019 13.494 16.019 13.494 16.019C13.560 16.019 13.624 16.006 13.684 15.981C13.745 15.956 13.799 15.919 13.845 15.873C13.892 15.827 13.928 15.772 13.953 15.712C13.978 15.652 13.991 15.587 13.991 15.522C13.991 15.522 13.989 14.019 13.989 14.019C13.989 14.019 13.992 14.019 13.992 14.019C13.992 14.019 13.992 10.074 13.992 10.074C13.995 8.477 13.368 6.942 12.247 5.805C11.125 4.668 9.600 4.019 8.003 4.001C8.003 4.001 7.992 3.999 7.992 3.999Z\" fill=\"currentColor\" transform=\"translate(1.009 1.981)\"/></svg>";
    readonly fork_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M1.154 4.396C1.081 4.396 1.010 4.411 0.944 4.441C0.878 4.471 0.820 4.515 0.772 4.570C0.772 4.570 0.123 5.318 0.123 5.318C0.080 5.369 0.046 5.428 0.025 5.491C0.004 5.554 -0.004 5.621 0.002 5.688C0.007 5.755 0.025 5.820 0.056 5.879C0.086 5.939 0.128 5.991 0.179 6.034C0.179 6.034 2.237 7.770 2.237 7.770C2.803 8.295 3.247 8.939 3.535 9.655C3.823 10.372 3.948 11.143 3.902 11.914C3.902 11.914 3.897 15.522 3.897 15.522C3.897 15.587 3.910 15.652 3.934 15.712C3.959 15.772 3.996 15.827 4.042 15.873C4.088 15.919 4.143 15.956 4.203 15.981C4.263 16.006 4.328 16.019 4.393 16.019C4.393 16.019 5.400 16.019 5.400 16.019C5.531 16.019 5.657 15.967 5.750 15.874C5.843 15.780 5.896 15.654 5.896 15.523C5.896 15.523 5.896 12.062 5.896 12.062C5.962 10.983 5.787 9.902 5.382 8.900C4.977 7.897 4.353 6.998 3.556 6.267C3.556 6.267 1.479 4.515 1.479 4.515C1.388 4.438 1.273 4.396 1.154 4.396Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(1.12 1.981)\"/><path d=\"M9.888 6.001C9.888 6.001 11.876 6.001 11.876 6.001C11.945 5.985 12.017 5.991 12.083 6.017C12.149 6.043 12.205 6.088 12.246 6.146C12.286 6.204 12.308 6.272 12.310 6.343C12.311 6.414 12.291 6.483 12.253 6.543C12.253 6.543 10.909 9.495 10.909 9.495C10.883 9.576 10.884 9.663 10.912 9.743C10.940 9.823 10.994 9.891 11.065 9.938C11.136 9.984 11.220 10.006 11.304 10.000C11.389 9.994 11.469 9.960 11.533 9.904C11.533 9.904 17.880 5.019 17.880 5.019C17.880 5.019 11.533 0.097 11.533 0.097C11.469 0.041 11.389 0.007 11.304 0.001C11.220 -0.005 11.136 0.017 11.065 0.063C10.994 0.110 10.940 0.179 10.912 0.259C10.884 0.339 10.883 0.426 10.909 0.506C10.909 0.506 12.253 3.458 12.253 3.458C12.291 3.518 12.311 3.587 12.310 3.658C12.308 3.729 12.286 3.797 12.246 3.855C12.205 3.914 12.149 3.958 12.083 3.984C12.017 4.010 11.945 4.016 11.876 4.001C11.876 4.001 9.888 4.001 9.888 4.001C9.888 4.001 9.888 3.999 9.888 3.999C9.888 3.999 9.877 4.001 9.877 4.001C8.280 4.019 6.755 4.668 5.633 5.805C4.512 6.942 3.885 8.477 3.888 10.074C3.888 10.074 3.888 14.019 3.888 14.019C3.888 14.019 3.891 14.019 3.891 14.019C3.891 14.019 3.889 15.522 3.889 15.522C3.889 15.587 3.902 15.652 3.927 15.712C3.952 15.772 3.988 15.827 4.034 15.873C4.080 15.919 4.135 15.956 4.195 15.981C4.256 16.006 4.320 16.019 4.386 16.019C4.386 16.019 5.392 16.019 5.392 16.019C5.524 16.019 5.650 15.967 5.743 15.874C5.836 15.780 5.888 15.654 5.888 15.523C5.888 15.523 5.888 10.074 5.888 10.074C5.884 9.007 6.300 7.981 7.047 7.219C7.793 6.456 8.810 6.019 9.877 6.001C9.877 6.001 9.888 6.001 9.888 6.001Z\" fill=\"currentColor\" transform=\"translate(1.12 1.981)\"/></svg>";
    readonly fork_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M12.756 3.377C12.637 3.377 12.522 3.419 12.431 3.496C12.431 3.496 10.354 5.249 10.354 5.249C9.557 5.979 8.933 6.878 8.528 7.881C8.123 8.883 7.947 9.964 8.014 11.043C8.014 11.043 8.014 14.504 8.014 14.504C8.014 14.635 8.066 14.762 8.159 14.855C8.253 14.948 8.379 15.000 8.510 15.000C8.510 15.000 9.517 15.000 9.517 15.000C9.582 15.000 9.647 14.987 9.707 14.962C9.767 14.937 9.822 14.901 9.868 14.854C9.914 14.808 9.950 14.754 9.975 14.693C10.000 14.633 10.013 14.568 10.013 14.503C10.013 14.503 10.008 10.896 10.008 10.896C9.962 10.125 10.087 9.353 10.375 8.636C10.663 7.920 11.106 7.276 11.673 6.751C11.673 6.751 13.731 5.016 13.731 5.016C13.782 4.973 13.824 4.920 13.854 4.860C13.885 4.801 13.903 4.736 13.908 4.669C13.913 4.603 13.905 4.536 13.884 4.472C13.864 4.409 13.830 4.350 13.787 4.299C13.787 4.299 13.138 3.551 13.138 3.551C13.090 3.496 13.032 3.452 12.966 3.422C12.900 3.392 12.828 3.377 12.756 3.377Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(3 3)\"/><path d=\"M7.646 5.249C7.646 5.249 5.189 3.175 5.189 3.175C5.126 3.141 5.076 3.089 5.043 3.027C5.010 2.964 4.997 2.893 5.004 2.823C5.012 2.752 5.040 2.686 5.085 2.631C5.130 2.577 5.191 2.538 5.259 2.518C5.259 2.518 8.208 1.167 8.208 1.167C8.280 1.124 8.337 1.057 8.368 0.978C8.399 0.900 8.403 0.813 8.380 0.731C8.357 0.649 8.308 0.578 8.240 0.527C8.172 0.476 8.090 0.449 8.005 0.450C8.005 0.450 0.000 0.000 0.000 0.000C0.000 0.000 1.580 7.860 1.580 7.860C1.592 7.944 1.630 8.022 1.690 8.082C1.750 8.142 1.828 8.181 1.912 8.192C1.996 8.203 2.082 8.186 2.155 8.144C2.229 8.102 2.287 8.037 2.320 7.959C2.320 7.959 3.238 4.849 3.238 4.849C3.248 4.778 3.279 4.713 3.326 4.660C3.373 4.608 3.435 4.571 3.504 4.553C3.572 4.536 3.645 4.539 3.711 4.563C3.778 4.586 3.836 4.629 3.878 4.686C3.878 4.686 6.327 6.751 6.327 6.751C6.894 7.276 7.337 7.920 7.625 8.636C7.913 9.353 8.038 10.125 7.992 10.895C7.992 10.895 7.987 14.503 7.987 14.503C7.987 14.568 8.000 14.633 8.025 14.693C8.049 14.754 8.086 14.808 8.132 14.854C8.178 14.901 8.233 14.937 8.293 14.962C8.353 14.987 8.418 15.000 8.483 15.000C8.483 15.000 9.490 15.000 9.490 15.000C9.621 15.000 9.748 14.948 9.841 14.855C9.934 14.762 9.986 14.635 9.986 14.504C9.986 14.504 9.986 11.043 9.986 11.043C10.053 9.964 9.877 8.883 9.472 7.881C9.067 6.878 8.443 5.979 7.646 5.249Z\" fill=\"currentColor\" transform=\"translate(3 3)\"/></svg>";
    readonly fork_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M1.154 3.377C1.081 3.377 1.010 3.392 0.944 3.422C0.878 3.452 0.820 3.496 0.772 3.551C0.772 3.551 0.123 4.299 0.123 4.299C0.080 4.350 0.046 4.409 0.025 4.472C0.004 4.536 -0.004 4.603 0.002 4.669C0.007 4.736 0.025 4.801 0.056 4.860C0.086 4.920 0.128 4.973 0.179 5.016C0.179 5.016 2.237 6.751 2.237 6.751C2.803 7.276 3.247 7.920 3.535 8.636C3.823 9.353 3.948 10.125 3.902 10.896C3.902 10.896 3.897 14.503 3.897 14.503C3.897 14.568 3.910 14.633 3.934 14.693C3.959 14.754 3.996 14.808 4.042 14.854C4.088 14.901 4.143 14.937 4.203 14.962C4.263 14.987 4.328 15.000 4.393 15.000C4.393 15.000 5.400 15.000 5.400 15.000C5.531 15.000 5.657 14.948 5.750 14.855C5.843 14.762 5.896 14.635 5.896 14.504C5.896 14.504 5.896 11.043 5.896 11.043C5.962 9.964 5.787 8.883 5.382 7.881C4.977 6.878 4.353 5.979 3.556 5.249C3.556 5.249 1.479 3.496 1.479 3.496C1.388 3.419 1.273 3.377 1.154 3.377Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(3.115 3)\"/><path d=\"M3.899 11.043C3.899 11.043 3.899 14.504 3.899 14.504C3.899 14.635 3.952 14.762 4.045 14.855C4.138 14.948 4.264 15.000 4.395 15.000C4.395 15.000 5.402 15.000 5.402 15.000C5.467 15.000 5.532 14.987 5.592 14.962C5.652 14.937 5.707 14.901 5.753 14.854C5.799 14.808 5.836 14.754 5.861 14.693C5.885 14.633 5.898 14.568 5.898 14.503C5.898 14.503 5.893 10.896 5.893 10.896C5.847 10.125 5.972 9.353 6.260 8.636C6.548 7.920 6.991 7.276 7.558 6.752C7.558 6.752 10.007 4.686 10.007 4.686C10.049 4.629 10.107 4.586 10.174 4.563C10.240 4.539 10.313 4.536 10.381 4.553C10.450 4.571 10.512 4.608 10.559 4.661C10.606 4.713 10.637 4.779 10.647 4.849C10.647 4.849 11.565 7.959 11.565 7.959C11.598 8.037 11.656 8.102 11.730 8.145C11.803 8.187 11.889 8.203 11.973 8.192C12.057 8.181 12.135 8.142 12.195 8.082C12.255 8.022 12.293 7.944 12.305 7.860C12.305 7.860 13.885 0.000 13.885 0.000C13.885 0.000 5.880 0.450 5.880 0.450C5.796 0.449 5.713 0.476 5.645 0.527C5.577 0.578 5.528 0.649 5.505 0.731C5.482 0.813 5.486 0.900 5.517 0.978C5.549 1.057 5.605 1.124 5.677 1.167C5.677 1.167 8.626 2.518 8.626 2.518C8.694 2.538 8.755 2.577 8.800 2.631C8.845 2.686 8.874 2.752 8.881 2.823C8.889 2.893 8.875 2.964 8.842 3.027C8.809 3.089 8.759 3.141 8.697 3.175C8.697 3.175 6.239 5.249 6.239 5.249C5.442 5.979 4.818 6.878 4.413 7.881C4.008 8.883 3.833 9.964 3.899 11.043Z\" fill=\"currentColor\" transform=\"translate(3.115 3)\"/></svg>";
    readonly fork_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M16.666 4.398C16.547 4.398 16.432 4.440 16.341 4.517C16.341 4.517 14.264 6.269 14.264 6.269C13.466 7.000 12.843 7.899 12.438 8.902C12.033 9.904 11.857 10.985 11.924 12.064C11.924 12.064 11.924 15.525 11.924 15.525C11.924 15.656 11.976 15.782 12.069 15.875C12.162 15.969 12.289 16.021 12.420 16.021C12.420 16.021 13.427 16.021 13.427 16.021C13.492 16.021 13.556 16.008 13.617 15.983C13.677 15.958 13.732 15.921 13.778 15.875C13.824 15.829 13.860 15.774 13.885 15.714C13.910 15.654 13.923 15.589 13.923 15.524C13.923 15.524 13.918 11.916 13.918 11.916C13.872 11.145 13.997 10.374 14.285 9.657C14.573 8.941 15.016 8.297 15.583 7.772C15.583 7.772 17.640 6.036 17.640 6.036C17.692 5.993 17.734 5.941 17.764 5.881C17.795 5.822 17.813 5.757 17.818 5.690C17.823 5.623 17.815 5.556 17.794 5.493C17.773 5.429 17.740 5.371 17.696 5.320C17.696 5.320 17.047 4.572 17.047 4.572C17.000 4.517 16.941 4.473 16.876 4.443C16.810 4.413 16.738 4.398 16.666 4.398Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(1.09 2)\"/><path class=\"mm2\" d=\"M1.154 4.377C1.081 4.377 1.010 4.392 0.944 4.422C0.878 4.452 0.820 4.496 0.772 4.551C0.772 4.551 0.123 5.299 0.123 5.299C0.080 5.350 0.046 5.409 0.025 5.472C0.004 5.536 -0.004 5.603 0.002 5.669C0.007 5.736 0.025 5.801 0.056 5.860C0.086 5.920 0.128 5.973 0.179 6.016C0.179 6.016 2.237 7.751 2.237 7.751C2.803 8.276 3.247 8.920 3.535 9.636C3.823 10.353 3.948 11.125 3.902 11.896C3.902 11.896 3.897 15.503 3.897 15.503C3.897 15.568 3.910 15.633 3.934 15.693C3.959 15.754 3.996 15.808 4.042 15.854C4.088 15.901 4.143 15.937 4.203 15.962C4.263 15.987 4.328 16.000 4.393 16.000C4.393 16.000 5.400 16.000 5.400 16.000C5.531 16.000 5.657 15.948 5.750 15.855C5.843 15.762 5.896 15.635 5.896 15.504C5.896 15.504 5.896 12.043 5.896 12.043C5.962 10.964 5.787 9.883 5.382 8.881C4.977 7.878 4.353 6.979 3.556 6.249C3.556 6.249 1.479 4.496 1.479 4.496C1.388 4.419 1.273 4.377 1.154 4.377Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(1.09 2)\"/><path d=\"M13.823 6.343C13.823 6.343 8.919 0.000 8.919 0.000C8.919 0.000 4.015 6.343 4.015 6.343C3.959 6.406 3.925 6.487 3.919 6.571C3.913 6.656 3.935 6.740 3.982 6.811C4.028 6.882 4.097 6.935 4.177 6.964C4.257 6.992 4.344 6.993 4.425 6.966C4.425 6.966 7.377 5.623 7.377 5.623C7.436 5.584 7.506 5.565 7.576 5.566C7.647 5.567 7.716 5.590 7.774 5.630C7.832 5.670 7.877 5.727 7.903 5.793C7.928 5.859 7.934 5.931 7.919 6.000C7.919 6.000 7.919 15.497 7.919 15.497C7.919 15.563 7.932 15.628 7.957 15.689C7.983 15.750 8.020 15.806 8.066 15.853C8.113 15.899 8.169 15.936 8.230 15.962C8.291 15.987 8.356 16.000 8.422 16.000C8.422 16.000 9.415 16.000 9.415 16.000C9.482 16.000 9.547 15.987 9.608 15.962C9.669 15.936 9.725 15.899 9.771 15.853C9.818 15.806 9.855 15.750 9.881 15.689C9.906 15.628 9.919 15.563 9.919 15.497C9.919 15.497 9.919 6.000 9.919 6.000C9.904 5.931 9.909 5.859 9.935 5.793C9.961 5.727 10.006 5.670 10.064 5.630C10.122 5.590 10.191 5.567 10.262 5.566C10.332 5.565 10.402 5.584 10.461 5.623C10.461 5.623 13.413 6.966 13.413 6.966C13.494 6.993 13.581 6.992 13.661 6.964C13.741 6.935 13.810 6.882 13.856 6.811C13.903 6.740 13.925 6.656 13.919 6.571C13.912 6.487 13.879 6.406 13.823 6.343Z\" fill=\"currentColor\" transform=\"translate(1.09 2)\"/></svg>";
    readonly invalid: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly invalid_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M7.992 3.999C7.992 3.999 8.003 4.001 8.003 4.001C9.600 4.019 11.125 4.668 12.247 5.805C13.368 6.942 13.995 8.477 13.992 10.074C13.992 10.074 13.992 14.019 13.992 14.019C13.992 14.019 13.989 14.019 13.989 14.019C13.989 14.019 13.991 15.522 13.991 15.522C13.991 15.587 13.978 15.652 13.953 15.712C13.928 15.772 13.892 15.827 13.845 15.873C13.799 15.919 13.745 15.956 13.684 15.981C13.624 16.006 13.560 16.019 13.494 16.019C13.494 16.019 12.488 16.019 12.488 16.019C12.356 16.019 12.230 15.967 12.137 15.874C12.044 15.780 11.992 15.654 11.992 15.523C11.992 15.523 11.992 10.074 11.992 10.074C11.996 9.007 11.580 7.981 10.833 7.219C10.087 6.456 9.070 6.019 8.003 6.001C8.003 6.001 6.004 6.001 6.004 6.001C5.935 5.985 5.863 5.991 5.797 6.017C5.731 6.043 5.675 6.088 5.634 6.146C5.594 6.204 5.572 6.272 5.570 6.343C5.569 6.414 5.589 6.483 5.627 6.543C5.627 6.543 6.971 9.495 6.971 9.495C6.997 9.576 6.996 9.663 6.968 9.743C6.940 9.823 6.886 9.891 6.815 9.938C6.744 9.984 6.660 10.006 6.575 10.000C6.491 9.994 6.411 9.960 6.347 9.904C6.347 9.904 0.000 5.019 0.000 5.019C0.000 5.019 6.347 0.097 6.347 0.097C6.411 0.041 6.491 0.007 6.575 0.001C6.660 -0.005 6.744 0.017 6.815 0.063C6.886 0.110 6.940 0.179 6.968 0.259C6.996 0.339 6.997 0.426 6.971 0.506C6.971 0.506 5.627 3.458 5.627 3.458C5.589 3.518 5.569 3.587 5.570 3.658C5.572 3.729 5.594 3.797 5.634 3.855C5.675 3.914 5.731 3.958 5.797 3.984C5.863 4.010 5.935 4.016 6.004 4.001C6.004 4.001 7.992 4.001 7.992 4.001Z\" fill=\"currentColor\" transform=\"translate(2.008 1.981)\"/></svg>";
    readonly invalid_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M5.989 6.001C4.922 6.019 3.905 6.456 3.159 7.219C2.412 7.981 1.996 9.007 2.000 10.074C2.000 10.074 2.000 15.523 2.000 15.523C2.000 15.654 1.948 15.780 1.855 15.874C1.762 15.967 1.635 16.019 1.504 16.019C1.504 16.019 0.497 16.019 0.497 16.019C0.432 16.019 0.368 16.006 0.307 15.981C0.247 15.956 0.192 15.919 0.146 15.873C0.100 15.827 0.064 15.772 0.039 15.712C0.014 15.652 0.001 15.587 0.001 15.522C0.001 15.522 0.003 14.019 0.003 14.019C0.003 14.019 0.000 14.019 0.000 14.019C0.000 14.019 0.000 10.074 0.000 10.074C-0.004 8.477 0.624 6.942 1.745 5.805C2.867 4.668 4.392 4.019 5.989 4.001C5.989 4.001 6.000 3.999 6.000 3.999C6.000 3.999 6.000 4.001 6.000 4.001C6.000 4.001 7.988 4.001 7.988 4.001C8.057 4.016 8.129 4.010 8.195 3.984C8.260 3.958 8.317 3.914 8.358 3.855C8.398 3.797 8.420 3.729 8.422 3.658C8.423 3.587 8.403 3.518 8.365 3.458C8.365 3.458 7.021 0.506 7.021 0.506C6.995 0.426 6.996 0.339 7.024 0.259C7.052 0.179 7.106 0.110 7.177 0.063C7.247 0.017 7.332 -0.005 7.416 0.001C7.501 0.007 7.581 0.041 7.645 0.097C7.645 0.097 13.992 5.019 13.992 5.019C13.992 5.019 7.645 9.904 7.645 9.904C7.581 9.960 7.501 9.994 7.416 10.000C7.332 10.006 7.247 9.984 7.177 9.938C7.106 9.891 7.052 9.823 7.024 9.743C6.996 9.663 6.995 9.576 7.021 9.495C7.021 9.495 8.365 6.543 8.365 6.543C8.403 6.483 8.423 6.414 8.422 6.343C8.420 6.272 8.398 6.204 8.358 6.146C8.317 6.088 8.260 6.043 8.195 6.017C8.129 5.991 8.057 5.985 7.988 6.001C7.988 6.001 5.989 6.001 5.989 6.001Z\" fill=\"currentColor\" transform=\"translate(4 1.981)\"/></svg>";
    readonly invalid_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.986 11.043C9.986 11.043 9.986 14.504 9.986 14.504C9.986 14.635 9.934 14.762 9.841 14.855C9.748 14.948 9.621 15.000 9.490 15.000C9.490 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.443 5.979 9.067 6.878 9.472 7.881C9.877 8.883 10.053 9.964 9.986 11.043Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly invalid_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.354 5.249C2.354 5.249 4.811 3.175 4.811 3.175C4.873 3.141 4.924 3.089 4.957 3.027C4.990 2.964 5.003 2.893 4.996 2.823C4.988 2.752 4.960 2.686 4.915 2.631C4.869 2.577 4.809 2.538 4.741 2.518C4.741 2.518 1.792 1.167 1.792 1.167C1.719 1.124 1.663 1.057 1.632 0.978C1.601 0.900 1.596 0.813 1.619 0.731C1.642 0.649 1.692 0.578 1.760 0.527C1.827 0.476 1.910 0.449 1.995 0.450C1.995 0.450 10.000 0.000 10.000 0.000C10.000 0.000 8.419 7.860 8.419 7.860C8.408 7.944 8.369 8.022 8.309 8.082C8.249 8.142 8.171 8.181 8.087 8.192C8.003 8.203 7.918 8.186 7.844 8.144C7.771 8.102 7.713 8.037 7.680 7.959C7.680 7.959 6.762 4.849 6.762 4.849C6.752 4.778 6.721 4.713 6.674 4.660C6.626 4.608 6.564 4.571 6.496 4.553C6.427 4.536 6.355 4.539 6.288 4.563C6.222 4.586 6.163 4.629 6.121 4.686C6.121 4.686 3.673 6.751 3.673 6.751C3.106 7.276 2.663 7.920 2.375 8.636C2.087 9.353 1.962 10.125 2.008 10.895C2.008 10.895 2.013 14.503 2.013 14.503C2.013 14.568 2.000 14.633 1.975 14.693C1.950 14.754 1.914 14.808 1.868 14.854C1.821 14.901 1.767 14.937 1.707 14.962C1.646 14.987 1.582 15.000 1.516 15.000C1.516 15.000 0.510 15.000 0.510 15.000C0.378 15.000 0.252 14.948 0.159 14.855C0.066 14.762 0.014 14.635 0.014 14.504C0.014 14.504 0.014 11.043 0.014 11.043C-0.053 9.964 0.123 8.883 0.528 7.881C0.932 6.878 1.556 5.979 2.354 5.249Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly invalid_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly invalid_uturn: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M15.010 5.500C15.010 5.500 15.010 14.997 15.010 14.997C15.010 15.063 14.997 15.128 14.971 15.189C14.946 15.250 14.909 15.306 14.862 15.353C14.815 15.399 14.760 15.436 14.699 15.462C14.638 15.487 14.572 15.500 14.506 15.500C14.506 15.500 13.513 15.500 13.513 15.500C13.447 15.500 13.382 15.487 13.321 15.462C13.259 15.436 13.204 15.399 13.157 15.353C13.110 15.306 13.073 15.250 13.048 15.189C13.023 15.128 13.010 15.063 13.010 14.997C13.010 14.997 13.010 5.500 13.010 5.500C13.010 4.572 12.641 3.682 11.985 3.025C11.328 2.369 10.438 2.000 9.510 2.000C8.581 2.000 7.691 2.369 7.035 3.025C6.378 3.682 6.010 4.572 6.010 5.500C6.010 5.500 6.010 9.500 6.010 9.500C6.010 9.500 6.001 9.500 6.001 9.500C5.985 9.569 5.991 9.641 6.017 9.707C6.043 9.773 6.088 9.830 6.146 9.870C6.204 9.910 6.272 9.933 6.343 9.934C6.414 9.935 6.483 9.916 6.543 9.877C6.543 9.877 9.495 8.534 9.495 8.534C9.575 8.507 9.663 8.508 9.743 8.536C9.823 8.565 9.891 8.618 9.938 8.689C9.984 8.760 10.006 8.844 10.000 8.929C9.994 9.013 9.960 9.094 9.904 9.157C9.904 9.157 5.001 15.500 5.001 15.500C5.001 15.500 0.097 9.157 0.097 9.157C0.041 9.094 0.007 9.013 0.001 8.929C-0.005 8.844 0.017 8.760 0.063 8.689C0.110 8.618 0.179 8.565 0.259 8.536C0.339 8.508 0.426 8.507 0.506 8.534C0.506 8.534 3.458 9.877 3.458 9.877C3.518 9.916 3.587 9.935 3.658 9.934C3.729 9.933 3.797 9.910 3.855 9.870C3.914 9.830 3.958 9.773 3.984 9.707C4.010 9.641 4.016 9.569 4.001 9.500C4.001 9.500 4.010 9.500 4.010 9.500C4.010 9.500 4.010 5.500 4.010 5.500C4.010 4.041 4.589 2.642 5.621 1.611C6.652 0.579 8.051 0.000 9.510 0.000C10.968 0.000 12.367 0.579 13.399 1.611C14.430 2.642 15.010 4.041 15.010 5.500Z\" fill=\"currentColor\" transform=\"translate(1.999 2.5)\"/></svg>";
    readonly merge_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M6.019 4.992C6.019 4.992 4.019 4.992 4.019 4.992C4.019 4.992 4.019 15.494 4.019 15.494C4.019 15.560 4.032 15.624 4.057 15.685C4.082 15.745 4.118 15.800 4.165 15.846C4.211 15.892 4.266 15.929 4.326 15.954C4.386 15.979 4.451 15.992 4.516 15.992C4.516 15.992 5.521 15.992 5.521 15.992C5.587 15.992 5.651 15.979 5.712 15.954C5.772 15.929 5.827 15.892 5.873 15.846C5.919 15.800 5.956 15.745 5.981 15.685C6.006 15.624 6.019 15.560 6.019 15.494C6.019 15.494 6.019 4.992 6.019 4.992Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(1.981 2.008)\"/><path d=\"M6.001 7.992C6.001 7.992 6.001 6.004 6.001 6.004C5.985 5.935 5.991 5.863 6.017 5.797C6.043 5.731 6.088 5.675 6.146 5.634C6.204 5.594 6.272 5.572 6.343 5.570C6.414 5.569 6.483 5.589 6.543 5.627C6.543 5.627 9.495 6.971 9.495 6.971C9.576 6.997 9.663 6.996 9.743 6.968C9.823 6.940 9.891 6.886 9.938 6.815C9.984 6.744 10.006 6.660 10.000 6.575C9.994 6.491 9.960 6.411 9.904 6.347C9.904 6.347 5.019 0.000 5.019 0.000C5.019 0.000 0.097 6.347 0.097 6.347C0.041 6.411 0.007 6.491 0.001 6.575C-0.005 6.660 0.017 6.744 0.063 6.815C0.110 6.886 0.179 6.940 0.259 6.968C0.339 6.996 0.426 6.997 0.506 6.971C0.506 6.971 3.458 5.627 3.458 5.627C3.518 5.589 3.587 5.569 3.658 5.570C3.729 5.572 3.797 5.594 3.855 5.634C3.914 5.675 3.958 5.731 3.984 5.797C4.010 5.863 4.016 5.935 4.001 6.004C4.001 6.004 4.001 7.992 4.001 7.992C4.001 7.992 3.999 7.992 3.999 7.992C3.999 7.992 4.001 8.003 4.001 8.003C4.019 9.600 4.668 11.125 5.805 12.247C6.942 13.368 8.477 13.995 10.074 13.992C10.074 13.992 14.019 13.992 14.019 13.992C14.019 13.992 14.019 13.989 14.019 13.989C14.019 13.989 15.522 13.991 15.522 13.991C15.587 13.991 15.652 13.978 15.712 13.953C15.772 13.928 15.827 13.892 15.873 13.845C15.919 13.799 15.956 13.745 15.981 13.684C16.006 13.624 16.019 13.560 16.019 13.494C16.019 13.494 16.019 12.488 16.019 12.488C16.019 12.356 15.967 12.230 15.874 12.137C15.780 12.044 15.654 11.992 15.523 11.992C15.523 11.992 10.074 11.992 10.074 11.992C9.007 11.996 7.981 11.580 7.219 10.833C6.456 10.087 6.019 9.070 6.001 8.003C6.001 8.003 6.001 7.992 6.001 7.992Z\" fill=\"currentColor\" transform=\"translate(1.981 2.008)\"/></svg>";
    readonly merge_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M12.000 4.992C12.000 4.992 10.000 4.992 10.000 4.992C10.000 4.992 10.000 15.491 10.000 15.491C10.000 15.624 10.053 15.751 10.147 15.845C10.241 15.939 10.368 15.992 10.501 15.992C10.501 15.992 11.499 15.992 11.499 15.992C11.565 15.992 11.630 15.979 11.691 15.954C11.752 15.928 11.807 15.892 11.853 15.845C11.900 15.799 11.937 15.743 11.962 15.683C11.987 15.622 12.000 15.557 12.000 15.491C12.000 15.491 12.000 4.992 12.000 4.992Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(2 2.008)\"/><path d=\"M12.018 7.992C12.018 7.992 12.018 6.004 12.018 6.004C12.003 5.935 12.009 5.863 12.035 5.797C12.061 5.731 12.105 5.675 12.163 5.634C12.221 5.594 12.290 5.572 12.361 5.570C12.432 5.569 12.501 5.589 12.561 5.627C12.561 5.627 15.513 6.971 15.513 6.971C15.593 6.997 15.680 6.996 15.760 6.968C15.840 6.940 15.909 6.886 15.955 6.815C16.002 6.744 16.024 6.660 16.018 6.575C16.012 6.491 15.978 6.411 15.922 6.347C15.922 6.347 11.000 0.000 11.000 0.000C11.000 0.000 6.115 6.347 6.115 6.347C6.058 6.411 6.025 6.491 6.019 6.575C6.013 6.660 6.035 6.744 6.081 6.815C6.128 6.886 6.196 6.940 6.276 6.968C6.356 6.996 6.443 6.997 6.524 6.971C6.524 6.971 9.476 5.627 9.476 5.627C9.535 5.589 9.605 5.569 9.676 5.570C9.746 5.572 9.815 5.594 9.873 5.634C9.931 5.675 9.976 5.731 10.002 5.797C10.028 5.863 10.033 5.935 10.018 6.004C10.018 6.004 10.018 8.003 10.018 8.003C10.000 9.070 9.563 10.087 8.800 10.833C8.038 11.580 7.012 11.996 5.945 11.992C5.945 11.992 0.496 11.992 0.496 11.992C0.365 11.992 0.238 12.044 0.145 12.137C0.052 12.230 0.000 12.356 0.000 12.488C0.000 12.488 0.000 13.494 0.000 13.494C-0.000 13.560 0.013 13.624 0.038 13.684C0.063 13.745 0.099 13.799 0.146 13.845C0.192 13.892 0.246 13.928 0.307 13.953C0.367 13.978 0.432 13.991 0.497 13.991C0.497 13.991 2.000 13.989 2.000 13.989C2.000 13.989 2.000 13.992 2.000 13.992C2.000 13.992 5.945 13.992 5.945 13.992C7.542 13.995 9.076 13.368 10.214 12.247C11.351 11.125 12.000 9.600 12.018 8.003C12.018 8.003 12.020 7.992 12.020 7.992Z\" fill=\"currentColor\" transform=\"translate(2 2.008)\"/></svg>";
    readonly merge_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M6.015 8.007C6.015 8.007 4.015 8.007 4.015 8.007C4.015 8.007 4.015 16.502 4.015 16.502C4.015 16.568 4.028 16.634 4.053 16.695C4.079 16.756 4.116 16.812 4.163 16.859C4.210 16.906 4.265 16.943 4.327 16.968C4.388 16.994 4.453 17.007 4.520 17.007C4.520 17.007 5.510 17.007 5.510 17.007C5.644 17.007 5.772 16.954 5.867 16.859C5.962 16.764 6.015 16.636 6.015 16.502C6.015 16.502 6.015 8.007 6.015 8.007Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(4.011 0.993)\"/><path d=\"M0.506 6.966C0.506 6.966 3.458 5.623 3.458 5.623C3.518 5.584 3.587 5.565 3.658 5.566C3.729 5.567 3.797 5.590 3.855 5.630C3.914 5.670 3.958 5.727 3.984 5.793C4.010 5.859 4.016 5.931 4.001 6.000C4.001 6.000 4.002 6.000 4.002 6.000C4.002 6.000 4.002 7.965 4.002 7.965C4.009 9.133 4.326 10.277 4.920 11.282C5.515 12.287 6.365 13.116 7.384 13.686C7.384 13.686 11.695 16.371 11.695 16.371C11.751 16.406 11.813 16.429 11.878 16.440C11.943 16.451 12.010 16.449 12.074 16.434C12.139 16.419 12.199 16.392 12.253 16.353C12.307 16.315 12.352 16.266 12.387 16.210C12.387 16.210 12.913 15.366 12.913 15.366C12.948 15.310 12.972 15.248 12.982 15.183C12.993 15.118 12.991 15.051 12.976 14.987C12.961 14.922 12.934 14.862 12.895 14.808C12.857 14.754 12.808 14.708 12.752 14.674C12.752 14.674 8.447 11.992 8.447 11.992C7.728 11.605 7.123 11.036 6.694 10.341C6.264 9.646 6.024 8.851 5.999 8.035C5.999 8.035 6.001 5.999 6.001 5.999C5.986 5.930 5.992 5.858 6.018 5.792C6.044 5.726 6.088 5.670 6.146 5.630C6.204 5.589 6.273 5.567 6.344 5.566C6.414 5.565 6.484 5.585 6.543 5.623C6.543 5.623 9.495 6.966 9.495 6.966C9.576 6.993 9.663 6.992 9.743 6.964C9.823 6.936 9.891 6.882 9.938 6.811C9.984 6.740 10.006 6.656 10.000 6.571C9.994 6.487 9.960 6.406 9.904 6.343C9.904 6.343 5.001 0.000 5.001 0.000C5.001 0.000 0.097 6.343 0.097 6.343C0.041 6.406 0.007 6.487 0.001 6.571C-0.005 6.656 0.017 6.740 0.063 6.811C0.110 6.882 0.179 6.935 0.259 6.964C0.339 6.992 0.426 6.993 0.506 6.966Z\" fill=\"currentColor\" transform=\"translate(4.011 0.993)\"/></svg>";
    readonly merge_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M8.974 8.007C8.974 8.007 6.974 8.007 6.974 8.007C6.974 8.007 6.974 16.502 6.974 16.502C6.974 16.568 6.987 16.634 7.013 16.695C7.038 16.756 7.075 16.812 7.122 16.859C7.169 16.906 7.225 16.943 7.286 16.968C7.347 16.994 7.413 17.007 7.479 17.007C7.479 17.007 8.469 17.007 8.469 17.007C8.603 17.007 8.732 16.954 8.826 16.859C8.921 16.764 8.974 16.636 8.974 16.502C8.974 16.502 8.974 8.007 8.974 8.007Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(3.018 0.993)\"/><path d=\"M12.892 6.343C12.892 6.343 7.989 0.000 7.989 0.000C7.989 0.000 3.085 6.343 3.085 6.343C3.029 6.406 2.995 6.487 2.989 6.571C2.983 6.656 3.005 6.740 3.051 6.811C3.098 6.882 3.167 6.935 3.247 6.964C3.327 6.992 3.414 6.993 3.494 6.966C3.494 6.966 6.446 5.623 6.446 5.623C6.506 5.584 6.575 5.565 6.646 5.566C6.716 5.567 6.785 5.589 6.843 5.630C6.901 5.670 6.946 5.726 6.972 5.792C6.997 5.858 7.003 5.930 6.988 5.999C6.988 5.999 6.990 8.035 6.990 8.035C6.965 8.851 6.725 9.646 6.296 10.341C5.866 11.036 5.261 11.605 4.542 11.992C4.542 11.992 0.237 14.674 0.237 14.674C0.181 14.708 0.132 14.754 0.094 14.808C0.055 14.862 0.028 14.922 0.013 14.987C-0.002 15.051 -0.004 15.118 0.007 15.183C0.018 15.248 0.041 15.310 0.076 15.366C0.076 15.366 0.602 16.210 0.602 16.210C0.637 16.266 0.682 16.315 0.736 16.353C0.790 16.392 0.851 16.419 0.915 16.434C0.979 16.449 1.046 16.451 1.111 16.440C1.176 16.429 1.238 16.406 1.294 16.371C1.294 16.371 5.605 13.686 5.605 13.686C6.625 13.116 7.475 12.287 8.069 11.282C8.663 10.277 8.980 9.133 8.988 7.965C8.988 7.965 8.988 6.000 8.988 6.000C8.972 5.931 8.979 5.859 9.005 5.793C9.031 5.727 9.076 5.670 9.134 5.630C9.192 5.590 9.261 5.567 9.331 5.566C9.402 5.565 9.472 5.584 9.531 5.623C9.531 5.623 12.483 6.966 12.483 6.966C12.564 6.993 12.651 6.992 12.731 6.964C12.811 6.935 12.879 6.882 12.926 6.811C12.972 6.740 12.994 6.656 12.988 6.571C12.982 6.487 12.949 6.406 12.892 6.343Z\" fill=\"currentColor\" transform=\"translate(3.018 0.993)\"/></svg>";
    readonly merge_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M0 1 C0 0.448 0.448 0 1 0 C1.552 0 2 0.448 2 1 L2 6 C2 6.552 1.552 7 1 7 C0.448 7 0 6.552 0 6 Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(3.9 10.1) rotate(20 1 6)\"/><path class=\"mm2\" d=\"M0 1 C0 0.448 0.448 0 1 0 C1.552 0 2 0.448 2 1 L2 6 C2 6.552 1.552 7 1 7 C0.448 7 0 6.552 0 6 Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(14.1 10.1) rotate(-20 1 6)\"/><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5 2)\"/></svg>";
    readonly mini_roundabout_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-4.000 -1.000L-7.750 -1.000L-7.750 1.000L-4.000 1.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-5.784 -4.494C-5.784 -4.494 -7.127 -1.542 -7.127 -1.542C-7.166 -1.482 -7.185 -1.413 -7.184 -1.342C-7.183 -1.271 -7.160 -1.203 -7.120 -1.145C-7.080 -1.087 -7.023 -1.042 -6.957 -1.016C-6.891 -0.990 -6.819 -0.984 -6.750 -1.000L-6.750 1.000C-6.819 0.985 -6.891 0.991 -6.957 1.017C-7.023 1.043 -7.080 1.087 -7.120 1.145C-7.160 1.204 -7.183 1.272 -7.184 1.343C-7.185 1.414 -7.166 1.483 -7.127 1.543C-7.127 1.543 -5.784 4.495 -5.784 4.495C-5.757 4.575 -5.758 4.662 -5.786 4.742C-5.815 4.822 -5.868 4.891 -5.939 4.938C-6.010 4.984 -6.094 5.006 -6.179 5.000C-6.263 4.994 -6.344 4.960 -6.407 4.904L-12.750 0.000L-6.407 -4.903C-6.344 -4.959 -6.263 -4.993 -6.179 -4.999C-6.094 -5.005 -6.010 -4.983 -5.939 -4.937C-5.868 -4.890 -5.815 -4.822 -5.786 -4.742C-5.758 -4.661 -5.757 -4.574 -5.784 -4.494Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/></svg>";
    readonly mini_roundabout_lht_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 15.500 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 15.500 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 15.500 9.000)\"/><path d=\"M4.000 1.000L7.750 1.000L7.750 -1.000L4.000 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 15.500 9.000)\"/><path d=\"M5.784 4.494C5.784 4.494 7.127 1.542 7.127 1.542C7.166 1.482 7.185 1.413 7.184 1.342C7.183 1.271 7.160 1.203 7.120 1.145C7.080 1.087 7.023 1.042 6.957 1.016C6.891 0.990 6.819 0.984 6.750 1.000L6.750 -1.000C6.819 -0.985 6.891 -0.991 6.957 -1.017C7.023 -1.043 7.080 -1.087 7.120 -1.145C7.160 -1.204 7.183 -1.272 7.184 -1.343C7.185 -1.414 7.166 -1.483 7.127 -1.543C7.127 -1.543 5.784 -4.495 5.784 -4.495C5.757 -4.575 5.758 -4.662 5.786 -4.742C5.815 -4.822 5.868 -4.891 5.939 -4.938C6.010 -4.984 6.094 -5.006 6.179 -5.000C6.263 -4.994 6.344 -4.960 6.407 -4.904L12.750 0.000L6.407 4.903C6.344 4.959 6.263 4.993 6.179 4.999C6.094 5.005 6.010 4.983 5.939 4.937C5.868 4.890 5.815 4.822 5.786 4.742C5.758 4.661 5.757 4.574 5.784 4.494Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 15.500 9.000)\"/></svg>";
    readonly mini_roundabout_lht_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-4.000 -1.000L-7.750 -1.000L-7.750 1.000L-4.000 1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-5.784 -4.494C-5.784 -4.494 -7.127 -1.542 -7.127 -1.542C-7.166 -1.482 -7.185 -1.413 -7.184 -1.342C-7.183 -1.271 -7.160 -1.203 -7.120 -1.145C-7.080 -1.087 -7.023 -1.042 -6.957 -1.016C-6.891 -0.990 -6.819 -0.984 -6.750 -1.000L-6.750 1.000C-6.819 0.985 -6.891 0.991 -6.957 1.017C-7.023 1.043 -7.080 1.087 -7.120 1.145C-7.160 1.204 -7.183 1.272 -7.184 1.343C-7.185 1.414 -7.166 1.483 -7.127 1.543C-7.127 1.543 -5.784 4.495 -5.784 4.495C-5.757 4.575 -5.758 4.662 -5.786 4.742C-5.815 4.822 -5.868 4.891 -5.939 4.938C-6.010 4.984 -6.094 5.006 -6.179 5.000C-6.263 4.994 -6.344 4.960 -6.407 4.904L-12.750 0.000L-6.407 -4.903C-6.344 -4.959 -6.263 -4.993 -6.179 -4.999C-6.094 -5.005 -6.010 -4.983 -5.939 -4.937C-5.868 -4.890 -5.815 -4.822 -5.786 -4.742C-5.758 -4.661 -5.757 -4.574 -5.784 -4.494Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/></svg>";
    readonly mini_roundabout_lht_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M-1.000 2.828L-1.000 6.000Q-1.000 6.500 -0.500 6.500L0.500 6.500Q1.000 6.500 1.000 6.000L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 0.000 -5.000L0.000 -3.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M1.000 -4.000L1.000 -7.000L-1.000 -7.000L-1.000 -4.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M4.494 -5.034C4.494 -5.034 1.542 -6.377 1.542 -6.377C1.482 -6.416 1.413 -6.435 1.342 -6.434C1.271 -6.433 1.203 -6.410 1.145 -6.370C1.087 -6.330 1.042 -6.273 1.016 -6.207C0.990 -6.141 0.984 -6.069 1.000 -6.000L-1.000 -6.000C-0.985 -6.069 -0.991 -6.141 -1.017 -6.207C-1.043 -6.273 -1.087 -6.330 -1.145 -6.370C-1.204 -6.410 -1.272 -6.433 -1.343 -6.434C-1.414 -6.435 -1.483 -6.416 -1.543 -6.377C-1.543 -6.377 -4.495 -5.034 -4.495 -5.034C-4.575 -5.007 -4.662 -5.008 -4.742 -5.036C-4.822 -5.065 -4.891 -5.118 -4.938 -5.189C-4.984 -5.260 -5.006 -5.344 -5.000 -5.429C-4.994 -5.513 -4.960 -5.594 -4.904 -5.657L0.000 -12.000L4.903 -5.657C4.959 -5.594 4.993 -5.513 4.999 -5.429C5.005 -5.344 4.983 -5.260 4.937 -5.189C4.890 -5.118 4.822 -5.065 4.742 -5.036C4.661 -5.008 4.574 -5.007 4.494 -5.034Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/></svg>";
    readonly mini_roundabout_lht_uturn: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M2.750 1.199L2.750 12.300Q2.750 12.800 3.250 12.800L4.250 12.800Q4.750 12.800 4.750 12.300L4.750 1.199Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M4.672 1.782A5.000 5.000 0 1 0 -4.672 1.782L-2.803 1.069A3.000 3.000 0 1 1 2.803 1.069Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M-4.750 1.199L-4.750 7.800L-2.750 7.800L-2.750 1.199Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M-8.244 5.834C-8.244 5.834 -5.292 7.177 -5.292 7.177C-5.232 7.216 -5.163 7.235 -5.092 7.234C-5.021 7.233 -4.953 7.210 -4.895 7.170C-4.837 7.130 -4.792 7.073 -4.766 7.007C-4.740 6.941 -4.734 6.869 -4.750 6.800L-2.750 6.800C-2.765 6.869 -2.759 6.941 -2.733 7.007C-2.707 7.073 -2.663 7.130 -2.605 7.170C-2.546 7.210 -2.478 7.233 -2.407 7.234C-2.336 7.235 -2.267 7.216 -2.207 7.177C-2.207 7.177 0.745 5.834 0.745 5.834C0.825 5.807 0.912 5.808 0.992 5.836C1.072 5.865 1.141 5.918 1.188 5.989C1.234 6.060 1.256 6.144 1.250 6.229C1.244 6.313 1.210 6.394 1.154 6.457L-3.750 12.800L-8.653 6.457C-8.709 6.394 -8.743 6.313 -8.749 6.229C-8.755 6.144 -8.733 6.060 -8.687 5.989C-8.640 5.918 -8.572 5.865 -8.492 5.836C-8.411 5.808 -8.324 5.807 -8.244 5.834Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/></svg>";
    readonly mini_roundabout_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"translate(4.500 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(4.500 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(4.500 9.000)\"/><path d=\"M4.000 1.000L7.750 1.000L7.750 -1.000L4.000 -1.000Z\" fill=\"currentColor\" transform=\"translate(4.500 9.000)\"/><path d=\"M5.784 4.494C5.784 4.494 7.127 1.542 7.127 1.542C7.166 1.482 7.185 1.413 7.184 1.342C7.183 1.271 7.160 1.203 7.120 1.145C7.080 1.087 7.023 1.042 6.957 1.016C6.891 0.990 6.819 0.984 6.750 1.000L6.750 -1.000C6.819 -0.985 6.891 -0.991 6.957 -1.017C7.023 -1.043 7.080 -1.087 7.120 -1.145C7.160 -1.204 7.183 -1.272 7.184 -1.343C7.185 -1.414 7.166 -1.483 7.127 -1.543C7.127 -1.543 5.784 -4.495 5.784 -4.495C5.757 -4.575 5.758 -4.662 5.786 -4.742C5.815 -4.822 5.868 -4.891 5.939 -4.938C6.010 -4.984 6.094 -5.006 6.179 -5.000C6.263 -4.994 6.344 -4.960 6.407 -4.904L12.750 0.000L6.407 4.903C6.344 4.959 6.263 4.993 6.179 4.999C6.094 5.005 6.010 4.983 5.939 4.937C5.868 4.890 5.815 4.822 5.786 4.742C5.758 4.661 5.757 4.574 5.784 4.494Z\" fill=\"currentColor\" transform=\"translate(4.500 9.000)\"/></svg>";
    readonly mini_roundabout_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M-1.000 2.828L-1.000 6.000Q-1.000 6.500 -0.500 6.500L0.500 6.500Q1.000 6.500 1.000 6.000L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 0.000 -5.000L0.000 -3.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M1.000 -4.000L1.000 -7.000L-1.000 -7.000L-1.000 -4.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M4.494 -5.034C4.494 -5.034 1.542 -6.377 1.542 -6.377C1.482 -6.416 1.413 -6.435 1.342 -6.434C1.271 -6.433 1.203 -6.410 1.145 -6.370C1.087 -6.330 1.042 -6.273 1.016 -6.207C0.990 -6.141 0.984 -6.069 1.000 -6.000L-1.000 -6.000C-0.985 -6.069 -0.991 -6.141 -1.017 -6.207C-1.043 -6.273 -1.087 -6.330 -1.145 -6.370C-1.204 -6.410 -1.272 -6.433 -1.343 -6.434C-1.414 -6.435 -1.483 -6.416 -1.543 -6.377C-1.543 -6.377 -4.495 -5.034 -4.495 -5.034C-4.575 -5.007 -4.662 -5.008 -4.742 -5.036C-4.822 -5.065 -4.891 -5.118 -4.938 -5.189C-4.984 -5.260 -5.006 -5.344 -5.000 -5.429C-4.994 -5.513 -4.960 -5.594 -4.904 -5.657L0.000 -12.000L4.903 -5.657C4.959 -5.594 4.993 -5.513 4.999 -5.429C5.005 -5.344 4.983 -5.260 4.937 -5.189C4.890 -5.118 4.822 -5.065 4.742 -5.036C4.661 -5.008 4.574 -5.007 4.494 -5.034Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/></svg>";
    readonly mini_roundabout_uturn: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M-1.750 0.000A1.750 1.750 0 1 0 1.750 0.000A1.750 1.750 0 1 0 -1.750 0.000Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M2.750 1.199L2.750 12.300Q2.750 12.800 3.250 12.800L4.250 12.800Q4.750 12.800 4.750 12.300L4.750 1.199Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M4.672 1.782A5.000 5.000 0 1 0 -4.672 1.782L-2.803 1.069A3.000 3.000 0 1 1 2.803 1.069Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M-4.750 1.199L-4.750 7.800L-2.750 7.800L-2.750 1.199Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M-8.244 5.834C-8.244 5.834 -5.292 7.177 -5.292 7.177C-5.232 7.216 -5.163 7.235 -5.092 7.234C-5.021 7.233 -4.953 7.210 -4.895 7.170C-4.837 7.130 -4.792 7.073 -4.766 7.007C-4.740 6.941 -4.734 6.869 -4.750 6.800L-2.750 6.800C-2.765 6.869 -2.759 6.941 -2.733 7.007C-2.707 7.073 -2.663 7.130 -2.605 7.170C-2.546 7.210 -2.478 7.233 -2.407 7.234C-2.336 7.235 -2.267 7.216 -2.207 7.177C-2.207 7.177 0.745 5.834 0.745 5.834C0.825 5.807 0.912 5.808 0.992 5.836C1.072 5.865 1.141 5.918 1.188 5.989C1.234 6.060 1.256 6.144 1.250 6.229C1.244 6.313 1.210 6.394 1.154 6.457L-3.750 12.800L-8.653 6.457C-8.709 6.394 -8.743 6.313 -8.749 6.229C-8.755 6.144 -8.733 6.060 -8.687 5.989C-8.640 5.918 -8.572 5.865 -8.492 5.836C-8.411 5.808 -8.324 5.807 -8.244 5.834Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/></svg>";
    readonly new_name_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M7.992 3.999C7.992 3.999 8.003 4.001 8.003 4.001C9.600 4.019 11.125 4.668 12.247 5.805C13.368 6.942 13.995 8.477 13.992 10.074C13.992 10.074 13.992 14.019 13.992 14.019C13.992 14.019 13.989 14.019 13.989 14.019C13.989 14.019 13.991 15.522 13.991 15.522C13.991 15.587 13.978 15.652 13.953 15.712C13.928 15.772 13.892 15.827 13.845 15.873C13.799 15.919 13.745 15.956 13.684 15.981C13.624 16.006 13.560 16.019 13.494 16.019C13.494 16.019 12.488 16.019 12.488 16.019C12.356 16.019 12.230 15.967 12.137 15.874C12.044 15.780 11.992 15.654 11.992 15.523C11.992 15.523 11.992 10.074 11.992 10.074C11.996 9.007 11.580 7.981 10.833 7.219C10.087 6.456 9.070 6.019 8.003 6.001C8.003 6.001 6.004 6.001 6.004 6.001C5.935 5.985 5.863 5.991 5.797 6.017C5.731 6.043 5.675 6.088 5.634 6.146C5.594 6.204 5.572 6.272 5.570 6.343C5.569 6.414 5.589 6.483 5.627 6.543C5.627 6.543 6.971 9.495 6.971 9.495C6.997 9.576 6.996 9.663 6.968 9.743C6.940 9.823 6.886 9.891 6.815 9.938C6.744 9.984 6.660 10.006 6.575 10.000C6.491 9.994 6.411 9.960 6.347 9.904C6.347 9.904 0.000 5.019 0.000 5.019C0.000 5.019 6.347 0.097 6.347 0.097C6.411 0.041 6.491 0.007 6.575 0.001C6.660 -0.005 6.744 0.017 6.815 0.063C6.886 0.110 6.940 0.179 6.968 0.259C6.996 0.339 6.997 0.426 6.971 0.506C6.971 0.506 5.627 3.458 5.627 3.458C5.589 3.518 5.569 3.587 5.570 3.658C5.572 3.729 5.594 3.797 5.634 3.855C5.675 3.914 5.731 3.958 5.797 3.984C5.863 4.010 5.935 4.016 6.004 4.001C6.004 4.001 7.992 4.001 7.992 4.001Z\" fill=\"currentColor\" transform=\"translate(2.008 1.981)\"/></svg>";
    readonly new_name_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M5.989 6.001C4.922 6.019 3.905 6.456 3.159 7.219C2.412 7.981 1.996 9.007 2.000 10.074C2.000 10.074 2.000 15.523 2.000 15.523C2.000 15.654 1.948 15.780 1.855 15.874C1.762 15.967 1.635 16.019 1.504 16.019C1.504 16.019 0.497 16.019 0.497 16.019C0.432 16.019 0.368 16.006 0.307 15.981C0.247 15.956 0.192 15.919 0.146 15.873C0.100 15.827 0.064 15.772 0.039 15.712C0.014 15.652 0.001 15.587 0.001 15.522C0.001 15.522 0.003 14.019 0.003 14.019C0.003 14.019 0.000 14.019 0.000 14.019C0.000 14.019 0.000 10.074 0.000 10.074C-0.004 8.477 0.624 6.942 1.745 5.805C2.867 4.668 4.392 4.019 5.989 4.001C5.989 4.001 6.000 3.999 6.000 3.999C6.000 3.999 6.000 4.001 6.000 4.001C6.000 4.001 7.988 4.001 7.988 4.001C8.057 4.016 8.129 4.010 8.195 3.984C8.260 3.958 8.317 3.914 8.358 3.855C8.398 3.797 8.420 3.729 8.422 3.658C8.423 3.587 8.403 3.518 8.365 3.458C8.365 3.458 7.021 0.506 7.021 0.506C6.995 0.426 6.996 0.339 7.024 0.259C7.052 0.179 7.106 0.110 7.177 0.063C7.247 0.017 7.332 -0.005 7.416 0.001C7.501 0.007 7.581 0.041 7.645 0.097C7.645 0.097 13.992 5.019 13.992 5.019C13.992 5.019 7.645 9.904 7.645 9.904C7.581 9.960 7.501 9.994 7.416 10.000C7.332 10.006 7.247 9.984 7.177 9.938C7.106 9.891 7.052 9.823 7.024 9.743C6.996 9.663 6.995 9.576 7.021 9.495C7.021 9.495 8.365 6.543 8.365 6.543C8.403 6.483 8.423 6.414 8.422 6.343C8.420 6.272 8.398 6.204 8.358 6.146C8.317 6.088 8.260 6.043 8.195 6.017C8.129 5.991 8.057 5.985 7.988 6.001C7.988 6.001 5.989 6.001 5.989 6.001Z\" fill=\"currentColor\" transform=\"translate(4 1.981)\"/></svg>";
    readonly new_name_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M13.481 15.011C13.349 15.011 13.223 14.959 13.129 14.865C13.036 14.772 12.983 14.645 12.983 14.513C12.983 14.513 12.983 2.015 12.983 2.015C12.983 2.015 12.704 2.009 12.704 2.009C12.317 1.974 11.928 2.026 11.565 2.163C11.202 2.300 10.875 2.517 10.608 2.799C10.608 2.799 5.213 7.412 5.213 7.412C5.155 7.447 5.109 7.498 5.080 7.559C5.051 7.620 5.041 7.687 5.049 7.754C5.058 7.821 5.086 7.884 5.130 7.935C5.173 7.986 5.231 8.024 5.295 8.043C5.295 8.043 8.224 9.437 8.224 9.437C8.296 9.481 8.351 9.549 8.381 9.628C8.411 9.707 8.414 9.794 8.390 9.875C8.366 9.957 8.316 10.028 8.247 10.078C8.178 10.127 8.095 10.153 8.010 10.151C8.010 10.151 0.000 10.483 0.000 10.483C0.000 10.483 1.696 2.647 1.696 2.647C1.708 2.564 1.748 2.486 1.809 2.427C1.870 2.368 1.948 2.331 2.032 2.320C2.117 2.310 2.202 2.328 2.275 2.372C2.348 2.415 2.405 2.481 2.436 2.559C2.436 2.559 3.309 5.683 3.309 5.683C3.318 5.753 3.348 5.819 3.394 5.872C3.441 5.926 3.502 5.964 3.570 5.982C3.639 6.001 3.711 5.998 3.778 5.976C3.845 5.953 3.904 5.911 3.947 5.855C3.947 5.855 3.951 5.859 3.951 5.859C3.951 5.859 9.343 1.250 9.343 1.250C9.790 0.820 10.322 0.487 10.904 0.273C11.487 0.059 12.108 -0.031 12.728 0.009C12.728 0.009 12.992 0.011 12.992 0.011C13.257 0.002 13.521 0.048 13.768 0.146C14.015 0.244 14.239 0.392 14.426 0.580C14.792 0.977 14.992 1.500 14.983 2.040C14.983 2.040 14.983 14.513 14.983 14.513C14.983 14.579 14.971 14.643 14.945 14.704C14.920 14.764 14.884 14.819 14.838 14.865C14.791 14.912 14.736 14.948 14.676 14.973C14.616 14.998 14.551 15.011 14.486 15.011C14.486 15.011 13.481 15.011 13.481 15.011Z\" fill=\"currentColor\" transform=\"translate(2.017 2.989)\"/></svg>";
    readonly new_name_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M0.498 15.011C0.432 15.011 0.368 14.998 0.307 14.973C0.247 14.948 0.192 14.912 0.146 14.865C0.100 14.819 0.063 14.764 0.038 14.704C0.013 14.643 -0.000 14.579 0.000 14.513C0.000 14.513 0.000 2.040 0.000 2.040C-0.008 1.500 0.191 0.977 0.558 0.580C0.745 0.392 0.969 0.244 1.215 0.146C1.462 0.048 1.726 0.002 1.992 0.011C1.992 0.011 2.256 0.009 2.256 0.009C2.875 -0.031 3.496 0.059 4.079 0.273C4.662 0.487 5.194 0.820 5.641 1.250C5.641 1.250 11.033 5.859 11.033 5.859C11.033 5.859 11.036 5.855 11.036 5.855C11.080 5.911 11.138 5.953 11.205 5.976C11.272 5.998 11.345 6.001 11.413 5.982C11.481 5.964 11.543 5.926 11.589 5.872C11.636 5.819 11.665 5.753 11.674 5.683C11.674 5.683 12.547 2.559 12.547 2.559C12.579 2.481 12.636 2.415 12.709 2.372C12.782 2.328 12.867 2.310 12.951 2.320C13.035 2.331 13.114 2.368 13.175 2.427C13.235 2.486 13.275 2.564 13.288 2.647C13.288 2.647 14.983 10.483 14.983 10.483C14.983 10.483 6.973 10.151 6.973 10.151C6.888 10.153 6.805 10.127 6.736 10.078C6.668 10.028 6.618 9.957 6.593 9.875C6.569 9.794 6.572 9.707 6.602 9.628C6.632 9.549 6.687 9.481 6.759 9.437C6.759 9.437 9.688 8.043 9.688 8.043C9.753 8.024 9.810 7.986 9.854 7.935C9.897 7.884 9.925 7.821 9.934 7.754C9.943 7.687 9.932 7.620 9.903 7.559C9.874 7.498 9.828 7.447 9.771 7.412C9.771 7.412 4.375 2.799 4.375 2.799C4.109 2.517 3.781 2.300 3.418 2.163C3.055 2.026 2.666 1.974 2.280 2.009C2.280 2.009 2.000 2.015 2.000 2.015C2.000 2.015 2.000 14.513 2.000 14.513C2.000 14.645 1.948 14.772 1.854 14.865C1.761 14.959 1.634 15.011 1.502 15.011C1.502 15.011 0.498 15.011 0.498 15.011Z\" fill=\"currentColor\" transform=\"translate(3 2.989)\"/></svg>";
    readonly new_name_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.986 11.043C9.986 11.043 9.986 14.504 9.986 14.504C9.986 14.635 9.934 14.762 9.841 14.855C9.748 14.948 9.621 15.000 9.490 15.000C9.490 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.443 5.979 9.067 6.878 9.472 7.881C9.877 8.883 10.053 9.964 9.986 11.043Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly new_name_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.354 5.249C2.354 5.249 4.811 3.175 4.811 3.175C4.873 3.141 4.924 3.089 4.957 3.027C4.990 2.964 5.003 2.893 4.996 2.823C4.988 2.752 4.960 2.686 4.915 2.631C4.869 2.577 4.809 2.538 4.741 2.518C4.741 2.518 1.792 1.167 1.792 1.167C1.719 1.124 1.663 1.057 1.632 0.978C1.601 0.900 1.596 0.813 1.619 0.731C1.642 0.649 1.692 0.578 1.760 0.527C1.827 0.476 1.910 0.449 1.995 0.450C1.995 0.450 10.000 0.000 10.000 0.000C10.000 0.000 8.419 7.860 8.419 7.860C8.408 7.944 8.369 8.022 8.309 8.082C8.249 8.142 8.171 8.181 8.087 8.192C8.003 8.203 7.918 8.186 7.844 8.144C7.771 8.102 7.713 8.037 7.680 7.959C7.680 7.959 6.762 4.849 6.762 4.849C6.752 4.778 6.721 4.713 6.674 4.660C6.626 4.608 6.564 4.571 6.496 4.553C6.427 4.536 6.355 4.539 6.288 4.563C6.222 4.586 6.163 4.629 6.121 4.686C6.121 4.686 3.673 6.751 3.673 6.751C3.106 7.276 2.663 7.920 2.375 8.636C2.087 9.353 1.962 10.125 2.008 10.895C2.008 10.895 2.013 14.503 2.013 14.503C2.013 14.568 2.000 14.633 1.975 14.693C1.950 14.754 1.914 14.808 1.868 14.854C1.821 14.901 1.767 14.937 1.707 14.962C1.646 14.987 1.582 15.000 1.516 15.000C1.516 15.000 0.510 15.000 0.510 15.000C0.378 15.000 0.252 14.948 0.159 14.855C0.066 14.762 0.014 14.635 0.014 14.504C0.014 14.504 0.014 11.043 0.014 11.043C-0.053 9.964 0.123 8.883 0.528 7.881C0.932 6.878 1.556 5.979 2.354 5.249Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly new_name_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly notification_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M7.992 3.999C7.992 3.999 8.003 4.001 8.003 4.001C9.600 4.019 11.125 4.668 12.247 5.805C13.368 6.942 13.995 8.477 13.992 10.074C13.992 10.074 13.992 14.019 13.992 14.019C13.992 14.019 13.989 14.019 13.989 14.019C13.989 14.019 13.991 15.522 13.991 15.522C13.991 15.587 13.978 15.652 13.953 15.712C13.928 15.772 13.892 15.827 13.845 15.873C13.799 15.919 13.745 15.956 13.684 15.981C13.624 16.006 13.560 16.019 13.494 16.019C13.494 16.019 12.488 16.019 12.488 16.019C12.356 16.019 12.230 15.967 12.137 15.874C12.044 15.780 11.992 15.654 11.992 15.523C11.992 15.523 11.992 10.074 11.992 10.074C11.996 9.007 11.580 7.981 10.833 7.219C10.087 6.456 9.070 6.019 8.003 6.001C8.003 6.001 6.004 6.001 6.004 6.001C5.935 5.985 5.863 5.991 5.797 6.017C5.731 6.043 5.675 6.088 5.634 6.146C5.594 6.204 5.572 6.272 5.570 6.343C5.569 6.414 5.589 6.483 5.627 6.543C5.627 6.543 6.971 9.495 6.971 9.495C6.997 9.576 6.996 9.663 6.968 9.743C6.940 9.823 6.886 9.891 6.815 9.938C6.744 9.984 6.660 10.006 6.575 10.000C6.491 9.994 6.411 9.960 6.347 9.904C6.347 9.904 0.000 5.019 0.000 5.019C0.000 5.019 6.347 0.097 6.347 0.097C6.411 0.041 6.491 0.007 6.575 0.001C6.660 -0.005 6.744 0.017 6.815 0.063C6.886 0.110 6.940 0.179 6.968 0.259C6.996 0.339 6.997 0.426 6.971 0.506C6.971 0.506 5.627 3.458 5.627 3.458C5.589 3.518 5.569 3.587 5.570 3.658C5.572 3.729 5.594 3.797 5.634 3.855C5.675 3.914 5.731 3.958 5.797 3.984C5.863 4.010 5.935 4.016 6.004 4.001C6.004 4.001 7.992 4.001 7.992 4.001Z\" fill=\"currentColor\" transform=\"translate(2.008 1.981)\"/></svg>";
    readonly notification_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M5.989 6.001C4.922 6.019 3.905 6.456 3.159 7.219C2.412 7.981 1.996 9.007 2.000 10.074C2.000 10.074 2.000 15.523 2.000 15.523C2.000 15.654 1.948 15.780 1.855 15.874C1.762 15.967 1.635 16.019 1.504 16.019C1.504 16.019 0.497 16.019 0.497 16.019C0.432 16.019 0.368 16.006 0.307 15.981C0.247 15.956 0.192 15.919 0.146 15.873C0.100 15.827 0.064 15.772 0.039 15.712C0.014 15.652 0.001 15.587 0.001 15.522C0.001 15.522 0.003 14.019 0.003 14.019C0.003 14.019 0.000 14.019 0.000 14.019C0.000 14.019 0.000 10.074 0.000 10.074C-0.004 8.477 0.624 6.942 1.745 5.805C2.867 4.668 4.392 4.019 5.989 4.001C5.989 4.001 6.000 3.999 6.000 3.999C6.000 3.999 6.000 4.001 6.000 4.001C6.000 4.001 7.988 4.001 7.988 4.001C8.057 4.016 8.129 4.010 8.195 3.984C8.260 3.958 8.317 3.914 8.358 3.855C8.398 3.797 8.420 3.729 8.422 3.658C8.423 3.587 8.403 3.518 8.365 3.458C8.365 3.458 7.021 0.506 7.021 0.506C6.995 0.426 6.996 0.339 7.024 0.259C7.052 0.179 7.106 0.110 7.177 0.063C7.247 0.017 7.332 -0.005 7.416 0.001C7.501 0.007 7.581 0.041 7.645 0.097C7.645 0.097 13.992 5.019 13.992 5.019C13.992 5.019 7.645 9.904 7.645 9.904C7.581 9.960 7.501 9.994 7.416 10.000C7.332 10.006 7.247 9.984 7.177 9.938C7.106 9.891 7.052 9.823 7.024 9.743C6.996 9.663 6.995 9.576 7.021 9.495C7.021 9.495 8.365 6.543 8.365 6.543C8.403 6.483 8.423 6.414 8.422 6.343C8.420 6.272 8.398 6.204 8.358 6.146C8.317 6.088 8.260 6.043 8.195 6.017C8.129 5.991 8.057 5.985 7.988 6.001C7.988 6.001 5.989 6.001 5.989 6.001Z\" fill=\"currentColor\" transform=\"translate(4 1.981)\"/></svg>";
    readonly notification_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M13.481 15.011C13.349 15.011 13.223 14.959 13.129 14.865C13.036 14.772 12.983 14.645 12.983 14.513C12.983 14.513 12.983 2.015 12.983 2.015C12.983 2.015 12.704 2.009 12.704 2.009C12.317 1.973 11.928 2.026 11.565 2.163C11.202 2.300 10.875 2.517 10.608 2.799C10.608 2.799 5.213 7.411 5.213 7.411C5.155 7.447 5.109 7.498 5.080 7.559C5.051 7.619 5.041 7.687 5.049 7.754C5.058 7.821 5.086 7.884 5.130 7.935C5.173 7.986 5.231 8.024 5.295 8.043C5.295 8.043 8.224 9.437 8.224 9.437C8.296 9.481 8.351 9.549 8.381 9.628C8.411 9.707 8.414 9.794 8.390 9.875C8.366 9.957 8.316 10.028 8.247 10.078C8.178 10.127 8.095 10.153 8.010 10.151C8.010 10.151 0.000 10.483 0.000 10.483C0.000 10.483 1.696 2.647 1.696 2.647C1.708 2.564 1.748 2.486 1.809 2.427C1.870 2.368 1.948 2.330 2.032 2.320C2.117 2.310 2.202 2.328 2.275 2.371C2.348 2.415 2.405 2.481 2.436 2.559C2.436 2.559 3.309 5.683 3.309 5.683C3.318 5.753 3.348 5.819 3.394 5.872C3.441 5.925 3.502 5.964 3.570 5.982C3.639 6.001 3.711 5.998 3.778 5.976C3.845 5.953 3.904 5.911 3.947 5.855C3.947 5.855 3.951 5.859 3.951 5.859C3.951 5.859 9.343 1.250 9.343 1.250C9.790 0.820 10.322 0.487 10.904 0.273C11.487 0.059 12.108 -0.031 12.728 0.009C12.728 0.009 12.992 0.011 12.992 0.011C13.257 0.002 13.521 0.048 13.768 0.146C14.015 0.244 14.239 0.392 14.426 0.580C14.792 0.977 14.992 1.500 14.983 2.040C14.983 2.040 14.983 14.513 14.983 14.513C14.983 14.579 14.971 14.643 14.945 14.704C14.920 14.764 14.884 14.819 14.838 14.865C14.791 14.912 14.736 14.948 14.676 14.973C14.616 14.998 14.551 15.011 14.486 15.011C14.486 15.011 13.481 15.011 13.481 15.011Z\" fill=\"currentColor\" transform=\"translate(2.017 2.984)\"/></svg>";
    readonly notification_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M0.498 15.011C0.432 15.011 0.368 14.998 0.307 14.973C0.247 14.948 0.192 14.912 0.146 14.865C0.100 14.819 0.063 14.764 0.038 14.704C0.013 14.643 -0.000 14.579 0.000 14.513C0.000 14.513 0.000 2.040 0.000 2.040C-0.008 1.500 0.191 0.977 0.558 0.580C0.745 0.392 0.969 0.244 1.215 0.146C1.462 0.048 1.726 0.002 1.992 0.011C1.992 0.011 2.256 0.009 2.256 0.009C2.875 -0.031 3.496 0.059 4.079 0.273C4.662 0.487 5.194 0.820 5.641 1.250C5.641 1.250 11.033 5.859 11.033 5.859C11.033 5.859 11.036 5.855 11.036 5.855C11.080 5.911 11.138 5.953 11.205 5.976C11.272 5.998 11.345 6.001 11.413 5.982C11.481 5.964 11.543 5.926 11.589 5.872C11.636 5.819 11.665 5.753 11.674 5.683C11.674 5.683 12.547 2.559 12.547 2.559C12.579 2.481 12.636 2.415 12.709 2.372C12.782 2.328 12.867 2.310 12.951 2.320C13.035 2.331 13.114 2.368 13.175 2.427C13.235 2.486 13.275 2.564 13.288 2.647C13.288 2.647 14.983 10.483 14.983 10.483C14.983 10.483 6.973 10.151 6.973 10.151C6.888 10.153 6.805 10.127 6.736 10.078C6.668 10.028 6.618 9.957 6.593 9.875C6.569 9.794 6.572 9.707 6.602 9.628C6.632 9.549 6.687 9.481 6.759 9.437C6.759 9.437 9.688 8.043 9.688 8.043C9.753 8.024 9.810 7.986 9.854 7.935C9.897 7.884 9.925 7.821 9.934 7.754C9.943 7.687 9.932 7.620 9.903 7.559C9.874 7.498 9.828 7.447 9.771 7.412C9.771 7.412 4.375 2.799 4.375 2.799C4.109 2.517 3.781 2.300 3.418 2.163C3.055 2.026 2.666 1.974 2.280 2.009C2.280 2.009 2.000 2.015 2.000 2.015C2.000 2.015 2.000 14.513 2.000 14.513C2.000 14.645 1.948 14.772 1.854 14.865C1.761 14.959 1.634 15.011 1.502 15.011C1.502 15.011 0.498 15.011 0.498 15.011Z\" fill=\"currentColor\" transform=\"translate(3 2.984)\"/></svg>";
    readonly notification_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.986 11.043C9.986 11.043 9.986 14.504 9.986 14.504C9.986 14.635 9.934 14.762 9.841 14.855C9.748 14.948 9.621 15.000 9.490 15.000C9.490 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.443 5.979 9.067 6.878 9.472 7.881C9.877 8.883 10.053 9.964 9.986 11.043Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly notification_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.354 5.249C2.354 5.249 4.811 3.175 4.811 3.175C4.873 3.141 4.924 3.089 4.957 3.027C4.990 2.964 5.003 2.893 4.996 2.823C4.988 2.752 4.960 2.686 4.915 2.631C4.869 2.577 4.809 2.538 4.741 2.518C4.741 2.518 1.792 1.167 1.792 1.167C1.719 1.124 1.663 1.057 1.632 0.978C1.601 0.900 1.596 0.813 1.619 0.731C1.642 0.649 1.692 0.578 1.760 0.527C1.827 0.476 1.910 0.449 1.995 0.450C1.995 0.450 10.000 0.000 10.000 0.000C10.000 0.000 8.419 7.860 8.419 7.860C8.408 7.944 8.369 8.022 8.309 8.082C8.249 8.142 8.171 8.181 8.087 8.192C8.003 8.203 7.918 8.186 7.844 8.144C7.771 8.102 7.713 8.037 7.680 7.959C7.680 7.959 6.762 4.849 6.762 4.849C6.752 4.778 6.721 4.713 6.674 4.660C6.626 4.608 6.564 4.571 6.496 4.553C6.427 4.536 6.355 4.539 6.288 4.563C6.222 4.586 6.163 4.629 6.121 4.686C6.121 4.686 3.673 6.751 3.673 6.751C3.106 7.276 2.663 7.920 2.375 8.636C2.087 9.353 1.962 10.125 2.008 10.895C2.008 10.895 2.013 14.503 2.013 14.503C2.013 14.568 2.000 14.633 1.975 14.693C1.950 14.754 1.914 14.808 1.868 14.854C1.821 14.901 1.767 14.937 1.707 14.962C1.646 14.987 1.582 15.000 1.516 15.000C1.516 15.000 0.510 15.000 0.510 15.000C0.378 15.000 0.252 14.948 0.159 14.855C0.066 14.762 0.014 14.635 0.014 14.504C0.014 14.504 0.014 11.043 0.014 11.043C-0.053 9.964 0.123 8.883 0.528 7.881C0.932 6.878 1.556 5.979 2.354 5.249Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly notification_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly off_ramp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M13.494 0.019C13.494 0.019 12.488 0.019 12.488 0.019C12.423 0.019 12.358 0.032 12.298 0.057C12.237 0.082 12.183 0.118 12.137 0.164C12.090 0.211 12.054 0.265 12.029 0.326C12.004 0.386 11.991 0.451 11.991 0.516C11.991 0.516 11.991 15.522 11.991 15.522C11.991 15.587 12.004 15.652 12.029 15.712C12.054 15.772 12.090 15.827 12.137 15.873C12.183 15.919 12.237 15.956 12.298 15.981C12.358 16.006 12.423 16.019 12.488 16.019C12.488 16.019 13.494 16.019 13.494 16.019C13.559 16.019 13.624 16.006 13.684 15.981C13.744 15.956 13.799 15.919 13.845 15.873C13.891 15.827 13.928 15.772 13.953 15.712C13.978 15.652 13.991 15.587 13.991 15.522C13.991 15.522 13.991 0.516 13.991 0.516C13.991 0.451 13.978 0.386 13.953 0.326C13.928 0.265 13.891 0.211 13.845 0.164C13.799 0.118 13.744 0.082 13.684 0.057C13.624 0.032 13.559 0.019 13.494 0.019Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(1.009 1.981)\"/><path d=\"M7.992 4.001C7.992 4.001 6.004 4.001 6.004 4.001C5.935 4.016 5.863 4.010 5.797 3.984C5.731 3.958 5.675 3.914 5.634 3.855C5.594 3.797 5.572 3.729 5.570 3.658C5.569 3.587 5.589 3.518 5.627 3.458C5.627 3.458 6.971 0.506 6.971 0.506C6.997 0.426 6.996 0.339 6.968 0.259C6.940 0.179 6.886 0.110 6.815 0.063C6.744 0.017 6.660 -0.005 6.575 0.001C6.491 0.007 6.411 0.041 6.347 0.097C6.347 0.097 0.000 5.019 0.000 5.019C0.000 5.019 6.347 9.904 6.347 9.904C6.411 9.960 6.491 9.994 6.575 10.000C6.660 10.006 6.744 9.984 6.815 9.938C6.886 9.891 6.940 9.823 6.968 9.743C6.996 9.663 6.997 9.576 6.971 9.495C6.971 9.495 5.627 6.543 5.627 6.543C5.589 6.483 5.569 6.414 5.570 6.343C5.572 6.273 5.594 6.204 5.634 6.146C5.675 6.088 5.731 6.043 5.797 6.017C5.863 5.991 5.935 5.985 6.004 6.001C6.004 6.001 8.003 6.001 8.003 6.001C9.070 6.019 10.087 6.456 10.833 7.219C11.580 7.981 11.996 9.007 11.992 10.074C11.992 10.074 11.992 15.523 11.992 15.523C11.992 15.588 12.004 15.652 12.029 15.712C12.054 15.772 12.090 15.827 12.137 15.873C12.183 15.919 12.237 15.956 12.298 15.981C12.358 16.006 12.423 16.019 12.488 16.019C12.488 16.019 13.494 16.019 13.494 16.019C13.559 16.019 13.624 16.006 13.684 15.981C13.744 15.956 13.799 15.919 13.845 15.873C13.891 15.827 13.928 15.772 13.953 15.712C13.978 15.652 13.991 15.587 13.991 15.522C13.991 15.522 13.989 14.019 13.989 14.019C13.989 14.019 13.992 14.019 13.992 14.019C13.992 14.019 13.992 10.074 13.992 10.074C13.995 8.477 13.368 6.942 12.247 5.805C11.125 4.668 9.600 4.019 8.003 4.001C8.003 4.001 7.992 3.999 7.992 3.999Z\" fill=\"currentColor\" transform=\"translate(1.009 1.981)\"/></svg>";
    readonly off_ramp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M1.503 0.019C1.503 0.019 0.497 0.019 0.497 0.019C0.432 0.019 0.367 0.032 0.307 0.057C0.247 0.082 0.192 0.118 0.146 0.164C0.099 0.211 0.063 0.265 0.038 0.326C0.013 0.386 -0.000 0.451 0.000 0.516C0.000 0.516 0.000 15.522 0.000 15.522C-0.000 15.587 0.013 15.652 0.038 15.712C0.063 15.772 0.099 15.827 0.146 15.873C0.192 15.919 0.247 15.956 0.307 15.981C0.367 16.006 0.432 16.019 0.497 16.019C0.497 16.019 1.503 16.019 1.503 16.019C1.568 16.019 1.633 16.006 1.693 15.981C1.753 15.956 1.808 15.919 1.854 15.873C1.901 15.827 1.937 15.772 1.962 15.712C1.987 15.652 2.000 15.587 2.000 15.522C2.000 15.522 2.000 0.516 2.000 0.516C2.000 0.451 1.987 0.386 1.962 0.326C1.937 0.265 1.901 0.211 1.854 0.164C1.808 0.118 1.753 0.082 1.693 0.057C1.633 0.032 1.568 0.019 1.503 0.019Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(5 1.981)\"/><path d=\"M6.008 6.001C6.008 6.001 7.996 6.001 7.996 6.001C8.065 5.985 8.137 5.991 8.203 6.017C8.269 6.043 8.325 6.088 8.366 6.146C8.406 6.204 8.428 6.272 8.430 6.343C8.431 6.414 8.411 6.483 8.373 6.543C8.373 6.543 7.029 9.495 7.029 9.495C7.003 9.576 7.004 9.663 7.032 9.743C7.060 9.823 7.114 9.891 7.185 9.938C7.256 9.984 7.340 10.006 7.425 10.000C7.509 9.994 7.589 9.960 7.653 9.904C7.653 9.904 14.000 5.019 14.000 5.019C14.000 5.019 7.653 0.097 7.653 0.097C7.589 0.041 7.509 0.007 7.425 0.001C7.340 -0.005 7.256 0.017 7.185 0.063C7.114 0.110 7.060 0.179 7.032 0.259C7.004 0.339 7.003 0.426 7.029 0.506C7.029 0.506 8.373 3.458 8.373 3.458C8.411 3.518 8.431 3.587 8.430 3.658C8.428 3.729 8.406 3.797 8.366 3.855C8.325 3.914 8.269 3.958 8.203 3.984C8.137 4.010 8.065 4.016 7.996 4.001C7.996 4.001 6.008 4.001 6.008 4.001C6.008 4.001 6.008 3.999 6.008 3.999C6.008 3.999 5.997 4.001 5.997 4.001C4.400 4.019 2.875 4.668 1.753 5.805C0.632 6.942 0.005 8.477 0.008 10.074C0.008 10.074 0.008 14.019 0.008 14.019C0.008 14.019 0.011 14.019 0.011 14.019C0.011 14.019 0.009 15.522 0.009 15.522C0.009 15.587 0.022 15.652 0.047 15.712C0.072 15.772 0.108 15.827 0.155 15.873C0.201 15.919 0.255 15.956 0.316 15.981C0.376 16.006 0.440 16.019 0.506 16.019C0.506 16.019 1.512 16.019 1.512 16.019C1.644 16.019 1.770 15.967 1.863 15.874C1.956 15.780 2.008 15.654 2.008 15.523C2.008 15.523 2.008 10.074 2.008 10.074C2.004 9.007 2.420 7.981 3.167 7.219C3.913 6.456 4.930 6.019 5.997 6.001C5.997 6.001 6.008 6.001 6.008 6.001Z\" fill=\"currentColor\" transform=\"translate(5 1.981)\"/></svg>";
    readonly off_ramp_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M9.503 0.000C9.503 0.000 8.497 0.000 8.497 0.000C8.432 0.000 8.367 0.013 8.307 0.038C8.247 0.063 8.192 0.099 8.146 0.146C8.099 0.192 8.063 0.247 8.038 0.307C8.013 0.367 8.000 0.432 8.000 0.497C8.000 0.497 8.000 15.503 8.000 15.503C8.000 15.568 8.013 15.633 8.038 15.693C8.063 15.753 8.099 15.808 8.146 15.854C8.192 15.901 8.247 15.937 8.307 15.962C8.367 15.987 8.432 16.000 8.497 16.000C8.497 16.000 9.503 16.000 9.503 16.000C9.568 16.000 9.633 15.987 9.693 15.962C9.753 15.937 9.808 15.901 9.854 15.854C9.901 15.808 9.937 15.753 9.962 15.693C9.987 15.633 10.000 15.568 10.000 15.503C10.000 15.503 10.000 0.497 10.000 0.497C10.000 0.432 9.987 0.367 9.962 0.307C9.937 0.247 9.901 0.192 9.854 0.146C9.808 0.099 9.753 0.063 9.693 0.038C9.633 0.013 9.568 0.000 9.503 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(5 2)\"/><path d=\"M7.646 6.249C7.646 6.249 5.189 4.175 5.189 4.175C5.126 4.141 5.076 4.089 5.043 4.027C5.010 3.964 4.997 3.893 5.004 3.823C5.012 3.752 5.040 3.686 5.085 3.631C5.130 3.577 5.191 3.538 5.259 3.518C5.259 3.518 8.208 2.167 8.208 2.167C8.280 2.124 8.337 2.057 8.368 1.978C8.399 1.900 8.403 1.813 8.380 1.731C8.357 1.649 8.308 1.578 8.240 1.527C8.172 1.476 8.090 1.449 8.005 1.450C8.005 1.450 0.000 1.000 0.000 1.000C0.000 1.000 1.580 8.860 1.580 8.860C1.592 8.944 1.630 9.022 1.690 9.082C1.750 9.142 1.828 9.181 1.912 9.192C1.996 9.203 2.082 9.186 2.155 9.144C2.229 9.102 2.287 9.037 2.320 8.959C2.320 8.959 3.238 5.849 3.238 5.849C3.248 5.778 3.279 5.713 3.326 5.660C3.373 5.608 3.435 5.571 3.504 5.553C3.572 5.536 3.645 5.539 3.711 5.563C3.778 5.586 3.836 5.629 3.878 5.686C3.878 5.686 6.327 7.751 6.327 7.751C6.894 8.276 7.337 8.920 7.625 9.636C7.913 10.353 8.038 11.125 7.992 11.895C7.992 11.895 7.987 15.503 7.987 15.503C7.987 15.568 8.000 15.633 8.025 15.693C8.049 15.754 8.086 15.808 8.132 15.854C8.178 15.901 8.233 15.937 8.293 15.962C8.353 15.987 8.418 16.000 8.483 16.000C8.483 16.000 9.490 16.000 9.490 16.000C9.621 16.000 9.748 15.948 9.841 15.855C9.934 15.762 9.986 15.635 9.986 15.504C9.986 15.504 9.986 12.043 9.986 12.043C10.053 10.964 9.877 9.883 9.472 8.881C9.067 7.878 8.443 6.979 7.646 6.249Z\" fill=\"currentColor\" transform=\"translate(5 2)\"/></svg>";
    readonly off_ramp_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M1.503 0.000C1.503 0.000 0.497 0.000 0.497 0.000C0.432 0.000 0.367 0.013 0.307 0.038C0.247 0.063 0.192 0.099 0.146 0.146C0.099 0.192 0.063 0.247 0.038 0.307C0.013 0.367 -0.000 0.432 0.000 0.497C0.000 0.497 0.000 15.503 0.000 15.503C-0.000 15.568 0.013 15.633 0.038 15.693C0.063 15.753 0.099 15.808 0.146 15.854C0.192 15.901 0.247 15.937 0.307 15.962C0.367 15.987 0.432 16.000 0.497 16.000C0.497 16.000 1.503 16.000 1.503 16.000C1.568 16.000 1.633 15.987 1.693 15.962C1.753 15.937 1.808 15.901 1.854 15.854C1.901 15.808 1.937 15.753 1.962 15.693C1.987 15.633 2.000 15.568 2.000 15.503C2.000 15.503 2.000 0.497 2.000 0.497C2.000 0.432 1.987 0.367 1.962 0.307C1.937 0.247 1.901 0.192 1.854 0.146C1.808 0.099 1.753 0.063 1.693 0.038C1.633 0.013 1.568 0.000 1.503 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" transform=\"translate(7 2)\"/><path d=\"M0.014 12.043C0.014 12.043 0.014 15.504 0.014 15.504C0.014 15.635 0.066 15.762 0.159 15.855C0.252 15.948 0.379 16.000 0.510 16.000C0.510 16.000 1.517 16.000 1.517 16.000C1.582 16.000 1.647 15.987 1.707 15.962C1.767 15.937 1.822 15.901 1.868 15.854C1.914 15.808 1.950 15.754 1.975 15.693C2.000 15.633 2.013 15.568 2.013 15.503C2.013 15.503 2.008 11.896 2.008 11.896C1.962 11.125 2.087 10.353 2.375 9.636C2.663 8.920 3.106 8.276 3.673 7.752C3.673 7.752 6.121 5.686 6.121 5.686C6.164 5.629 6.222 5.586 6.289 5.563C6.355 5.539 6.427 5.536 6.496 5.553C6.565 5.571 6.626 5.608 6.674 5.661C6.721 5.713 6.752 5.779 6.762 5.849C6.762 5.849 7.680 8.959 7.680 8.959C7.713 9.037 7.771 9.102 7.845 9.145C7.918 9.187 8.004 9.203 8.088 9.192C8.172 9.181 8.250 9.142 8.310 9.082C8.370 9.022 8.408 8.944 8.420 8.860C8.420 8.860 10.000 1.000 10.000 1.000C10.000 1.000 1.995 1.450 1.995 1.450C1.910 1.449 1.828 1.476 1.760 1.527C1.692 1.578 1.643 1.649 1.620 1.731C1.597 1.813 1.601 1.900 1.632 1.978C1.663 2.057 1.720 2.124 1.792 2.167C1.792 2.167 4.741 3.518 4.741 3.518C4.809 3.538 4.870 3.577 4.915 3.631C4.960 3.686 4.988 3.752 4.996 3.823C5.003 3.893 4.990 3.964 4.957 4.027C4.924 4.089 4.874 4.141 4.811 4.175C4.811 4.175 2.354 6.249 2.354 6.249C1.557 6.979 0.933 7.878 0.528 8.881C0.123 9.883 -0.053 10.964 0.014 12.043Z\" fill=\"currentColor\" transform=\"translate(7 2)\"/></svg>";
    readonly on_ramp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M7.992 3.999C7.992 3.999 8.003 4.001 8.003 4.001C9.600 4.019 11.125 4.668 12.247 5.805C13.368 6.942 13.995 8.477 13.992 10.074C13.992 10.074 13.992 14.019 13.992 14.019C13.992 14.019 13.989 14.019 13.989 14.019C13.989 14.019 13.991 15.522 13.991 15.522C13.991 15.587 13.978 15.652 13.953 15.712C13.928 15.772 13.892 15.827 13.845 15.873C13.799 15.919 13.745 15.956 13.684 15.981C13.624 16.006 13.560 16.019 13.494 16.019C13.494 16.019 12.488 16.019 12.488 16.019C12.356 16.019 12.230 15.967 12.137 15.874C12.044 15.780 11.992 15.654 11.992 15.523C11.992 15.523 11.992 10.074 11.992 10.074C11.996 9.007 11.580 7.981 10.833 7.219C10.087 6.456 9.070 6.019 8.003 6.001C8.003 6.001 6.004 6.001 6.004 6.001C5.935 5.985 5.863 5.991 5.797 6.017C5.731 6.043 5.675 6.088 5.634 6.146C5.594 6.204 5.572 6.272 5.570 6.343C5.569 6.414 5.589 6.483 5.627 6.543C5.627 6.543 6.971 9.495 6.971 9.495C6.997 9.576 6.996 9.663 6.968 9.743C6.940 9.823 6.886 9.891 6.815 9.938C6.744 9.984 6.660 10.006 6.575 10.000C6.491 9.994 6.411 9.960 6.347 9.904C6.347 9.904 0.000 5.019 0.000 5.019C0.000 5.019 6.347 0.097 6.347 0.097C6.411 0.041 6.491 0.007 6.575 0.001C6.660 -0.005 6.744 0.017 6.815 0.063C6.886 0.110 6.940 0.179 6.968 0.259C6.996 0.339 6.997 0.426 6.971 0.506C6.971 0.506 5.627 3.458 5.627 3.458C5.589 3.518 5.569 3.587 5.570 3.658C5.572 3.729 5.594 3.797 5.634 3.855C5.675 3.914 5.731 3.958 5.797 3.984C5.863 4.010 5.935 4.016 6.004 4.001C6.004 4.001 7.992 4.001 7.992 4.001Z\" fill=\"currentColor\" transform=\"translate(1.009 1.981)\"/></svg>";
    readonly on_ramp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M5.989 6.001C4.922 6.019 3.905 6.456 3.159 7.219C2.412 7.981 1.996 9.007 2.000 10.074C2.000 10.074 2.000 15.523 2.000 15.523C2.000 15.654 1.948 15.780 1.855 15.873C1.762 15.967 1.635 16.019 1.504 16.019C1.504 16.019 0.497 16.019 0.497 16.019C0.432 16.019 0.368 16.006 0.307 15.981C0.247 15.956 0.192 15.919 0.146 15.873C0.100 15.827 0.064 15.772 0.039 15.712C0.014 15.652 0.001 15.587 0.001 15.522C0.001 15.522 0.003 14.019 0.003 14.019C0.003 14.019 0.000 14.019 0.000 14.019C0.000 14.019 0.000 10.074 0.000 10.074C-0.004 8.477 0.624 6.942 1.745 5.805C2.867 4.668 4.392 4.019 5.989 4.001C5.989 4.001 6.000 3.999 6.000 3.999C6.000 3.999 6.000 4.001 6.000 4.001C6.000 4.001 7.988 4.001 7.988 4.001C8.057 4.016 8.129 4.010 8.195 3.984C8.260 3.958 8.317 3.914 8.358 3.855C8.398 3.797 8.420 3.729 8.422 3.658C8.423 3.587 8.403 3.518 8.365 3.458C8.365 3.458 7.021 0.506 7.021 0.506C6.995 0.426 6.996 0.339 7.024 0.259C7.052 0.179 7.106 0.110 7.177 0.063C7.247 0.017 7.332 -0.005 7.416 0.001C7.501 0.007 7.581 0.041 7.645 0.097C7.645 0.097 13.992 5.019 13.992 5.019C13.992 5.019 7.645 9.904 7.645 9.904C7.581 9.960 7.501 9.994 7.416 10.000C7.332 10.006 7.247 9.984 7.177 9.938C7.106 9.891 7.052 9.823 7.024 9.743C6.996 9.663 6.995 9.575 7.021 9.495C7.021 9.495 8.365 6.543 8.365 6.543C8.403 6.483 8.423 6.414 8.422 6.343C8.420 6.272 8.398 6.204 8.358 6.146C8.317 6.088 8.260 6.043 8.195 6.017C8.129 5.991 8.057 5.985 7.988 6.001C7.988 6.001 5.989 6.001 5.989 6.001Z\" fill=\"currentColor\" transform=\"translate(5.008 1.981)\"/></svg>";
    readonly on_ramp_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M13.481 15.011C13.349 15.011 13.223 14.959 13.129 14.865C13.036 14.772 12.983 14.645 12.983 14.513C12.983 14.513 12.983 2.015 12.983 2.015C12.983 2.015 12.704 2.009 12.704 2.009C12.317 1.974 11.928 2.026 11.565 2.163C11.202 2.300 10.875 2.517 10.608 2.799C10.608 2.799 5.213 7.412 5.213 7.412C5.155 7.447 5.109 7.498 5.080 7.559C5.051 7.620 5.041 7.687 5.049 7.754C5.058 7.821 5.086 7.884 5.130 7.935C5.173 7.986 5.231 8.024 5.295 8.043C5.295 8.043 8.224 9.437 8.224 9.437C8.296 9.481 8.351 9.549 8.381 9.628C8.411 9.707 8.414 9.794 8.390 9.875C8.366 9.957 8.316 10.028 8.247 10.078C8.178 10.127 8.095 10.153 8.010 10.151C8.010 10.151 0.000 10.483 0.000 10.483C0.000 10.483 1.696 2.647 1.696 2.647C1.708 2.564 1.748 2.486 1.809 2.427C1.870 2.368 1.948 2.331 2.032 2.320C2.117 2.310 2.202 2.328 2.275 2.372C2.348 2.415 2.405 2.481 2.436 2.559C2.436 2.559 3.309 5.683 3.309 5.683C3.318 5.753 3.348 5.819 3.394 5.872C3.441 5.926 3.502 5.964 3.570 5.982C3.639 6.001 3.711 5.998 3.778 5.976C3.845 5.953 3.904 5.911 3.947 5.855C3.947 5.855 3.951 5.859 3.951 5.859C3.951 5.859 9.343 1.250 9.343 1.250C9.790 0.820 10.322 0.487 10.904 0.273C11.487 0.059 12.108 -0.031 12.728 0.009C12.728 0.009 12.992 0.011 12.992 0.011C13.257 0.002 13.521 0.048 13.768 0.146C14.015 0.244 14.239 0.392 14.426 0.580C14.792 0.977 14.992 1.500 14.983 2.040C14.983 2.040 14.983 14.513 14.983 14.513C14.983 14.579 14.971 14.643 14.945 14.704C14.920 14.764 14.884 14.819 14.838 14.865C14.791 14.912 14.736 14.948 14.676 14.973C14.616 14.998 14.551 15.011 14.486 15.011C14.486 15.011 13.481 15.011 13.481 15.011Z\" fill=\"currentColor\" transform=\"translate(2.017 2.989)\"/></svg>";
    readonly on_ramp_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M0.498 15.011C0.432 15.011 0.368 14.998 0.307 14.973C0.247 14.948 0.192 14.912 0.146 14.865C0.100 14.819 0.063 14.764 0.038 14.704C0.013 14.643 -0.000 14.579 0.000 14.513C0.000 14.513 0.000 2.040 0.000 2.040C-0.008 1.500 0.191 0.977 0.558 0.580C0.745 0.392 0.969 0.244 1.215 0.146C1.462 0.048 1.726 0.002 1.992 0.011C1.992 0.011 2.256 0.009 2.256 0.009C2.875 -0.031 3.496 0.059 4.079 0.273C4.662 0.487 5.194 0.820 5.641 1.250C5.641 1.250 11.033 5.859 11.033 5.859C11.033 5.859 11.036 5.855 11.036 5.855C11.080 5.911 11.138 5.953 11.205 5.976C11.272 5.998 11.345 6.001 11.413 5.982C11.481 5.964 11.543 5.926 11.589 5.872C11.636 5.819 11.665 5.753 11.674 5.683C11.674 5.683 12.547 2.559 12.547 2.559C12.579 2.481 12.636 2.415 12.709 2.372C12.782 2.328 12.867 2.310 12.951 2.320C13.035 2.331 13.114 2.368 13.175 2.427C13.235 2.486 13.275 2.564 13.288 2.647C13.288 2.647 14.983 10.483 14.983 10.483C14.983 10.483 6.973 10.151 6.973 10.151C6.888 10.153 6.805 10.127 6.736 10.078C6.668 10.028 6.618 9.957 6.593 9.875C6.569 9.794 6.572 9.707 6.602 9.628C6.632 9.549 6.687 9.481 6.759 9.437C6.759 9.437 9.688 8.043 9.688 8.043C9.753 8.024 9.810 7.986 9.854 7.935C9.897 7.884 9.925 7.821 9.934 7.754C9.943 7.687 9.932 7.620 9.903 7.559C9.874 7.498 9.828 7.447 9.771 7.412C9.771 7.412 4.375 2.799 4.375 2.799C4.109 2.517 3.781 2.300 3.418 2.163C3.055 2.026 2.666 1.974 2.280 2.009C2.280 2.009 2.000 2.015 2.000 2.015C2.000 2.015 2.000 14.513 2.000 14.513C2.000 14.645 1.948 14.772 1.854 14.865C1.761 14.959 1.634 15.011 1.502 15.011C1.502 15.011 0.498 15.011 0.498 15.011Z\" fill=\"currentColor\" transform=\"translate(3 2.984)\"/></svg>";
    readonly on_ramp_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.986 11.043C9.986 11.043 9.986 14.504 9.986 14.504C9.986 14.635 9.934 14.762 9.841 14.855C9.748 14.948 9.621 15.000 9.490 15.000C9.490 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.443 5.979 9.067 6.878 9.472 7.881C9.877 8.883 10.053 9.964 9.986 11.043Z\" fill=\"currentColor\" transform=\"translate(3 3)\"/></svg>";
    readonly on_ramp_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.354 5.249C2.354 5.249 4.811 3.175 4.811 3.175C4.873 3.141 4.924 3.089 4.957 3.027C4.990 2.964 5.003 2.893 4.996 2.823C4.988 2.752 4.960 2.686 4.915 2.631C4.869 2.577 4.809 2.538 4.741 2.518C4.741 2.518 1.792 1.167 1.792 1.167C1.719 1.124 1.663 1.057 1.632 0.978C1.601 0.900 1.596 0.813 1.619 0.731C1.642 0.649 1.692 0.578 1.760 0.527C1.827 0.476 1.910 0.449 1.995 0.450C1.995 0.450 10.000 0.000 10.000 0.000C10.000 0.000 8.419 7.860 8.419 7.860C8.408 7.944 8.369 8.022 8.309 8.082C8.249 8.142 8.171 8.181 8.087 8.192C8.003 8.203 7.918 8.186 7.844 8.144C7.771 8.102 7.713 8.037 7.680 7.959C7.680 7.959 6.762 4.849 6.762 4.849C6.752 4.778 6.721 4.713 6.674 4.660C6.626 4.608 6.564 4.571 6.496 4.553C6.427 4.536 6.355 4.539 6.288 4.563C6.222 4.586 6.163 4.629 6.121 4.686C6.121 4.686 3.673 6.751 3.673 6.751C3.106 7.276 2.663 7.920 2.375 8.636C2.087 9.353 1.962 10.125 2.008 10.895C2.008 10.895 2.013 14.503 2.013 14.503C2.013 14.568 2.000 14.633 1.975 14.693C1.950 14.754 1.914 14.808 1.868 14.854C1.822 14.901 1.767 14.937 1.707 14.962C1.646 14.987 1.582 15.000 1.516 15.000C1.516 15.000 0.510 15.000 0.510 15.000C0.378 15.000 0.252 14.948 0.159 14.855C0.066 14.762 0.014 14.635 0.014 14.504C0.014 14.504 0.014 11.043 0.014 11.043C-0.053 9.964 0.123 8.883 0.528 7.881C0.932 6.878 1.556 5.979 2.354 5.249Z\" fill=\"currentColor\" transform=\"translate(7 3)\"/></svg>";
    readonly on_ramp_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly rotary: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(8.500 9.250)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(8.500 9.250)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.782 -1.462L2.869 -0.877A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(8.500 9.250)\"/><path d=\"M7.536 -1.219C7.536 -1.219 4.977 -1.646 4.977 -1.646C4.923 -1.664 4.866 -1.664 4.811 -1.649C4.756 -1.633 4.708 -1.601 4.672 -1.558C4.635 -1.515 4.612 -1.462 4.606 -1.406C4.600 -1.349 4.610 -1.292 4.636 -1.242L3.091 -0.828C3.088 -0.885 3.069 -0.939 3.035 -0.985C3.001 -1.030 2.955 -1.065 2.902 -1.084C2.848 -1.103 2.791 -1.106 2.736 -1.093C2.681 -1.079 2.632 -1.050 2.593 -1.007C2.593 -1.007 0.590 0.642 0.590 0.642C0.534 0.679 0.467 0.697 0.399 0.692C0.331 0.686 0.267 0.659 0.216 0.614C0.166 0.569 0.131 0.508 0.118 0.441C0.105 0.375 0.115 0.305 0.145 0.245L2.621 -5.672L7.723 -1.785C7.780 -1.748 7.823 -1.693 7.845 -1.629C7.867 -1.565 7.867 -1.495 7.847 -1.431C7.825 -1.366 7.783 -1.311 7.728 -1.272C7.671 -1.234 7.604 -1.215 7.536 -1.219Z\" fill=\"currentColor\" transform=\"translate(8.500 9.250)\"/></svg>";
    readonly rotary_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(14.000 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-4.000 -1.000L-7.750 -1.000L-7.750 1.000L-4.000 1.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-5.784 -4.494C-5.784 -4.494 -7.127 -1.542 -7.127 -1.542C-7.166 -1.482 -7.185 -1.413 -7.184 -1.342C-7.183 -1.271 -7.160 -1.203 -7.120 -1.145C-7.080 -1.087 -7.023 -1.042 -6.957 -1.016C-6.891 -0.990 -6.819 -0.984 -6.750 -1.000L-6.750 1.000C-6.819 0.985 -6.891 0.991 -6.957 1.017C-7.023 1.043 -7.080 1.087 -7.120 1.145C-7.160 1.204 -7.183 1.272 -7.184 1.343C-7.185 1.414 -7.166 1.483 -7.127 1.543C-7.127 1.543 -5.784 4.495 -5.784 4.495C-5.757 4.575 -5.758 4.662 -5.786 4.742C-5.815 4.822 -5.868 4.891 -5.939 4.938C-6.010 4.984 -6.094 5.006 -6.179 5.000C-6.263 4.994 -6.344 4.960 -6.407 4.904L-12.750 0.000L-6.407 -4.903C-6.344 -4.959 -6.263 -4.993 -6.179 -4.999C-6.094 -5.005 -6.010 -4.983 -5.939 -4.937C-5.868 -4.890 -5.815 -4.822 -5.786 -4.742C-5.758 -4.661 -5.757 -4.574 -5.784 -4.494Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/></svg>";
    readonly rotary_lht: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.782 -1.462L2.869 -0.877A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/><path d=\"M7.536 -1.219C7.536 -1.219 4.977 -1.646 4.977 -1.646C4.923 -1.664 4.866 -1.664 4.811 -1.649C4.756 -1.633 4.708 -1.601 4.672 -1.558C4.635 -1.515 4.612 -1.462 4.606 -1.406C4.600 -1.349 4.610 -1.292 4.636 -1.242L3.091 -0.828C3.088 -0.885 3.069 -0.939 3.035 -0.985C3.001 -1.030 2.955 -1.065 2.902 -1.084C2.848 -1.103 2.791 -1.106 2.736 -1.093C2.681 -1.079 2.632 -1.050 2.593 -1.007C2.593 -1.007 0.590 0.642 0.590 0.642C0.534 0.679 0.467 0.697 0.399 0.692C0.331 0.686 0.267 0.659 0.216 0.614C0.166 0.569 0.131 0.508 0.118 0.441C0.105 0.375 0.115 0.305 0.145 0.245L2.621 -5.672L7.723 -1.785C7.780 -1.748 7.823 -1.693 7.845 -1.629C7.867 -1.565 7.867 -1.495 7.847 -1.431C7.825 -1.366 7.783 -1.311 7.728 -1.272C7.671 -1.234 7.604 -1.215 7.536 -1.219Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/></svg>";
    readonly rotary_lht_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M4.000 1.000L7.750 1.000L7.750 -1.000L4.000 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M5.784 4.494C5.784 4.494 7.127 1.542 7.127 1.542C7.166 1.482 7.185 1.413 7.184 1.342C7.183 1.271 7.160 1.203 7.120 1.145C7.080 1.087 7.023 1.042 6.957 1.016C6.891 0.990 6.819 0.984 6.750 1.000L6.750 -1.000C6.819 -0.985 6.891 -0.991 6.957 -1.017C7.023 -1.043 7.080 -1.087 7.120 -1.145C7.160 -1.204 7.183 -1.272 7.184 -1.343C7.185 -1.414 7.166 -1.483 7.127 -1.543C7.127 -1.543 5.784 -4.495 5.784 -4.495C5.757 -4.575 5.758 -4.662 5.786 -4.742C5.815 -4.822 5.868 -4.891 5.939 -4.938C6.010 -4.984 6.094 -5.006 6.179 -5.000C6.263 -4.994 6.344 -4.960 6.407 -4.904L12.750 0.000L6.407 4.903C6.344 4.959 6.263 4.993 6.179 4.999C6.094 5.005 6.010 4.983 5.939 4.937C5.868 4.890 5.815 4.822 5.786 4.742C5.758 4.661 5.757 4.574 5.784 4.494Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/></svg>";
    readonly rotary_lht_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-4.000 -1.000L-7.750 -1.000L-7.750 1.000L-4.000 1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-5.784 -4.494C-5.784 -4.494 -7.127 -1.542 -7.127 -1.542C-7.166 -1.482 -7.185 -1.413 -7.184 -1.342C-7.183 -1.271 -7.160 -1.203 -7.120 -1.145C-7.080 -1.087 -7.023 -1.042 -6.957 -1.016C-6.891 -0.990 -6.819 -0.984 -6.750 -1.000L-6.750 1.000C-6.819 0.985 -6.891 0.991 -6.957 1.017C-7.023 1.043 -7.080 1.087 -7.120 1.145C-7.160 1.204 -7.183 1.272 -7.184 1.343C-7.185 1.414 -7.166 1.483 -7.127 1.543C-7.127 1.543 -5.784 4.495 -5.784 4.495C-5.757 4.575 -5.758 4.662 -5.786 4.742C-5.815 4.822 -5.868 4.891 -5.939 4.938C-6.010 4.984 -6.094 5.006 -6.179 5.000C-6.263 4.994 -6.344 4.960 -6.407 4.904L-12.750 0.000L-6.407 -4.903C-6.344 -4.959 -6.263 -4.993 -6.179 -4.999C-6.094 -5.005 -6.010 -4.983 -5.939 -4.937C-5.868 -4.890 -5.815 -4.822 -5.786 -4.742C-5.758 -4.661 -5.757 -4.574 -5.784 -4.494Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/></svg>";
    readonly rotary_lht_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.636 1.873L2.782 1.124A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M3.002 2.206L6.184 5.388L7.598 3.973L4.416 0.791Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M2.323 6.468C2.323 6.468 5.360 5.330 5.360 5.330C5.430 5.315 5.492 5.280 5.542 5.229C5.591 5.178 5.623 5.114 5.636 5.045C5.648 4.975 5.640 4.903 5.612 4.838C5.583 4.773 5.537 4.718 5.477 4.680L6.891 3.266C6.929 3.326 6.984 3.372 7.049 3.401C7.114 3.429 7.186 3.438 7.255 3.425C7.325 3.412 7.389 3.380 7.440 3.331C7.491 3.281 7.526 3.219 7.541 3.149C7.541 3.149 8.679 0.112 8.679 0.112C8.716 0.036 8.779 -0.025 8.855 -0.061C8.932 -0.098 9.018 -0.109 9.102 -0.092C9.185 -0.074 9.260 -0.030 9.315 0.034C9.371 0.098 9.404 0.179 9.409 0.263L10.426 8.216L2.474 7.198C2.390 7.193 2.309 7.160 2.245 7.104C2.181 7.049 2.137 6.974 2.119 6.891C2.102 6.807 2.113 6.722 2.149 6.645C2.186 6.568 2.247 6.505 2.323 6.468Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/></svg>";
    readonly rotary_lht_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -4.636 1.873L-2.782 1.124A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M-4.416 0.791L-7.598 3.973L-6.184 5.388L-3.002 2.206Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M-8.678 0.112C-8.678 0.112 -7.541 3.150 -7.541 3.150C-7.526 3.220 -7.490 3.282 -7.439 3.331C-7.389 3.381 -7.324 3.413 -7.255 3.425C-7.186 3.438 -7.113 3.430 -7.048 3.401C-6.983 3.373 -6.928 3.326 -6.891 3.266L-5.477 4.680C-5.536 4.719 -5.583 4.774 -5.611 4.839C-5.639 4.904 -5.648 4.975 -5.636 5.045C-5.622 5.115 -5.590 5.179 -5.541 5.230C-5.491 5.281 -5.429 5.316 -5.359 5.331C-5.359 5.331 -2.322 6.469 -2.322 6.469C-2.246 6.506 -2.186 6.568 -2.149 6.645C-2.113 6.722 -2.101 6.808 -2.118 6.892C-2.136 6.974 -2.180 7.049 -2.244 7.105C-2.308 7.160 -2.389 7.193 -2.473 7.198L-10.426 8.216L-9.408 0.264C-9.403 0.180 -9.370 0.098 -9.315 0.035C-9.259 -0.030 -9.184 -0.073 -9.101 -0.091C-9.018 -0.108 -8.932 -0.098 -8.855 -0.061C-8.778 -0.024 -8.716 0.037 -8.678 0.112Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/></svg>";
    readonly rotary_lht_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M3.300 1.000L5.550 1.000L5.550 -1.000L3.300 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M5.500 1.000A4.000 4.000 0 0 0 8.328 -0.172L6.914 -1.586A2.000 2.000 0 0 1 5.500 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M8.293 -0.136L9.919 -1.763L8.505 -3.177L6.879 -1.550Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M11.000 2.098C11.000 2.098 9.862 -0.939 9.862 -0.939C9.847 -1.009 9.812 -1.071 9.761 -1.121C9.710 -1.170 9.646 -1.202 9.576 -1.215C9.507 -1.227 9.435 -1.219 9.370 -1.191C9.305 -1.162 9.250 -1.116 9.212 -1.055L7.798 -2.470C7.857 -2.508 7.904 -2.563 7.932 -2.628C7.961 -2.693 7.970 -2.765 7.957 -2.834C7.944 -2.904 7.912 -2.968 7.862 -3.019C7.813 -3.070 7.751 -3.105 7.681 -3.120C7.681 -3.120 4.644 -4.258 4.644 -4.258C4.568 -4.295 4.507 -4.358 4.470 -4.434C4.434 -4.511 4.423 -4.597 4.440 -4.681C4.458 -4.764 4.502 -4.838 4.566 -4.894C4.630 -4.949 4.711 -4.983 4.795 -4.988L12.748 -6.005L11.730 1.947C11.725 2.031 11.691 2.112 11.636 2.176C11.580 2.240 11.505 2.284 11.423 2.302C11.339 2.319 11.254 2.308 11.177 2.272C11.100 2.235 11.037 2.174 11.000 2.098Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/></svg>";
    readonly rotary_lht_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-3.300 -1.000L-5.550 -1.000L-5.550 1.000L-3.300 1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-5.500 1.000A4.000 4.000 0 0 1 -8.328 -0.172L-6.914 -1.586A2.000 2.000 0 0 0 -5.500 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-6.879 -1.550L-8.505 -3.177L-9.919 -1.763L-8.293 -0.136Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-4.644 -4.257C-4.644 -4.257 -7.681 -3.120 -7.681 -3.120C-7.751 -3.105 -7.814 -3.069 -7.863 -3.018C-7.913 -2.967 -7.944 -2.903 -7.957 -2.834C-7.970 -2.765 -7.961 -2.692 -7.933 -2.627C-7.905 -2.562 -7.858 -2.507 -7.798 -2.470L-9.212 -1.055C-9.250 -1.115 -9.306 -1.162 -9.371 -1.190C-9.436 -1.218 -9.507 -1.227 -9.576 -1.215C-9.646 -1.201 -9.711 -1.169 -9.762 -1.120C-9.813 -1.070 -9.848 -1.008 -9.863 -0.938C-9.863 -0.938 -11.001 2.099 -11.001 2.099C-11.038 2.175 -11.100 2.235 -11.177 2.272C-11.254 2.308 -11.340 2.320 -11.423 2.303C-11.506 2.285 -11.581 2.241 -11.637 2.177C-11.692 2.113 -11.725 2.032 -11.730 1.948L-12.748 -6.005L-4.796 -4.987C-4.712 -4.982 -4.630 -4.949 -4.567 -4.894C-4.502 -4.838 -4.458 -4.763 -4.441 -4.680C-4.424 -4.597 -4.434 -4.511 -4.470 -4.434C-4.508 -4.357 -4.569 -4.295 -4.644 -4.257Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/></svg>";
    readonly rotary_lht_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M-1.000 2.828L-1.000 6.000Q-1.000 6.500 -0.500 6.500L0.500 6.500Q1.000 6.500 1.000 6.000L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 0.000 -5.000L0.000 -3.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M1.000 -4.000L1.000 -7.000L-1.000 -7.000L-1.000 -4.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M4.494 -5.034C4.494 -5.034 1.542 -6.377 1.542 -6.377C1.482 -6.416 1.413 -6.435 1.342 -6.434C1.271 -6.433 1.203 -6.410 1.145 -6.370C1.087 -6.330 1.042 -6.273 1.016 -6.207C0.990 -6.141 0.984 -6.069 1.000 -6.000L-1.000 -6.000C-0.985 -6.069 -0.991 -6.141 -1.017 -6.207C-1.043 -6.273 -1.087 -6.330 -1.145 -6.370C-1.204 -6.410 -1.272 -6.433 -1.343 -6.434C-1.414 -6.435 -1.483 -6.416 -1.543 -6.377C-1.543 -6.377 -4.495 -5.034 -4.495 -5.034C-4.575 -5.007 -4.662 -5.008 -4.742 -5.036C-4.822 -5.065 -4.891 -5.118 -4.938 -5.189C-4.984 -5.260 -5.006 -5.344 -5.000 -5.429C-4.994 -5.513 -4.960 -5.594 -4.904 -5.657L0.000 -12.000L4.903 -5.657C4.959 -5.594 4.993 -5.513 4.999 -5.429C5.005 -5.344 4.983 -5.260 4.937 -5.189C4.890 -5.118 4.822 -5.065 4.742 -5.036C4.661 -5.008 4.574 -5.007 4.494 -5.034Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/></svg>";
    readonly rotary_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(6.250 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/><path d=\"M4.000 1.000L7.750 1.000L7.750 -1.000L4.000 -1.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/><path d=\"M5.784 4.494C5.784 4.494 7.127 1.542 7.127 1.542C7.166 1.482 7.185 1.413 7.184 1.342C7.183 1.271 7.160 1.203 7.120 1.145C7.080 1.087 7.023 1.042 6.957 1.016C6.891 0.990 6.819 0.984 6.750 1.000L6.750 -1.000C6.819 -0.985 6.891 -0.991 6.957 -1.017C7.023 -1.043 7.080 -1.087 7.120 -1.145C7.160 -1.204 7.183 -1.272 7.184 -1.343C7.185 -1.414 7.166 -1.483 7.127 -1.543C7.127 -1.543 5.784 -4.495 5.784 -4.495C5.757 -4.575 5.758 -4.662 5.786 -4.742C5.815 -4.822 5.868 -4.891 5.939 -4.938C6.010 -4.984 6.094 -5.006 6.179 -5.000C6.263 -4.994 6.344 -4.960 6.407 -4.904L12.750 0.000L6.407 4.903C6.344 4.959 6.263 4.993 6.179 4.999C6.094 5.005 6.010 4.983 5.939 4.937C5.868 4.890 5.815 4.822 5.786 4.742C5.758 4.661 5.757 4.574 5.784 4.494Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/></svg>";
    readonly rotary_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(12.750 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -4.636 1.873L-2.782 1.124A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/><path d=\"M-4.416 0.791L-7.598 3.973L-6.184 5.388L-3.002 2.206Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/><path d=\"M-8.678 0.112C-8.678 0.112 -7.541 3.150 -7.541 3.150C-7.526 3.220 -7.490 3.282 -7.439 3.331C-7.389 3.381 -7.324 3.413 -7.255 3.425C-7.186 3.438 -7.113 3.430 -7.048 3.401C-6.983 3.373 -6.928 3.326 -6.891 3.266L-5.477 4.680C-5.536 4.719 -5.583 4.774 -5.611 4.839C-5.639 4.904 -5.648 4.975 -5.636 5.045C-5.622 5.115 -5.590 5.179 -5.541 5.230C-5.491 5.281 -5.429 5.316 -5.359 5.331C-5.359 5.331 -2.322 6.469 -2.322 6.469C-2.246 6.506 -2.186 6.568 -2.149 6.645C-2.113 6.722 -2.101 6.808 -2.118 6.892C-2.136 6.974 -2.180 7.049 -2.244 7.105C-2.308 7.160 -2.389 7.193 -2.473 7.198L-10.426 8.216L-9.408 0.264C-9.403 0.180 -9.370 0.098 -9.315 0.035C-9.259 -0.030 -9.184 -0.073 -9.101 -0.091C-9.018 -0.108 -8.932 -0.098 -8.855 -0.061C-8.778 -0.024 -8.716 0.037 -8.678 0.112Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/></svg>";
    readonly rotary_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(7.250 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.636 1.873L2.782 1.124A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/><path d=\"M3.002 2.206L6.184 5.388L7.598 3.973L4.416 0.791Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/><path d=\"M2.323 6.468C2.323 6.468 5.360 5.330 5.360 5.330C5.430 5.315 5.492 5.280 5.542 5.229C5.591 5.178 5.623 5.114 5.636 5.045C5.648 4.975 5.640 4.903 5.612 4.838C5.583 4.773 5.537 4.718 5.477 4.680L6.891 3.266C6.929 3.326 6.984 3.372 7.049 3.401C7.114 3.429 7.186 3.438 7.255 3.425C7.325 3.412 7.389 3.380 7.440 3.331C7.491 3.281 7.526 3.219 7.541 3.149C7.541 3.149 8.679 0.112 8.679 0.112C8.716 0.036 8.779 -0.025 8.855 -0.061C8.932 -0.098 9.018 -0.109 9.102 -0.092C9.185 -0.074 9.260 -0.030 9.315 0.034C9.371 0.098 9.404 0.179 9.409 0.263L10.426 8.216L2.474 7.198C2.390 7.193 2.309 7.160 2.245 7.104C2.181 7.049 2.137 6.974 2.119 6.891C2.102 6.807 2.113 6.722 2.149 6.645C2.186 6.568 2.247 6.505 2.323 6.468Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/></svg>";
    readonly rotary_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(13.750 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-3.300 -1.000L-5.550 -1.000L-5.550 1.000L-3.300 1.000Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-5.500 1.000A4.000 4.000 0 0 1 -8.328 -0.172L-6.914 -1.586A2.000 2.000 0 0 0 -5.500 -1.000Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-6.879 -1.550L-8.505 -3.177L-9.919 -1.763L-8.293 -0.136Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-4.644 -4.257C-4.644 -4.257 -7.681 -3.120 -7.681 -3.120C-7.751 -3.105 -7.814 -3.069 -7.863 -3.018C-7.913 -2.967 -7.944 -2.903 -7.957 -2.834C-7.970 -2.765 -7.961 -2.692 -7.933 -2.627C-7.905 -2.562 -7.858 -2.507 -7.798 -2.470L-9.212 -1.055C-9.250 -1.115 -9.306 -1.162 -9.371 -1.190C-9.436 -1.218 -9.507 -1.227 -9.576 -1.215C-9.646 -1.201 -9.711 -1.169 -9.762 -1.120C-9.813 -1.070 -9.848 -1.008 -9.863 -0.938C-9.863 -0.938 -11.001 2.099 -11.001 2.099C-11.038 2.175 -11.100 2.235 -11.177 2.272C-11.254 2.308 -11.340 2.320 -11.423 2.303C-11.506 2.285 -11.581 2.241 -11.637 2.177C-11.692 2.113 -11.725 2.032 -11.730 1.948L-12.748 -6.005L-4.796 -4.987C-4.712 -4.982 -4.630 -4.949 -4.567 -4.894C-4.502 -4.838 -4.458 -4.763 -4.441 -4.680C-4.424 -4.597 -4.434 -4.511 -4.470 -4.434C-4.508 -4.357 -4.569 -4.295 -4.644 -4.257Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/></svg>";
    readonly rotary_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(6.250 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M3.300 1.000L5.550 1.000L5.550 -1.000L3.300 -1.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M5.500 1.000A4.000 4.000 0 0 0 8.328 -0.172L6.914 -1.586A2.000 2.000 0 0 1 5.500 -1.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M8.293 -0.136L9.919 -1.763L8.505 -3.177L6.879 -1.550Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M11.000 2.098C11.000 2.098 9.862 -0.939 9.862 -0.939C9.847 -1.009 9.812 -1.071 9.761 -1.121C9.710 -1.170 9.646 -1.202 9.576 -1.215C9.507 -1.227 9.435 -1.219 9.370 -1.191C9.305 -1.162 9.250 -1.116 9.212 -1.055L7.798 -2.470C7.857 -2.508 7.904 -2.563 7.932 -2.628C7.961 -2.693 7.970 -2.765 7.957 -2.834C7.944 -2.904 7.912 -2.968 7.862 -3.019C7.813 -3.070 7.751 -3.105 7.681 -3.120C7.681 -3.120 4.644 -4.258 4.644 -4.258C4.568 -4.295 4.507 -4.358 4.470 -4.434C4.434 -4.511 4.423 -4.597 4.440 -4.681C4.458 -4.764 4.502 -4.838 4.566 -4.894C4.630 -4.949 4.711 -4.983 4.795 -4.988L12.748 -6.005L11.730 1.947C11.725 2.031 11.691 2.112 11.636 2.176C11.580 2.240 11.505 2.284 11.423 2.302C11.339 2.319 11.254 2.308 11.177 2.272C11.100 2.235 11.037 2.174 11.000 2.098Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/></svg>";
    readonly rotary_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(10.000 12.750)\"/><path d=\"M-1.000 2.828L-1.000 6.000Q-1.000 6.500 -0.500 6.500L0.500 6.500Q1.000 6.500 1.000 6.000L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 0.000 -5.000L0.000 -3.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M1.000 -4.000L1.000 -7.000L-1.000 -7.000L-1.000 -4.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M4.494 -5.034C4.494 -5.034 1.542 -6.377 1.542 -6.377C1.482 -6.416 1.413 -6.435 1.342 -6.434C1.271 -6.433 1.203 -6.410 1.145 -6.370C1.087 -6.330 1.042 -6.273 1.016 -6.207C0.990 -6.141 0.984 -6.069 1.000 -6.000L-1.000 -6.000C-0.985 -6.069 -0.991 -6.141 -1.017 -6.207C-1.043 -6.273 -1.087 -6.330 -1.145 -6.370C-1.204 -6.410 -1.272 -6.433 -1.343 -6.434C-1.414 -6.435 -1.483 -6.416 -1.543 -6.377C-1.543 -6.377 -4.495 -5.034 -4.495 -5.034C-4.575 -5.007 -4.662 -5.008 -4.742 -5.036C-4.822 -5.065 -4.891 -5.118 -4.938 -5.189C-4.984 -5.260 -5.006 -5.344 -5.000 -5.429C-4.994 -5.513 -4.960 -5.594 -4.904 -5.657L0.000 -12.000L4.903 -5.657C4.959 -5.594 4.993 -5.513 4.999 -5.429C5.005 -5.344 4.983 -5.260 4.937 -5.189C4.890 -5.118 4.822 -5.065 4.742 -5.036C4.661 -5.008 4.574 -5.007 4.494 -5.034Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/></svg>";
    readonly roundabout: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(8.500 9.250)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(8.500 9.250)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.782 -1.462L2.869 -0.877A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(8.500 9.250)\"/><path d=\"M7.536 -1.219C7.536 -1.219 4.977 -1.646 4.977 -1.646C4.923 -1.664 4.866 -1.664 4.811 -1.649C4.756 -1.633 4.708 -1.601 4.672 -1.558C4.635 -1.515 4.612 -1.462 4.606 -1.406C4.600 -1.349 4.610 -1.292 4.636 -1.242L3.091 -0.828C3.088 -0.885 3.069 -0.939 3.035 -0.985C3.001 -1.030 2.955 -1.065 2.902 -1.084C2.848 -1.103 2.791 -1.106 2.736 -1.093C2.681 -1.079 2.632 -1.050 2.593 -1.007C2.593 -1.007 0.590 0.642 0.590 0.642C0.534 0.679 0.467 0.697 0.399 0.692C0.331 0.686 0.267 0.659 0.216 0.614C0.166 0.569 0.131 0.508 0.118 0.441C0.105 0.375 0.115 0.305 0.145 0.245L2.621 -5.672L7.723 -1.785C7.780 -1.748 7.823 -1.693 7.845 -1.629C7.867 -1.565 7.867 -1.495 7.847 -1.431C7.825 -1.366 7.783 -1.311 7.728 -1.272C7.671 -1.234 7.604 -1.215 7.536 -1.219Z\" fill=\"currentColor\" transform=\"translate(8.500 9.250)\"/></svg>";
    readonly roundabout_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(14.000 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-4.000 -1.000L-7.750 -1.000L-7.750 1.000L-4.000 1.000Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/><path d=\"M-5.784 -4.494C-5.784 -4.494 -7.127 -1.542 -7.127 -1.542C-7.166 -1.482 -7.185 -1.413 -7.184 -1.342C-7.183 -1.271 -7.160 -1.203 -7.120 -1.145C-7.080 -1.087 -7.023 -1.042 -6.957 -1.016C-6.891 -0.990 -6.819 -0.984 -6.750 -1.000L-6.750 1.000C-6.819 0.985 -6.891 0.991 -6.957 1.017C-7.023 1.043 -7.080 1.087 -7.120 1.145C-7.160 1.204 -7.183 1.272 -7.184 1.343C-7.185 1.414 -7.166 1.483 -7.127 1.543C-7.127 1.543 -5.784 4.495 -5.784 4.495C-5.757 4.575 -5.758 4.662 -5.786 4.742C-5.815 4.822 -5.868 4.891 -5.939 4.938C-6.010 4.984 -6.094 5.006 -6.179 5.000C-6.263 4.994 -6.344 4.960 -6.407 4.904L-12.750 0.000L-6.407 -4.903C-6.344 -4.959 -6.263 -4.993 -6.179 -4.999C-6.094 -5.005 -6.010 -4.983 -5.939 -4.937C-5.868 -4.890 -5.815 -4.822 -5.786 -4.742C-5.758 -4.661 -5.757 -4.574 -5.784 -4.494Z\" fill=\"currentColor\" transform=\"translate(14.000 9.000)\"/></svg>";
    readonly roundabout_lht: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.782 -1.462L2.869 -0.877A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/><path d=\"M7.536 -1.219C7.536 -1.219 4.977 -1.646 4.977 -1.646C4.923 -1.664 4.866 -1.664 4.811 -1.649C4.756 -1.633 4.708 -1.601 4.672 -1.558C4.635 -1.515 4.612 -1.462 4.606 -1.406C4.600 -1.349 4.610 -1.292 4.636 -1.242L3.091 -0.828C3.088 -0.885 3.069 -0.939 3.035 -0.985C3.001 -1.030 2.955 -1.065 2.902 -1.084C2.848 -1.103 2.791 -1.106 2.736 -1.093C2.681 -1.079 2.632 -1.050 2.593 -1.007C2.593 -1.007 0.590 0.642 0.590 0.642C0.534 0.679 0.467 0.697 0.399 0.692C0.331 0.686 0.267 0.659 0.216 0.614C0.166 0.569 0.131 0.508 0.118 0.441C0.105 0.375 0.115 0.305 0.145 0.245L2.621 -5.672L7.723 -1.785C7.780 -1.748 7.823 -1.693 7.845 -1.629C7.867 -1.565 7.867 -1.495 7.847 -1.431C7.825 -1.366 7.783 -1.311 7.728 -1.272C7.671 -1.234 7.604 -1.215 7.536 -1.219Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 11.500 9.250)\"/></svg>";
    readonly roundabout_lht_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M4.000 1.000L7.750 1.000L7.750 -1.000L4.000 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/><path d=\"M5.784 4.494C5.784 4.494 7.127 1.542 7.127 1.542C7.166 1.482 7.185 1.413 7.184 1.342C7.183 1.271 7.160 1.203 7.120 1.145C7.080 1.087 7.023 1.042 6.957 1.016C6.891 0.990 6.819 0.984 6.750 1.000L6.750 -1.000C6.819 -0.985 6.891 -0.991 6.957 -1.017C7.023 -1.043 7.080 -1.087 7.120 -1.145C7.160 -1.204 7.183 -1.272 7.184 -1.343C7.185 -1.414 7.166 -1.483 7.127 -1.543C7.127 -1.543 5.784 -4.495 5.784 -4.495C5.757 -4.575 5.758 -4.662 5.786 -4.742C5.815 -4.822 5.868 -4.891 5.939 -4.938C6.010 -4.984 6.094 -5.006 6.179 -5.000C6.263 -4.994 6.344 -4.960 6.407 -4.904L12.750 0.000L6.407 4.903C6.344 4.959 6.263 4.993 6.179 4.999C6.094 5.005 6.010 4.983 5.939 4.937C5.868 4.890 5.815 4.822 5.786 4.742C5.758 4.661 5.757 4.574 5.784 4.494Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.000)\"/></svg>";
    readonly roundabout_lht_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-4.000 -1.000L-7.750 -1.000L-7.750 1.000L-4.000 1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/><path d=\"M-5.784 -4.494C-5.784 -4.494 -7.127 -1.542 -7.127 -1.542C-7.166 -1.482 -7.185 -1.413 -7.184 -1.342C-7.183 -1.271 -7.160 -1.203 -7.120 -1.145C-7.080 -1.087 -7.023 -1.042 -6.957 -1.016C-6.891 -0.990 -6.819 -0.984 -6.750 -1.000L-6.750 1.000C-6.819 0.985 -6.891 0.991 -6.957 1.017C-7.023 1.043 -7.080 1.087 -7.120 1.145C-7.160 1.204 -7.183 1.272 -7.184 1.343C-7.185 1.414 -7.166 1.483 -7.127 1.543C-7.127 1.543 -5.784 4.495 -5.784 4.495C-5.757 4.575 -5.758 4.662 -5.786 4.742C-5.815 4.822 -5.868 4.891 -5.939 4.938C-6.010 4.984 -6.094 5.006 -6.179 5.000C-6.263 4.994 -6.344 4.960 -6.407 4.904L-12.750 0.000L-6.407 -4.903C-6.344 -4.959 -6.263 -4.993 -6.179 -4.999C-6.094 -5.005 -6.010 -4.983 -5.939 -4.937C-5.868 -4.890 -5.815 -4.822 -5.786 -4.742C-5.758 -4.661 -5.757 -4.574 -5.784 -4.494Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.000 9.000)\"/></svg>";
    readonly roundabout_lht_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.636 1.873L2.782 1.124A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M3.002 2.206L6.184 5.388L7.598 3.973L4.416 0.791Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/><path d=\"M2.323 6.468C2.323 6.468 5.360 5.330 5.360 5.330C5.430 5.315 5.492 5.280 5.542 5.229C5.591 5.178 5.623 5.114 5.636 5.045C5.648 4.975 5.640 4.903 5.612 4.838C5.583 4.773 5.537 4.718 5.477 4.680L6.891 3.266C6.929 3.326 6.984 3.372 7.049 3.401C7.114 3.429 7.186 3.438 7.255 3.425C7.325 3.412 7.389 3.380 7.440 3.331C7.491 3.281 7.526 3.219 7.541 3.149C7.541 3.149 8.679 0.112 8.679 0.112C8.716 0.036 8.779 -0.025 8.855 -0.061C8.932 -0.098 9.018 -0.109 9.102 -0.092C9.185 -0.074 9.260 -0.030 9.315 0.034C9.371 0.098 9.404 0.179 9.409 0.263L10.426 8.216L2.474 7.198C2.390 7.193 2.309 7.160 2.245 7.104C2.181 7.049 2.137 6.974 2.119 6.891C2.102 6.807 2.113 6.722 2.149 6.645C2.186 6.568 2.247 6.505 2.323 6.468Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 12.750 8.500)\"/></svg>";
    readonly roundabout_lht_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -4.636 1.873L-2.782 1.124A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M-4.416 0.791L-7.598 3.973L-6.184 5.388L-3.002 2.206Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/><path d=\"M-8.678 0.112C-8.678 0.112 -7.541 3.150 -7.541 3.150C-7.526 3.220 -7.490 3.282 -7.439 3.331C-7.389 3.381 -7.324 3.413 -7.255 3.425C-7.186 3.438 -7.113 3.430 -7.048 3.401C-6.983 3.373 -6.928 3.326 -6.891 3.266L-5.477 4.680C-5.536 4.719 -5.583 4.774 -5.611 4.839C-5.639 4.904 -5.648 4.975 -5.636 5.045C-5.622 5.115 -5.590 5.179 -5.541 5.230C-5.491 5.281 -5.429 5.316 -5.359 5.331C-5.359 5.331 -2.322 6.469 -2.322 6.469C-2.246 6.506 -2.186 6.568 -2.149 6.645C-2.113 6.722 -2.101 6.808 -2.118 6.892C-2.136 6.974 -2.180 7.049 -2.244 7.105C-2.308 7.160 -2.389 7.193 -2.473 7.198L-10.426 8.216L-9.408 0.264C-9.403 0.180 -9.370 0.098 -9.315 0.035C-9.259 -0.030 -9.184 -0.073 -9.101 -0.091C-9.018 -0.108 -8.932 -0.098 -8.855 -0.061C-8.778 -0.024 -8.716 0.037 -8.678 0.112Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 7.250 8.500)\"/></svg>";
    readonly roundabout_lht_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M3.300 1.000L5.550 1.000L5.550 -1.000L3.300 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M5.500 1.000A4.000 4.000 0 0 0 8.328 -0.172L6.914 -1.586A2.000 2.000 0 0 1 5.500 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M8.293 -0.136L9.919 -1.763L8.505 -3.177L6.879 -1.550Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/><path d=\"M11.000 2.098C11.000 2.098 9.862 -0.939 9.862 -0.939C9.847 -1.009 9.812 -1.071 9.761 -1.121C9.710 -1.170 9.646 -1.202 9.576 -1.215C9.507 -1.227 9.435 -1.219 9.370 -1.191C9.305 -1.162 9.250 -1.116 9.212 -1.055L7.798 -2.470C7.857 -2.508 7.904 -2.563 7.932 -2.628C7.961 -2.693 7.970 -2.765 7.957 -2.834C7.944 -2.904 7.912 -2.968 7.862 -3.019C7.813 -3.070 7.751 -3.105 7.681 -3.120C7.681 -3.120 4.644 -4.258 4.644 -4.258C4.568 -4.295 4.507 -4.358 4.470 -4.434C4.434 -4.511 4.423 -4.597 4.440 -4.681C4.458 -4.764 4.502 -4.838 4.566 -4.894C4.630 -4.949 4.711 -4.983 4.795 -4.988L12.748 -6.005L11.730 1.947C11.725 2.031 11.691 2.112 11.636 2.176C11.580 2.240 11.505 2.284 11.423 2.302C11.339 2.319 11.254 2.308 11.177 2.272C11.100 2.235 11.037 2.174 11.000 2.098Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 13.750 9.500)\"/></svg>";
    readonly roundabout_lht_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-3.300 -1.000L-5.550 -1.000L-5.550 1.000L-3.300 1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-5.500 1.000A4.000 4.000 0 0 1 -8.328 -0.172L-6.914 -1.586A2.000 2.000 0 0 0 -5.500 -1.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-6.879 -1.550L-8.505 -3.177L-9.919 -1.763L-8.293 -0.136Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/><path d=\"M-4.644 -4.257C-4.644 -4.257 -7.681 -3.120 -7.681 -3.120C-7.751 -3.105 -7.814 -3.069 -7.863 -3.018C-7.913 -2.967 -7.944 -2.903 -7.957 -2.834C-7.970 -2.765 -7.961 -2.692 -7.933 -2.627C-7.905 -2.562 -7.858 -2.507 -7.798 -2.470L-9.212 -1.055C-9.250 -1.115 -9.306 -1.162 -9.371 -1.190C-9.436 -1.218 -9.507 -1.227 -9.576 -1.215C-9.646 -1.201 -9.711 -1.169 -9.762 -1.120C-9.813 -1.070 -9.848 -1.008 -9.863 -0.938C-9.863 -0.938 -11.001 2.099 -11.001 2.099C-11.038 2.175 -11.100 2.235 -11.177 2.272C-11.254 2.308 -11.340 2.320 -11.423 2.303C-11.506 2.285 -11.581 2.241 -11.637 2.177C-11.692 2.113 -11.725 2.032 -11.730 1.948L-12.748 -6.005L-4.796 -4.987C-4.712 -4.982 -4.630 -4.949 -4.567 -4.894C-4.502 -4.838 -4.458 -4.763 -4.441 -4.680C-4.424 -4.597 -4.434 -4.511 -4.470 -4.434C-4.508 -4.357 -4.569 -4.295 -4.644 -4.257Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 6.250 9.500)\"/></svg>";
    readonly roundabout_lht_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M-1.000 2.828L-1.000 6.000Q-1.000 6.500 -0.500 6.500L0.500 6.500Q1.000 6.500 1.000 6.000L1.000 2.828Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 0.000 -5.000L0.000 -3.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M1.000 -4.000L1.000 -7.000L-1.000 -7.000L-1.000 -4.000Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/><path d=\"M4.494 -5.034C4.494 -5.034 1.542 -6.377 1.542 -6.377C1.482 -6.416 1.413 -6.435 1.342 -6.434C1.271 -6.433 1.203 -6.410 1.145 -6.370C1.087 -6.330 1.042 -6.273 1.016 -6.207C0.990 -6.141 0.984 -6.069 1.000 -6.000L-1.000 -6.000C-0.985 -6.069 -0.991 -6.141 -1.017 -6.207C-1.043 -6.273 -1.087 -6.330 -1.145 -6.370C-1.204 -6.410 -1.272 -6.433 -1.343 -6.434C-1.414 -6.435 -1.483 -6.416 -1.543 -6.377C-1.543 -6.377 -4.495 -5.034 -4.495 -5.034C-4.575 -5.007 -4.662 -5.008 -4.742 -5.036C-4.822 -5.065 -4.891 -5.118 -4.938 -5.189C-4.984 -5.260 -5.006 -5.344 -5.000 -5.429C-4.994 -5.513 -4.960 -5.594 -4.904 -5.657L0.000 -12.000L4.903 -5.657C4.959 -5.594 4.993 -5.513 4.999 -5.429C5.005 -5.344 4.983 -5.260 4.937 -5.189C4.890 -5.118 4.822 -5.065 4.742 -5.036C4.661 -5.008 4.574 -5.007 4.494 -5.034Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 10.000 12.750)\"/></svg>";
    readonly roundabout_lht_uturn: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M2.750 1.199L2.750 12.300Q2.750 12.800 3.250 12.800L4.250 12.800Q4.750 12.800 4.750 12.300L4.750 1.199Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M4.672 1.782A5.000 5.000 0 1 0 -4.672 1.782L-2.803 1.069A3.000 3.000 0 1 1 2.803 1.069Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M-4.750 1.199L-4.750 7.800L-2.750 7.800L-2.750 1.199Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/><path d=\"M-8.244 5.834C-8.244 5.834 -5.292 7.177 -5.292 7.177C-5.232 7.216 -5.163 7.235 -5.092 7.234C-5.021 7.233 -4.953 7.210 -4.895 7.170C-4.837 7.130 -4.792 7.073 -4.766 7.007C-4.740 6.941 -4.734 6.869 -4.750 6.800L-2.750 6.800C-2.765 6.869 -2.759 6.941 -2.733 7.007C-2.707 7.073 -2.663 7.130 -2.605 7.170C-2.546 7.210 -2.478 7.233 -2.407 7.234C-2.336 7.235 -2.267 7.216 -2.207 7.177C-2.207 7.177 0.745 5.834 0.745 5.834C0.825 5.807 0.912 5.808 0.992 5.836C1.072 5.865 1.141 5.918 1.188 5.989C1.234 6.060 1.256 6.144 1.250 6.229C1.244 6.313 1.210 6.394 1.154 6.457L-3.750 12.800L-8.653 6.457C-8.709 6.394 -8.743 6.313 -8.749 6.229C-8.755 6.144 -8.733 6.060 -8.687 5.989C-8.640 5.918 -8.572 5.865 -8.492 5.836C-8.411 5.808 -8.324 5.807 -8.244 5.834Z\" fill=\"currentColor\" transform=\"matrix(-1 0 0 1 8.000 6.000)\"/></svg>";
    readonly roundabout_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(6.250 9.000)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/><path d=\"M4.000 1.000L7.750 1.000L7.750 -1.000L4.000 -1.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/><path d=\"M5.784 4.494C5.784 4.494 7.127 1.542 7.127 1.542C7.166 1.482 7.185 1.413 7.184 1.342C7.183 1.271 7.160 1.203 7.120 1.145C7.080 1.087 7.023 1.042 6.957 1.016C6.891 0.990 6.819 0.984 6.750 1.000L6.750 -1.000C6.819 -0.985 6.891 -0.991 6.957 -1.017C7.023 -1.043 7.080 -1.087 7.120 -1.145C7.160 -1.204 7.183 -1.272 7.184 -1.343C7.185 -1.414 7.166 -1.483 7.127 -1.543C7.127 -1.543 5.784 -4.495 5.784 -4.495C5.757 -4.575 5.758 -4.662 5.786 -4.742C5.815 -4.822 5.868 -4.891 5.939 -4.938C6.010 -4.984 6.094 -5.006 6.179 -5.000C6.263 -4.994 6.344 -4.960 6.407 -4.904L12.750 0.000L6.407 4.903C6.344 4.959 6.263 4.993 6.179 4.999C6.094 5.005 6.010 4.983 5.939 4.937C5.868 4.890 5.815 4.822 5.786 4.742C5.758 4.661 5.757 4.574 5.784 4.494Z\" fill=\"currentColor\" transform=\"translate(6.250 9.000)\"/></svg>";
    readonly roundabout_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(12.750 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -4.636 1.873L-2.782 1.124A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/><path d=\"M-4.416 0.791L-7.598 3.973L-6.184 5.388L-3.002 2.206Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/><path d=\"M-8.678 0.112C-8.678 0.112 -7.541 3.150 -7.541 3.150C-7.526 3.220 -7.490 3.282 -7.439 3.331C-7.389 3.381 -7.324 3.413 -7.255 3.425C-7.186 3.438 -7.113 3.430 -7.048 3.401C-6.983 3.373 -6.928 3.326 -6.891 3.266L-5.477 4.680C-5.536 4.719 -5.583 4.774 -5.611 4.839C-5.639 4.904 -5.648 4.975 -5.636 5.045C-5.622 5.115 -5.590 5.179 -5.541 5.230C-5.491 5.281 -5.429 5.316 -5.359 5.331C-5.359 5.331 -2.322 6.469 -2.322 6.469C-2.246 6.506 -2.186 6.568 -2.149 6.645C-2.113 6.722 -2.101 6.808 -2.118 6.892C-2.136 6.974 -2.180 7.049 -2.244 7.105C-2.308 7.160 -2.389 7.193 -2.473 7.198L-10.426 8.216L-9.408 0.264C-9.403 0.180 -9.370 0.098 -9.315 0.035C-9.259 -0.030 -9.184 -0.073 -9.101 -0.091C-9.018 -0.108 -8.932 -0.098 -8.855 -0.061C-8.778 -0.024 -8.716 0.037 -8.678 0.112Z\" fill=\"currentColor\" transform=\"translate(12.750 8.500)\"/></svg>";
    readonly roundabout_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(7.250 8.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 4.636 1.873L2.782 1.124A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/><path d=\"M3.002 2.206L6.184 5.388L7.598 3.973L4.416 0.791Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/><path d=\"M2.323 6.468C2.323 6.468 5.360 5.330 5.360 5.330C5.430 5.315 5.492 5.280 5.542 5.229C5.591 5.178 5.623 5.114 5.636 5.045C5.648 4.975 5.640 4.903 5.612 4.838C5.583 4.773 5.537 4.718 5.477 4.680L6.891 3.266C6.929 3.326 6.984 3.372 7.049 3.401C7.114 3.429 7.186 3.438 7.255 3.425C7.325 3.412 7.389 3.380 7.440 3.331C7.491 3.281 7.526 3.219 7.541 3.149C7.541 3.149 8.679 0.112 8.679 0.112C8.716 0.036 8.779 -0.025 8.855 -0.061C8.932 -0.098 9.018 -0.109 9.102 -0.092C9.185 -0.074 9.260 -0.030 9.315 0.034C9.371 0.098 9.404 0.179 9.409 0.263L10.426 8.216L2.474 7.198C2.390 7.193 2.309 7.160 2.245 7.104C2.181 7.049 2.137 6.974 2.119 6.891C2.102 6.807 2.113 6.722 2.149 6.645C2.186 6.568 2.247 6.505 2.323 6.468Z\" fill=\"currentColor\" transform=\"translate(7.250 8.500)\"/></svg>";
    readonly roundabout_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(13.750 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 1 0 -5.000 0.000L-3.000 0.000A3.000 3.000 0 1 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-3.300 -1.000L-5.550 -1.000L-5.550 1.000L-3.300 1.000Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-5.500 1.000A4.000 4.000 0 0 1 -8.328 -0.172L-6.914 -1.586A2.000 2.000 0 0 0 -5.500 -1.000Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-6.879 -1.550L-8.505 -3.177L-9.919 -1.763L-8.293 -0.136Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/><path d=\"M-4.644 -4.257C-4.644 -4.257 -7.681 -3.120 -7.681 -3.120C-7.751 -3.105 -7.814 -3.069 -7.863 -3.018C-7.913 -2.967 -7.944 -2.903 -7.957 -2.834C-7.970 -2.765 -7.961 -2.692 -7.933 -2.627C-7.905 -2.562 -7.858 -2.507 -7.798 -2.470L-9.212 -1.055C-9.250 -1.115 -9.306 -1.162 -9.371 -1.190C-9.436 -1.218 -9.507 -1.227 -9.576 -1.215C-9.646 -1.201 -9.711 -1.169 -9.762 -1.120C-9.813 -1.070 -9.848 -1.008 -9.863 -0.938C-9.863 -0.938 -11.001 2.099 -11.001 2.099C-11.038 2.175 -11.100 2.235 -11.177 2.272C-11.254 2.308 -11.340 2.320 -11.423 2.303C-11.506 2.285 -11.581 2.241 -11.637 2.177C-11.692 2.113 -11.725 2.032 -11.730 1.948L-12.748 -6.005L-4.796 -4.987C-4.712 -4.982 -4.630 -4.949 -4.567 -4.894C-4.502 -4.838 -4.458 -4.763 -4.441 -4.680C-4.424 -4.597 -4.434 -4.511 -4.470 -4.434C-4.508 -4.357 -4.569 -4.295 -4.644 -4.257Z\" fill=\"currentColor\" transform=\"translate(13.750 9.500)\"/></svg>";
    readonly roundabout_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(6.250 9.500)\"/><path d=\"M-1.000 2.828L-1.000 6.500Q-1.000 7.000 -0.500 7.000L0.500 7.000Q1.000 7.000 1.000 6.500L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 5.000 0.000L3.000 0.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M3.300 1.000L5.550 1.000L5.550 -1.000L3.300 -1.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M5.500 1.000A4.000 4.000 0 0 0 8.328 -0.172L6.914 -1.586A2.000 2.000 0 0 1 5.500 -1.000Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M8.293 -0.136L9.919 -1.763L8.505 -3.177L6.879 -1.550Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/><path d=\"M11.000 2.098C11.000 2.098 9.862 -0.939 9.862 -0.939C9.847 -1.009 9.812 -1.071 9.761 -1.121C9.710 -1.170 9.646 -1.202 9.576 -1.215C9.507 -1.227 9.435 -1.219 9.370 -1.191C9.305 -1.162 9.250 -1.116 9.212 -1.055L7.798 -2.470C7.857 -2.508 7.904 -2.563 7.932 -2.628C7.961 -2.693 7.970 -2.765 7.957 -2.834C7.944 -2.904 7.912 -2.968 7.862 -3.019C7.813 -3.070 7.751 -3.105 7.681 -3.120C7.681 -3.120 4.644 -4.258 4.644 -4.258C4.568 -4.295 4.507 -4.358 4.470 -4.434C4.434 -4.511 4.423 -4.597 4.440 -4.681C4.458 -4.764 4.502 -4.838 4.566 -4.894C4.630 -4.949 4.711 -4.983 4.795 -4.988L12.748 -6.005L11.730 1.947C11.725 2.031 11.691 2.112 11.636 2.176C11.580 2.240 11.505 2.284 11.423 2.302C11.339 2.319 11.254 2.308 11.177 2.272C11.100 2.235 11.037 2.174 11.000 2.098Z\" fill=\"currentColor\" transform=\"translate(6.250 9.500)\"/></svg>";
    readonly roundabout_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(10.000 12.750)\"/><path d=\"M-1.000 2.828L-1.000 6.000Q-1.000 6.500 -0.500 6.500L0.500 6.500Q1.000 6.500 1.000 6.000L1.000 2.828Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M0.000 5.000A5.000 5.000 0 0 0 0.000 -5.000L0.000 -3.000A3.000 3.000 0 0 1 0.000 3.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M1.000 -4.000L1.000 -7.000L-1.000 -7.000L-1.000 -4.000Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/><path d=\"M4.494 -5.034C4.494 -5.034 1.542 -6.377 1.542 -6.377C1.482 -6.416 1.413 -6.435 1.342 -6.434C1.271 -6.433 1.203 -6.410 1.145 -6.370C1.087 -6.330 1.042 -6.273 1.016 -6.207C0.990 -6.141 0.984 -6.069 1.000 -6.000L-1.000 -6.000C-0.985 -6.069 -0.991 -6.141 -1.017 -6.207C-1.043 -6.273 -1.087 -6.330 -1.145 -6.370C-1.204 -6.410 -1.272 -6.433 -1.343 -6.434C-1.414 -6.435 -1.483 -6.416 -1.543 -6.377C-1.543 -6.377 -4.495 -5.034 -4.495 -5.034C-4.575 -5.007 -4.662 -5.008 -4.742 -5.036C-4.822 -5.065 -4.891 -5.118 -4.938 -5.189C-4.984 -5.260 -5.006 -5.344 -5.000 -5.429C-4.994 -5.513 -4.960 -5.594 -4.904 -5.657L0.000 -12.000L4.903 -5.657C4.959 -5.594 4.993 -5.513 4.999 -5.429C5.005 -5.344 4.983 -5.260 4.937 -5.189C4.890 -5.118 4.822 -5.065 4.742 -5.036C4.661 -5.008 4.574 -5.007 4.494 -5.034Z\" fill=\"currentColor\" transform=\"translate(10.000 12.750)\"/></svg>";
    readonly roundabout_uturn: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path class=\"mm2\" d=\"M-5.000 0.000A5.000 5.000 0 1 0 5.000 0.000A5.000 5.000 0 1 0 -5.000 0.000ZM-3.000 0.000A3.000 3.000 0 1 0 3.000 0.000A3.000 3.000 0 1 0 -3.000 0.000Z\" fill=\"currentColor\" fill-opacity=\"0.35\" fill-rule=\"evenodd\" transform=\"translate(12.000 6.000)\"/><path d=\"M2.750 1.199L2.750 12.300Q2.750 12.800 3.250 12.800L4.250 12.800Q4.750 12.800 4.750 12.300L4.750 1.199Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M4.672 1.782A5.000 5.000 0 1 0 -4.672 1.782L-2.803 1.069A3.000 3.000 0 1 1 2.803 1.069Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M-4.750 1.199L-4.750 7.800L-2.750 7.800L-2.750 1.199Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/><path d=\"M-8.244 5.834C-8.244 5.834 -5.292 7.177 -5.292 7.177C-5.232 7.216 -5.163 7.235 -5.092 7.234C-5.021 7.233 -4.953 7.210 -4.895 7.170C-4.837 7.130 -4.792 7.073 -4.766 7.007C-4.740 6.941 -4.734 6.869 -4.750 6.800L-2.750 6.800C-2.765 6.869 -2.759 6.941 -2.733 7.007C-2.707 7.073 -2.663 7.130 -2.605 7.170C-2.546 7.210 -2.478 7.233 -2.407 7.234C-2.336 7.235 -2.267 7.216 -2.207 7.177C-2.207 7.177 0.745 5.834 0.745 5.834C0.825 5.807 0.912 5.808 0.992 5.836C1.072 5.865 1.141 5.918 1.188 5.989C1.234 6.060 1.256 6.144 1.250 6.229C1.244 6.313 1.210 6.394 1.154 6.457L-3.750 12.800L-8.653 6.457C-8.709 6.394 -8.743 6.313 -8.749 6.229C-8.755 6.144 -8.733 6.060 -8.687 5.989C-8.640 5.918 -8.572 5.865 -8.492 5.836C-8.411 5.808 -8.324 5.807 -8.244 5.834Z\" fill=\"currentColor\" transform=\"translate(12.000 6.000)\"/></svg>";
    readonly stairs: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.9 17 V13.9 H7 V10.3 H11.1 V6.7 H15.2 V3.1 H17.1 V17 Z\" fill=\"currentColor\" stroke=\"currentColor\" stroke-width=\"1\" stroke-linejoin=\"round\"/></svg>";
    readonly turn_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M7.992 3.999C7.992 3.999 8.003 4.001 8.003 4.001C9.600 4.019 11.125 4.668 12.247 5.805C13.368 6.942 13.995 8.477 13.992 10.074C13.992 10.074 13.992 14.019 13.992 14.019C13.992 14.019 13.989 14.019 13.989 14.019C13.989 14.019 13.991 15.522 13.991 15.522C13.991 15.587 13.978 15.652 13.953 15.712C13.928 15.772 13.892 15.827 13.845 15.873C13.799 15.919 13.745 15.956 13.684 15.981C13.624 16.006 13.560 16.019 13.494 16.019C13.494 16.019 12.488 16.019 12.488 16.019C12.356 16.019 12.230 15.967 12.137 15.874C12.044 15.780 11.992 15.654 11.992 15.523C11.992 15.523 11.992 10.074 11.992 10.074C11.996 9.007 11.580 7.981 10.833 7.219C10.087 6.456 9.070 6.019 8.003 6.001C8.003 6.001 6.004 6.001 6.004 6.001C5.935 5.985 5.863 5.991 5.797 6.017C5.731 6.043 5.675 6.088 5.634 6.146C5.594 6.204 5.572 6.272 5.570 6.343C5.569 6.414 5.589 6.483 5.627 6.543C5.627 6.543 6.971 9.495 6.971 9.495C6.997 9.576 6.996 9.663 6.968 9.743C6.940 9.823 6.886 9.891 6.815 9.938C6.744 9.984 6.660 10.006 6.575 10.000C6.491 9.994 6.411 9.960 6.347 9.904C6.347 9.904 0.000 5.019 0.000 5.019C0.000 5.019 6.347 0.097 6.347 0.097C6.411 0.041 6.491 0.007 6.575 0.001C6.660 -0.005 6.744 0.017 6.815 0.063C6.886 0.110 6.940 0.179 6.968 0.259C6.996 0.339 6.997 0.426 6.971 0.506C6.971 0.506 5.627 3.458 5.627 3.458C5.589 3.518 5.569 3.587 5.570 3.658C5.572 3.729 5.594 3.797 5.634 3.855C5.675 3.914 5.731 3.958 5.797 3.984C5.863 4.010 5.935 4.016 6.004 4.001C6.004 4.001 7.992 4.001 7.992 4.001Z\" fill=\"currentColor\" transform=\"translate(2.008 1.981)\"/></svg>";
    readonly turn_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M5.989 6.001C4.922 6.019 3.905 6.456 3.159 7.219C2.412 7.981 1.996 9.007 2.000 10.074C2.000 10.074 2.000 15.523 2.000 15.523C2.000 15.654 1.948 15.780 1.855 15.874C1.762 15.967 1.635 16.019 1.504 16.019C1.504 16.019 0.497 16.019 0.497 16.019C0.432 16.019 0.368 16.006 0.307 15.981C0.247 15.956 0.192 15.919 0.146 15.873C0.100 15.827 0.064 15.772 0.039 15.712C0.014 15.652 0.001 15.587 0.001 15.522C0.001 15.522 0.003 14.019 0.003 14.019C0.003 14.019 0.000 14.019 0.000 14.019C0.000 14.019 0.000 10.074 0.000 10.074C-0.004 8.477 0.624 6.942 1.745 5.805C2.867 4.668 4.392 4.019 5.989 4.001C5.989 4.001 6.000 3.999 6.000 3.999C6.000 3.999 6.000 4.001 6.000 4.001C6.000 4.001 7.988 4.001 7.988 4.001C8.057 4.016 8.129 4.010 8.195 3.984C8.260 3.958 8.317 3.914 8.358 3.855C8.398 3.797 8.420 3.729 8.422 3.658C8.423 3.587 8.403 3.518 8.365 3.458C8.365 3.458 7.021 0.506 7.021 0.506C6.995 0.426 6.996 0.339 7.024 0.259C7.052 0.179 7.106 0.110 7.177 0.063C7.247 0.017 7.332 -0.005 7.416 0.001C7.501 0.007 7.581 0.041 7.645 0.097C7.645 0.097 13.992 5.019 13.992 5.019C13.992 5.019 7.645 9.904 7.645 9.904C7.581 9.960 7.501 9.994 7.416 10.000C7.332 10.006 7.247 9.984 7.177 9.938C7.106 9.891 7.052 9.823 7.024 9.743C6.996 9.663 6.995 9.576 7.021 9.495C7.021 9.495 8.365 6.543 8.365 6.543C8.403 6.483 8.423 6.414 8.422 6.343C8.420 6.272 8.398 6.204 8.358 6.146C8.317 6.088 8.260 6.043 8.195 6.017C8.129 5.991 8.057 5.985 7.988 6.001C7.988 6.001 5.989 6.001 5.989 6.001Z\" fill=\"currentColor\" transform=\"translate(4 1.981)\"/></svg>";
    readonly turn_sharp_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M13.481 15.011C13.349 15.011 13.223 14.959 13.129 14.865C13.036 14.772 12.983 14.645 12.983 14.513C12.983 14.513 12.983 2.015 12.983 2.015C12.983 2.015 12.704 2.009 12.704 2.009C12.317 1.973 11.928 2.026 11.565 2.163C11.202 2.300 10.875 2.517 10.608 2.799C10.608 2.799 5.213 7.411 5.213 7.411C5.155 7.447 5.109 7.498 5.080 7.559C5.051 7.619 5.041 7.687 5.049 7.754C5.058 7.821 5.086 7.884 5.130 7.935C5.173 7.986 5.231 8.024 5.295 8.043C5.295 8.043 8.224 9.437 8.224 9.437C8.296 9.481 8.351 9.549 8.381 9.628C8.411 9.707 8.414 9.794 8.390 9.875C8.366 9.957 8.316 10.028 8.247 10.078C8.178 10.127 8.095 10.153 8.010 10.151C8.010 10.151 0.000 10.483 0.000 10.483C0.000 10.483 1.696 2.647 1.696 2.647C1.708 2.564 1.748 2.486 1.809 2.427C1.870 2.368 1.948 2.330 2.032 2.320C2.117 2.310 2.202 2.328 2.275 2.371C2.348 2.415 2.405 2.481 2.436 2.559C2.436 2.559 3.309 5.683 3.309 5.683C3.318 5.753 3.348 5.819 3.394 5.872C3.441 5.925 3.502 5.964 3.570 5.982C3.639 6.001 3.711 5.998 3.778 5.976C3.845 5.953 3.904 5.911 3.947 5.855C3.947 5.855 3.951 5.859 3.951 5.859C3.951 5.859 9.343 1.250 9.343 1.250C9.790 0.820 10.322 0.487 10.904 0.273C11.487 0.059 12.108 -0.031 12.728 0.009C12.728 0.009 12.992 0.011 12.992 0.011C13.257 0.002 13.521 0.048 13.768 0.146C14.015 0.244 14.239 0.392 14.426 0.580C14.792 0.977 14.992 1.500 14.983 2.040C14.983 2.040 14.983 14.513 14.983 14.513C14.983 14.579 14.971 14.643 14.945 14.704C14.920 14.764 14.884 14.819 14.838 14.865C14.791 14.912 14.736 14.948 14.676 14.973C14.616 14.998 14.551 15.011 14.486 15.011C14.486 15.011 13.481 15.011 13.481 15.011Z\" fill=\"currentColor\" transform=\"translate(2.017 2.984)\"/></svg>";
    readonly turn_sharp_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M0.498 15.011C0.432 15.011 0.368 14.998 0.307 14.973C0.247 14.948 0.192 14.912 0.146 14.865C0.100 14.819 0.063 14.764 0.038 14.704C0.013 14.643 -0.000 14.579 0.000 14.513C0.000 14.513 0.000 2.040 0.000 2.040C-0.008 1.500 0.191 0.977 0.558 0.580C0.745 0.392 0.969 0.244 1.215 0.146C1.462 0.048 1.726 0.002 1.992 0.011C1.992 0.011 2.256 0.009 2.256 0.009C2.875 -0.031 3.496 0.059 4.079 0.273C4.662 0.487 5.194 0.820 5.641 1.250C5.641 1.250 11.033 5.859 11.033 5.859C11.033 5.859 11.036 5.855 11.036 5.855C11.080 5.911 11.138 5.953 11.205 5.976C11.272 5.998 11.345 6.001 11.413 5.982C11.481 5.964 11.543 5.926 11.589 5.872C11.636 5.819 11.665 5.753 11.674 5.683C11.674 5.683 12.547 2.559 12.547 2.559C12.579 2.481 12.636 2.415 12.709 2.372C12.782 2.328 12.867 2.310 12.951 2.320C13.035 2.331 13.114 2.368 13.175 2.427C13.235 2.486 13.275 2.564 13.288 2.647C13.288 2.647 14.983 10.483 14.983 10.483C14.983 10.483 6.973 10.151 6.973 10.151C6.888 10.153 6.805 10.127 6.736 10.078C6.668 10.028 6.618 9.957 6.593 9.875C6.569 9.794 6.572 9.707 6.602 9.628C6.632 9.549 6.687 9.481 6.759 9.437C6.759 9.437 9.688 8.043 9.688 8.043C9.753 8.024 9.810 7.986 9.854 7.935C9.897 7.884 9.925 7.821 9.934 7.754C9.943 7.687 9.932 7.620 9.903 7.559C9.874 7.498 9.828 7.447 9.771 7.412C9.771 7.412 4.375 2.799 4.375 2.799C4.109 2.517 3.781 2.300 3.418 2.163C3.055 2.026 2.666 1.974 2.280 2.009C2.280 2.009 2.000 2.015 2.000 2.015C2.000 2.015 2.000 14.513 2.000 14.513C2.000 14.645 1.948 14.772 1.854 14.865C1.761 14.959 1.634 15.011 1.502 15.011C1.502 15.011 0.498 15.011 0.498 15.011Z\" fill=\"currentColor\" transform=\"translate(3 2.984)\"/></svg>";
    readonly turn_slight_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.986 11.043C9.986 11.043 9.986 14.504 9.986 14.504C9.986 14.635 9.934 14.762 9.841 14.855C9.748 14.948 9.621 15.000 9.490 15.000C9.490 15.000 8.483 15.000 8.483 15.000C8.418 15.000 8.353 14.987 8.293 14.962C8.233 14.937 8.178 14.901 8.132 14.854C8.086 14.808 8.050 14.754 8.025 14.693C8.000 14.633 7.987 14.568 7.987 14.503C7.987 14.503 7.992 10.896 7.992 10.896C8.038 10.125 7.913 9.353 7.625 8.636C7.337 7.920 6.894 7.276 6.327 6.752C6.327 6.752 3.879 4.686 3.879 4.686C3.836 4.629 3.778 4.586 3.711 4.563C3.645 4.539 3.572 4.536 3.504 4.553C3.435 4.571 3.373 4.608 3.326 4.660C3.279 4.713 3.248 4.778 3.238 4.849C3.238 4.849 2.320 7.959 2.320 7.959C2.287 8.037 2.229 8.102 2.155 8.144C2.082 8.186 1.996 8.203 1.912 8.192C1.828 8.181 1.750 8.142 1.690 8.082C1.630 8.022 1.592 7.944 1.580 7.860C1.580 7.860 0.000 0.000 0.000 0.000C0.000 0.000 8.005 0.450 8.005 0.450C8.090 0.449 8.172 0.476 8.240 0.527C8.308 0.578 8.357 0.649 8.380 0.731C8.403 0.813 8.399 0.900 8.368 0.978C8.337 1.057 8.280 1.124 8.208 1.167C8.208 1.167 5.259 2.518 5.259 2.518C5.191 2.538 5.130 2.577 5.085 2.631C5.040 2.686 5.012 2.752 5.004 2.823C4.997 2.893 5.010 2.964 5.043 3.027C5.076 3.089 5.126 3.141 5.189 3.175C5.189 3.175 7.646 5.249 7.646 5.249C8.443 5.979 9.067 6.878 9.472 7.881C9.877 8.883 10.053 9.964 9.986 11.043Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly turn_slight_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M2.354 5.249C2.354 5.249 4.811 3.175 4.811 3.175C4.873 3.141 4.924 3.089 4.957 3.027C4.990 2.964 5.003 2.893 4.996 2.823C4.988 2.752 4.960 2.686 4.915 2.631C4.869 2.577 4.809 2.538 4.741 2.518C4.741 2.518 1.792 1.167 1.792 1.167C1.719 1.124 1.663 1.057 1.632 0.978C1.601 0.900 1.596 0.813 1.619 0.731C1.642 0.649 1.692 0.578 1.760 0.527C1.827 0.476 1.910 0.449 1.995 0.450C1.995 0.450 10.000 0.000 10.000 0.000C10.000 0.000 8.419 7.860 8.419 7.860C8.408 7.944 8.369 8.022 8.309 8.082C8.249 8.142 8.171 8.181 8.087 8.192C8.003 8.203 7.918 8.186 7.844 8.144C7.771 8.102 7.713 8.037 7.680 7.959C7.680 7.959 6.762 4.849 6.762 4.849C6.752 4.778 6.721 4.713 6.674 4.660C6.626 4.608 6.564 4.571 6.496 4.553C6.427 4.536 6.355 4.539 6.288 4.563C6.222 4.586 6.163 4.629 6.121 4.686C6.121 4.686 3.673 6.751 3.673 6.751C3.106 7.276 2.663 7.920 2.375 8.636C2.087 9.353 1.962 10.125 2.008 10.895C2.008 10.895 2.013 14.503 2.013 14.503C2.013 14.568 2.000 14.633 1.975 14.693C1.950 14.754 1.914 14.808 1.868 14.854C1.821 14.901 1.767 14.937 1.707 14.962C1.646 14.987 1.582 15.000 1.516 15.000C1.516 15.000 0.510 15.000 0.510 15.000C0.378 15.000 0.252 14.948 0.159 14.855C0.066 14.762 0.014 14.635 0.014 14.504C0.014 14.504 0.014 11.043 0.014 11.043C-0.053 9.964 0.123 8.883 0.528 7.881C0.932 6.878 1.556 5.979 2.354 5.249Z\" fill=\"currentColor\" transform=\"translate(5 3)\"/></svg>";
    readonly turn_straight: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 15.497 6.001 15.497C6.001 15.563 5.988 15.628 5.962 15.689C5.937 15.750 5.900 15.806 5.853 15.853C5.806 15.899 5.751 15.936 5.690 15.962C5.629 15.987 5.563 16.000 5.497 16.000C5.497 16.000 4.504 16.000 4.504 16.000C4.438 16.000 4.372 15.987 4.311 15.962C4.250 15.936 4.195 15.899 4.148 15.853C4.101 15.806 4.064 15.750 4.039 15.689C4.014 15.628 4.001 15.563 4.001 15.497C4.001 15.497 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.856 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966Z\" fill=\"currentColor\" transform=\"translate(5.009 2)\"/></svg>";
    readonly updown: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M9.904 11.649C9.904 11.649 5.001 17.992 5.001 17.992C5.001 17.992 0.097 11.649 0.097 11.649C0.041 11.585 0.007 11.505 0.001 11.420C-0.005 11.336 0.017 11.252 0.063 11.181C0.110 11.110 0.179 11.056 0.259 11.028C0.339 11.000 0.426 10.999 0.506 11.025C0.506 11.025 3.458 12.369 3.458 12.369C3.518 12.407 3.587 12.427 3.658 12.426C3.729 12.424 3.797 12.402 3.855 12.362C3.914 12.321 3.958 12.265 3.984 12.199C4.010 12.133 4.016 12.061 4.001 11.992C4.001 11.992 4.001 6.000 4.001 6.000C4.016 5.931 4.010 5.859 3.984 5.793C3.958 5.727 3.914 5.670 3.855 5.630C3.797 5.590 3.729 5.567 3.658 5.566C3.587 5.565 3.518 5.584 3.458 5.623C3.458 5.623 0.506 6.966 0.506 6.966C0.426 6.993 0.339 6.992 0.259 6.964C0.179 6.935 0.110 6.882 0.063 6.811C0.017 6.740 -0.005 6.656 0.001 6.571C0.007 6.487 0.041 6.406 0.097 6.343C0.097 6.343 5.001 0.000 5.001 0.000C5.001 0.000 9.904 6.343 9.904 6.343C9.960 6.406 9.994 6.487 10.000 6.571C10.006 6.656 9.984 6.740 9.938 6.811C9.891 6.882 9.823 6.935 9.743 6.964C9.662 6.992 9.575 6.993 9.495 6.966C9.495 6.966 6.543 5.623 6.543 5.623C6.483 5.584 6.414 5.565 6.343 5.566C6.272 5.567 6.204 5.590 6.146 5.630C6.088 5.670 6.043 5.727 6.017 5.793C5.991 5.859 5.985 5.931 6.001 6.000C6.001 6.000 6.001 11.992 6.001 11.992C5.985 12.061 5.991 12.133 6.017 12.199C6.043 12.265 6.088 12.321 6.146 12.362C6.204 12.402 6.272 12.424 6.343 12.426C6.414 12.427 6.483 12.407 6.543 12.369C6.543 12.369 9.495 11.025 9.495 11.025C9.575 10.999 9.662 11.000 9.742 11.028C9.823 11.056 9.891 11.110 9.938 11.181C9.984 11.252 10.006 11.336 10.000 11.420C9.994 11.505 9.960 11.585 9.904 11.649Z\" fill=\"currentColor\" transform=\"translate(5.009 1.008)\"/></svg>";
    readonly uturn_left: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M15.010 5.500C15.010 5.500 15.010 14.997 15.010 14.997C15.010 15.063 14.997 15.128 14.971 15.189C14.946 15.250 14.909 15.306 14.862 15.353C14.815 15.399 14.760 15.436 14.699 15.462C14.638 15.487 14.572 15.500 14.506 15.500C14.506 15.500 13.513 15.500 13.513 15.500C13.447 15.500 13.382 15.487 13.321 15.462C13.259 15.436 13.204 15.399 13.157 15.353C13.110 15.306 13.073 15.250 13.048 15.189C13.023 15.128 13.010 15.063 13.010 14.997C13.010 14.997 13.010 5.500 13.010 5.500C13.010 4.572 12.641 3.682 11.985 3.025C11.328 2.369 10.438 2.000 9.510 2.000C8.581 2.000 7.691 2.369 7.035 3.025C6.378 3.682 6.010 4.572 6.010 5.500C6.010 5.500 6.010 9.500 6.010 9.500C6.010 9.500 6.001 9.500 6.001 9.500C5.985 9.569 5.991 9.641 6.017 9.707C6.043 9.773 6.088 9.830 6.146 9.870C6.204 9.910 6.272 9.933 6.343 9.934C6.414 9.935 6.483 9.916 6.543 9.877C6.543 9.877 9.495 8.534 9.495 8.534C9.575 8.507 9.663 8.508 9.743 8.536C9.823 8.565 9.891 8.618 9.938 8.689C9.984 8.760 10.006 8.844 10.000 8.929C9.994 9.013 9.960 9.094 9.904 9.157C9.904 9.157 5.001 15.500 5.001 15.500C5.001 15.500 0.097 9.157 0.097 9.157C0.041 9.094 0.007 9.013 0.001 8.929C-0.005 8.844 0.017 8.760 0.063 8.689C0.110 8.618 0.179 8.565 0.259 8.536C0.339 8.508 0.426 8.507 0.506 8.534C0.506 8.534 3.458 9.877 3.458 9.877C3.518 9.916 3.587 9.935 3.658 9.934C3.729 9.933 3.797 9.910 3.855 9.870C3.914 9.830 3.958 9.773 3.984 9.707C4.010 9.641 4.016 9.569 4.001 9.500C4.001 9.500 4.010 9.500 4.010 9.500C4.010 9.500 4.010 5.500 4.010 5.500C4.010 4.041 4.589 2.642 5.621 1.611C6.652 0.579 8.051 0.000 9.510 0.000C10.968 0.000 12.367 0.579 13.399 1.611C14.430 2.642 15.010 4.041 15.010 5.500Z\" fill=\"currentColor\" transform=\"translate(1.999 2.5)\"/></svg>";
    readonly uturn_right: "<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 20 20\" width=\"20\" height=\"20\"><path d=\"M15.010 5.500C15.010 5.500 15.010 14.997 15.010 14.997C15.010 15.063 14.997 15.128 14.971 15.189C14.946 15.250 14.909 15.306 14.862 15.353C14.815 15.399 14.760 15.436 14.699 15.462C14.638 15.487 14.572 15.500 14.506 15.500C14.506 15.500 13.513 15.500 13.513 15.500C13.447 15.500 13.382 15.487 13.321 15.462C13.259 15.436 13.204 15.399 13.157 15.353C13.110 15.306 13.073 15.250 13.048 15.189C13.023 15.128 13.010 15.063 13.010 14.997C13.010 14.997 13.010 5.500 13.010 5.500C13.010 4.572 12.641 3.682 11.985 3.025C11.328 2.369 10.438 2.000 9.510 2.000C8.581 2.000 7.691 2.369 7.035 3.025C6.378 3.682 6.010 4.572 6.010 5.500C6.010 5.500 6.010 9.500 6.010 9.500C6.010 9.500 6.001 9.500 6.001 9.500C5.985 9.569 5.991 9.641 6.017 9.707C6.043 9.773 6.088 9.830 6.146 9.870C6.204 9.910 6.272 9.933 6.343 9.934C6.414 9.935 6.483 9.916 6.543 9.877C6.543 9.877 9.495 8.534 9.495 8.534C9.575 8.507 9.663 8.508 9.743 8.536C9.823 8.565 9.891 8.618 9.938 8.689C9.984 8.760 10.006 8.844 10.000 8.929C9.994 9.013 9.960 9.094 9.904 9.157C9.904 9.157 5.001 15.500 5.001 15.500C5.001 15.500 0.097 9.157 0.097 9.157C0.041 9.094 0.007 9.013 0.001 8.929C-0.005 8.844 0.017 8.760 0.063 8.689C0.110 8.618 0.179 8.565 0.259 8.536C0.339 8.508 0.426 8.507 0.506 8.534C0.506 8.534 3.458 9.877 3.458 9.877C3.518 9.916 3.587 9.935 3.658 9.934C3.729 9.933 3.797 9.910 3.855 9.870C3.914 9.830 3.958 9.773 3.984 9.707C4.010 9.641 4.016 9.569 4.001 9.500C4.001 9.500 4.010 9.500 4.010 9.500C4.010 9.500 4.010 5.500 4.010 5.500C4.010 4.041 4.589 2.642 5.621 1.611C6.652 0.579 8.051 0.000 9.510 0.000C10.968 0.000 12.367 0.579 13.399 1.611C14.430 2.642 15.010 4.041 15.010 5.500Z\" fill=\"currentColor\" transform=\"translate(20 0) scale(-1 1) translate(1.999 2.5)\"/></svg>";
};
```

Inline SVG markup for every direction icon, keyed by icon name.

#### `EFFECTS_METADATA_KEY`

*Constant*

```ts
const EFFECTS_METADATA_KEY = "mapmap:effects";
```

The `metadata` key a compiled style carries its effects block under.
Shared with `sn-style` (compile.rs) and read back by `MapMapMap` to
auto-enable the route effect for styles that request one.

#### `FEATURESETS`

*Constant*

```ts
const FEATURESETS: Readonly<Record<FeaturesetId, readonly string[]>>;
```

The basemap layer ids each featureset covers. A layer the live style does
not carry is skipped (`building-3d` exists only with 3D buildings on).

#### `FLOW_DEFAULTS`

*Constant*

```ts
const FLOW_DEFAULTS: {
    readonly color: "#3a86ff";
    readonly width: 10;
    readonly speed: 0.6;
};
```

Default flow parameters (exported so docs/tests state one truth).

#### `FULL_ATTRIBUTION`

*Constant*

```ts
const FULL_ATTRIBUTION = "\u00A9 OpenStreetMap contributors \u00A9 OpenMapTiles";
```

Combined attribution stamped on every compiled style's tile source.
Legally required (ODbL + CC-BY) - enforced, not themable: no theme
field can remove it.

#### `GLOBE_BLEND_GLSL`

*Constant*

```ts
const GLOBE_BLEND_GLSL = "uniform mat4 uGlobeMatrix;\nuniform float uGlobeness;\nvec4 mapmapProject(vec4 localPos) {\n  vec4 mercatorPos = projectionMatrix * localPos;\n  if (uGlobeness <= 0.0) return mercatorPos;\n  vec4 globePos = uGlobeMatrix * localPos;\n  if (uGlobeness > 0.999) return globePos;\n  return mix(mercatorPos, globePos, uGlobeness);\n}\n";
```

The vertex-shader tail every tier-2 layer uses: declare it once, then
write `gl_Position = mapmapProject(vec4(localPos, 1.0));` where
`projectionMatrix` is `ClipMatrices.mercator`, `uGlobeMatrix` is
`ClipMatrices.globe` and `uGlobeness` is
`ClipMatrices.transition`. The mercator branch is the same
expression the layers evaluated before, so mercator output does not move
by a bit; the blend is a branch, never a `mix` on that path. (Variable
names avoid `flat`, a GLSL ES 3.00 keyword.)

#### `GLOBE_RADIUS_M`

*Constant*

```ts
const GLOBE_RADIUS_M = 6371008.8;
```

The prelude's `GLOBE_RADIUS`: MapLibre's mean Earth radius, metres.

#### `IDENTITY_LUT`

*Constant*

```ts
const IDENTITY_LUT: LutBytes;
```

The identity LUT: lattice point `i` holds `i * 17` (`i * 255 / 15`
exactly), so `lutApply(IDENTITY_LUT, c)` returns `c` for every colour.

#### `KEYED_TILES_TERRITORY`

*Constant*

```ts
const KEYED_TILES_TERRITORY = "planet";
```

The gateway territory a keyed default basemap reads. The gateway serves
it from the same `planet.pmtiles` build as [`DEFAULT_TERRITORY_TILES_URL`](#default_territory_tiles_url),
so feature ids, zoom range and the OpenMapTiles schema are identical.

#### `LOGO_SVG`

*Constant* · deprecated

```ts
const LOGO_SVG: string;
```

The Day mark as an SVG string (v2/MapBrand, 79.7 x 16.6 px art box).

**Deprecated.** Kept so existing imports compile; it is now the v2 Day mark,
not the old outline lockup. Use `mapBrandSvg("day" | "night")`.

#### `LUT_BYTES`

*Constant*

```ts
const LUT_BYTES: number;
```

Bytes in a LUT: 16 * 16 * 16 RGB triplets.

#### `LUT_SIZE`

*Constant*

```ts
const LUT_SIZE = 16;
```

Lattice points per axis. Fixed at 16 (Mapbox's size too).

#### `MAPMAP_BASES`

*Constant*

```ts
const MAPMAP_BASES: readonly MapMapBase[];
```

The catalogue, in display order.

#### `MARKER_DEFAULT_COLOUR`

*Constant*

```ts
const MARKER_DEFAULT_COLOUR = "#1a6bff";
```

Default marker colour (MapMap marker blue).

#### `MARKER_GLYPH_IDS`

*Constant*

```ts
const MARKER_GLYPH_IDS: readonly ["pin", "dot", "star", "heart", "flag", "home", "work", "food", "cafe", "bar", "shop", "hotel", "parking", "fuel", "charging", "transit", "bike", "camera", "music", "warning", "info"];
```

The 21 marker glyph ids of the `extra.markers` v1 contract, in order.

#### `MARKER_GLYPH_VIEWBOX`

*Constant*

```ts
const MARKER_GLYPH_VIEWBOX = 24;
```

The glyph and pin-body coordinate space (a 24x24 viewBox).

#### `MARKER_LABEL_TEXT_SIZES`

*Constant*

```ts
const MARKER_LABEL_TEXT_SIZES: Record<MarkerSize, number>;
```

Label text size in px for each marker size preset.

#### `MARKER_PIN_BODY_PATH`

*Constant*

```ts
const MARKER_PIN_BODY_PATH = "M12 24C7 17.2 3 13.2 3 9.4a9 9 0 0 1 18 0C21 13.2 17 17.2 12 24Z";
```

The coloured pin-body teardrop, 24x24 viewBox: head centre (12, 9.4),
radius 9, tip at (12, 24). Every glyph except `dot` is drawn in white
on top of this body, centred on the head.

#### `MARKER_SIZES`

*Constant*

```ts
const MARKER_SIZES: readonly ["s", "m", "l"];
```

The marker size presets, smallest first.

#### `MARKER_SIZES_PX`

*Constant*

```ts
const MARKER_SIZES_PX: Record<MarkerSize, number>;
```

CSS pixel size of each marker preset (the rasterised square canvas).

#### `markerGlyphPaths`

*Constant*

```ts
const markerGlyphPaths: Record<MarkerGlyphId, string>;
```

24x24 viewBox SVG path data for every marker glyph, keyed by id.

#### `MARKERS_ID`

*Constant*

```ts
const MARKERS_ID = "mm-user-markers";
```

The shared GeoJSON source id AND symbol layer id (contract).

#### `MAX_MARKER_ID_LENGTH`

*Constant*

```ts
const MAX_MARKER_ID_LENGTH = 64;
```

Maximum marker `id` length (contract).

#### `MAX_MARKER_IMAGE_BYTES`

*Constant*

```ts
const MAX_MARKER_IMAGE_BYTES: number;
```

Maximum accepted `data:` marker-image payload (matches the puck's cap).

#### `MAX_MARKER_ITEMS`

*Constant*

```ts
const MAX_MARKER_ITEMS = 200;
```

Maximum markers per theme (`extra.markers` v1 contract).

#### `MAX_MARKER_LABEL_LENGTH`

*Constant*

```ts
const MAX_MARKER_LABEL_LENGTH = 120;
```

Maximum marker `label` length (contract).

#### `MAX_MARKER_TEXT_LENGTH`

*Constant*

```ts
const MAX_MARKER_TEXT_LENGTH = 3;
```

Maximum characters drawn INSIDE a pin head by the short-text mode of
[`renderMarkerImage`](#rendermarkerimage) (numbered waypoints `1`, `2`, `3`; lettered
stops `A`, `B`). Anything longer belongs in a `label` below the pin.

#### `MAX_PUCK_IMAGE_BYTES`

*Constant*

```ts
const MAX_PUCK_IMAGE_BYTES: number;
```

Maximum accepted `data:` puck-image payload (matches Studio's upload cap).

#### `MAX_THEME_GEOJSON_SOURCES`

*Constant*

```ts
const MAX_THEME_GEOJSON_SOURCES = 8;
```

At most this many GeoJSON sources per theme.

#### `MAX_THEME_SOURCES`

*Constant*

```ts
const MAX_THEME_SOURCES = 15;
```

At most this many sources per theme. Mirrors `sn_style::MAX_THEME_SOURCES`.

#### `NAPTAN_SHORT_CREDIT`

*Constant*

```ts
const NAPTAN_SHORT_CREDIT = "Bus stops: NaPTAN, DfT, OGL v3.0";
```

The short NaPTAN credit the stops source adds to the map's credit line.
The full OGL wording is in the tiles index and THIRD-PARTY-NOTICES.md.

#### `NAV_ALERT_AUDIO_MODES`

*Constant*

```ts
const NAV_ALERT_AUDIO_MODES: readonly ["off", "earcon", "earcon_and_speech"];
```

Audio channel for a kind: silent, a non-verbal earcon, or an earcon
followed by speech naming the kind. Kind is carried in the ICON and in
SPEECH, never in the earcon's identity - urgency lives in timbre, so the
escalated state is the same motif made more insistent, not a new sound.

#### `NAV_ALERT_CHIP_POSITIONS`

*Constant*

```ts
const NAV_ALERT_CHIP_POSITIONS: readonly ["aboveSpeed", "topLeading", "topTrailing"];
```

Where the chip sits. Constrained slots, never free x/y: the chip must not
occlude the current speed or the limit. Covering exactly the two numbers
the alert is telling the driver to act on has been the single
most-complained-about defect in the category since 2023, and a free
position would let a customer reintroduce it.

#### `NAV_ALERT_ICON_SETS`

*Constant*

```ts
const NAV_ALERT_ICON_SETS: readonly ["european", "us", "minimal", "brand"];
```

Curated icon sets a design can pick for the alert chip and map pins.

#### `NAV_ALERT_LEAD_DISTANCE_M`

*Constant*

```ts
const NAV_ALERT_LEAD_DISTANCE_M: Record<NavAlertLeadDistance, number>;
```

Metres before the camera at which the alert may first appear, per preset.
These are FLOORS, not targets: a host may announce earlier (a motorway
closing speed eats 400 m in nine seconds) but never later. Exposed as an
enum rather than a numeric field precisely so no design can set a lead
below the safety floor.

#### `NAV_ALERT_LEAD_DISTANCES`

*Constant*

```ts
const NAV_ALERT_LEAD_DISTANCES: readonly ["short", "standard", "long"];
```

Lead-distance presets. Not a free number: see [`NAV_ALERT_LEAD_DISTANCE_M`](#nav_alert_lead_distance_m).

#### `NAV_ALERT_MODES`

*Constant*

```ts
const NAV_ALERT_MODES: readonly ["always", "whenSpeeding", "never"];
```

When a kind alerts AUDIBLY while driving.

The visual chip is governed by `alertWhileDriving` and is deliberately
unconditional and calm; audio is the escalation channel (the shipping
model: the pop-up always appears, the sound only when the driver is over
the limit). UI labels must describe the behaviour - "Always alert",
"Warn if speeding", "Never alert" - and must never say "Auto", which
Organic Maps relabelled after users could not tell what it did.

#### `NAV_ALERT_SOUND_BASE_URL`

*Constant*

```ts
const NAV_ALERT_SOUND_BASE_URL = "https://mapmap.ai/earcons/";
```

Where the built-in earcons are hosted. Public, unmetered and immutable
(the files are content-stable; a new sound gets a new name). Pass your
own base to [`navAlertSoundUrl`](#navalertsoundurl) to self-host them - the SDK bundles
no audio, exactly like `VoiceGuidance`'s earcon URLs.

#### `NAV_ALERT_SOUND_FILES`

*Constant*

```ts
const NAV_ALERT_SOUND_FILES: Record<NavAlertSound, string>;
```

File name of each built-in earcon, relative to
[`NAV_ALERT_SOUND_BASE_URL`](#nav_alert_sound_base_url). Mono, ~0.5-0.9 s, mixed dry with no
reverb tail so they stay crisp over road noise and never trail into the
spoken instruction that follows.

#### `NAV_ALERT_SOUNDS`

*Constant*

```ts
const NAV_ALERT_SOUNDS: readonly ["cameraCalm", "cameraUrgent", "overspeedSoft", "zoneEnter", "zoneClear"];
```

The built-in earcon library, as a curated enum rather than a free URL.

A design picks ONE sound for the calm alert and ONE for its escalated
variant - never one per camera kind. Kind is carried by the icon and by
speech; urgency is carried by timbre, so the escalated state is the same
motif made more insistent. Per-kind sounds would ask a driver to learn
five timbres at 70 mph, which no shipping product does.

`zoneEnter` / `zoneClear` are the average-speed corridor pair a host
plays at the entry and exit of a corridor. They are offered here too so
a design can borrow their timbre, but the corridor itself always uses
the pair - it is a boundary crossing, not a point alert.

#### `NAV_CAMERA_DEFAULTS`

*Constant*

```ts
const NAV_CAMERA_DEFAULTS: {
    readonly pitch: 60;
    readonly zoom: 17.5;
    readonly speedMps: 12;
};
```

Defaults for the optional `camera` block. LOCKSTEP: mirrors
`NAV_CAMERA_DEFAULTS` in `website/lib/studio-nav.ts`.

#### `NAV_CAMERA_KINDS`

*Constant*

```ts
const NAV_CAMERA_KINDS: readonly ["fixed", "average", "red_light", "mobile_site", "unknown"];
```

Safety-camera kinds a design can style, matching the kind the data model
already carries per camera. LOCKSTEP: mirrors `NAV_CAMERA_KINDS` in
`website/lib/studio-nav.ts`.

WIRE NOTE: the gateway serialises `sn_safety::CameraKind` lowercase
(`fixed` / `average` / `redlight` / `unknown`); `mobile_site` is a
product-level kind a host may classify itself (the sidecar deliberately
leaves an OSM `speed_camera=mobile` node as `unknown` rather than
overclaiming a fixed installation). Normalise a wire value with
[`navCameraKind`](#navcamerakind) before indexing a design.

#### `NO_ROADS_NEAR_ORIGIN`

*Constant*

```ts
const NO_ROADS_NEAR_ORIGIN = "urn:sn-gateway:problem:no-roads-near-origin";
```

The gateway's problem `type` for an origin with no road near it.

#### `OPENMAPTILES_ATTRIBUTION`

*Constant*

```ts
const OPENMAPTILES_ATTRIBUTION = "\u00A9 OpenMapTiles";
```

The OpenMapTiles schema attribution (CC-BY 4.0).

#### `OSM_ATTRIBUTION`

*Constant*

```ts
const OSM_ATTRIBUTION = "\u00A9 OpenStreetMap contributors";
```

The OSM attribution string. Legally required (ODbL) - never removed.

#### `PALETTE_SLOTS`

*Constant*

```ts
const PALETTE_SLOTS: readonly (readonly [string, string, string])[];
```

The 32 named palette slots as `[slot, lightDefault, darkDefault]`,
mirroring `sn_style::palette_slots()` (and `PALETTE_SLOTS` in the Studio
compiler, `website/lib/studio-style.ts`). Order is stable and
documentation-worthy: it is the order a Studio UI should present them in.

LOCKSTEP: this list, its defaults and the inherit rule in
`resolvePalette` must match the crate and the Studio compiler
exactly, or a theme authored in Studio fails to compile in the SDK. A
guard test parses `crates/sn-style/src/theme.rs` and fails on drift.

#### `POI_CATEGORY_COLORS`

*Constant*

```ts
const POI_CATEGORY_COLORS: readonly (readonly [string, string])[];
```

The valid category ids with their built-in dot colours, in presentation
order. LOCKSTEP: mirrors `POI_CATEGORIES` in `website/lib/poi-icons.ts`.

#### `POI_CATEGORY_IDS`

*Constant*

```ts
const POI_CATEGORY_IDS: readonly string[];
```

The valid category ids, in presentation order.

#### `POI_CLASS_CATEGORIES`

*Constant*

```ts
const POI_CLASS_CATEGORIES: Record<string, string[]>;
```

OpenMapTiles `poi` layer `class` values grouped into the categories
above; every class not listed falls through to `services`. LOCKSTEP:
mirrors `POI_CLASS_CATEGORIES` in `website/lib/poi-icons.ts`.

#### `PROJECTION_UNIFORMS`

*Constant*

```ts
const PROJECTION_UNIFORMS: readonly ["u_projection_matrix", "u_projection_tile_mercator_coords", "u_projection_clipping_plane", "u_projection_transition", "u_projection_fallback_matrix"];
```

The five uniforms MapLibre's projection prelude declares and reads.

#### `RIBBON_FLOATS_PER_VERTEX`

*Constant*

```ts
const RIBBON_FLOATS_PER_VERTEX = 5;
```

Floats per ribbon vertex: x, y (mercator), nx, ny (unit normal), progress.

#### `ROUTE_EFFECTS`

*Constant*

```ts
const ROUTE_EFFECTS: Record<RouteEffectName, (geometry: RouteGeometry, options?: RouteFlowOptions) => RouteEffectLayer>;
```

The effect registry: name -> layer factory. New effects (ambient life,
seasonal snow, fog-of-exploration) add an entry here and a name to
[`RouteEffectName`](#routeeffectname).

#### `SIGNAL_BLUE`

*Constant*

```ts
const SIGNAL_BLUE = "#3a86ff";
```

MapMap brand signal blue (see website `--signal`).

#### `SLOT_ANCHORS`

*Constant*

```ts
const SLOT_ANCHORS: Readonly<Record<Slot, {
    before: string;
    fallback: string[];
}>>;
```

Where each slot resolves: insert before the layer `before`; when the live
style has no such layer, try each `fallback` entry in turn; when nothing
matches, append (top of the stack).

A fallback entry matches a layer when:
- it ends with `-` and the layer id starts with it (`"road-"`), or
- it is a style-spec layer type and the layer has that type (`"symbol"`), or
- it equals the layer id (`"city-labels"`).

The first matching layer in style order wins.

#### `SLOTS`

*Constant*

```ts
const SLOTS: readonly Slot[];
```

Every slot, bottom of the stack first.

#### `SOURCE_LAYERS`

*Constant*

```ts
const SOURCE_LAYERS: readonly string[];
```

Every source-layer the Planetiler (OpenMapTiles-schema) tiles emit.
The style skeleton covers all of them; extra layers referencing anything
else are rejected. Mirrors `sn_style::SOURCE_LAYERS`.

#### `TRANSIT_LINE_FALLBACK_COLOUR`

*Constant*

```ts
const TRANSIT_LINE_FALLBACK_COLOUR = "#4c72b0";
```

Line colour where the timetable gives none (the first entry of the
MapMap points SDK's colour-blind-aware class palette).

#### `TRANSIT_TILES_INDEX_URL`

*Constant*

```ts
const TRANSIT_TILES_INDEX_URL = "https://tiles.mapmap.ai/index.json";
```

The tiles index the layer reads its archive URLs from.

## `@mapmap/maps/direction-icons`

```ts
import { /* … */ } from "@mapmap/maps/direction-icons";
```

Every export of this entry point is documented above, under `@mapmap/maps`: 7 names, that is all of them except [`addBusMarkerImages`](#addbusmarkerimages), [`AdrCheck`](#adrcheck), [`AdrCheckOptions`](#adrcheckoptions), [`AdrCheckRequest`](#adrcheckrequest), [`AdrCheckResult`](#adrcheckresult), [`AdrDimensions`](#adrdimensions), [`AdrTunnelCategory`](#adrtunnelcategory), [`ALERT_CONTRAST_MIN`](#alert_contrast_min), [`alertChipContent`](#alertchipcontent), [`AlertChipContent`](#alertchipcontent-1), [`AlertContrastIssue`](#alertcontrastissue), [`alertContrastIssues`](#alertcontrastissues), [`alertIconSvg`](#alerticonsvg), [`alertIconUrl`](#alerticonurl), [`alertPresentation`](#alertpresentation), [`AlertPresentation`](#alertpresentation-1), [`alertSoundUrl`](#alertsoundurl), [`alertSpeedReadoutColor`](#alertspeedreadoutcolor), [`applyColourTheme`](#applycolourtheme), [`applyGeoJSONDiff`](#applygeojsondiff), [`applyPoiDesign`](#applypoidesign), [`applyTerrain`](#applyterrain), [`AttributionPosition`](#attributionposition), [`BAKED_MARKERS_GLYPH_ID`](#baked_markers_glyph_id), [`BannerComponent`](#bannercomponent), [`BannerContent`](#bannercontent), [`BannerInstruction`](#bannerinstruction), [`bannerLanes`](#bannerlanes), [`BASE_ID_ALIASES`](#base_id_aliases), [`baseTheme`](#basetheme), [`bearingBetween`](#bearingbetween), [`bindFlythroughToScroll`](#bindflythroughtoscroll), [`BODS_SHORT_CREDIT`](#bods_short_credit), [`buildAdrCheckBody`](#buildadrcheckbody), [`buildGeocodeUrl`](#buildgeocodeurl), [`buildLut`](#buildlut), [`buildProbeUrl`](#buildprobeurl), [`buildRetrieveUrl`](#buildretrieveurl), [`buildReverseGeocodeUrl`](#buildreversegeocodeurl), [`buildRouteQuery`](#buildroutequery), [`buildRouteUrl`](#buildrouteurl), [`buildStyle`](#buildstyle), [`BuildStyleOptions`](#buildstyleoptions), [`buildSuggestUrl`](#buildsuggesturl), [`buildVertexSource`](#buildvertexsource), [`builtInPoiColor`](#builtinpoicolor), [`BUS_DEFAULT_VEHICLE_TYPE`](#bus_default_vehicle_type), [`BUS_IMAGES`](#bus_images), [`BUS_INK`](#bus_ink), [`BUS_RING_IMAGES`](#bus_ring_images), [`BUS_SPRITE_IMAGES`](#bus_sprite_images), [`BUS_STALE_AFTER_S`](#bus_stale_after_s), [`BUS_VEHICLE_TYPES`](#bus_vehicle_types), [`busMarkerLayers`](#busmarkerlayers), [`busPuckImage`](#buspuckimage), [`busPuckImageOptions`](#buspuckimageoptions), [`busSilhouetteSdf`](#bussilhouettesdf), [`busSpriteBody`](#busspritebody), [`busTickImage`](#bustickimage), [`busVehicleType`](#busvehicletype), [`BusVehicleType`](#busvehicletype-1), [`CameraAlert`](#cameraalert), [`CameraAlertChip`](#cameraalertchip), [`CameraFix`](#camerafix), [`cameraKindLabel`](#camerakindlabel), [`cameraKindsShownOnMap`](#camerakindsshownonmap), [`cameraShownOnMap`](#camerashownonmap), [`cameraSpriteName`](#cameraspritename), [`cameraSymbolLayer`](#camerasymbollayer), [`capTransitBbox`](#captransitbbox), [`checkSourceUrl`](#checksourceurl), [`CLIP_DEFAULT_MINZOOM`](#clip_default_minzoom), [`CLIP_MAX_VERTICES`](#clip_max_vertices), [`CLIP_TARGETS`](#clip_targets), [`clipFilter`](#clipfilter), [`ClipMatrices`](#clipmatrices), [`ClipOptions`](#clipoptions), [`ClipType`](#cliptype), [`ColorThemeSpec`](#colorthemespec), [`COLOUR_THEME_LAYER_ID`](#colour_theme_layer_id), [`COLOUR_THEME_METADATA_KEY`](#colour_theme_metadata_key), [`ColourTheme`](#colourtheme), [`ColourThemeLayer`](#colourthemelayer), [`ColourThemeLayerOptions`](#colourthemelayeroptions), [`colourThemeMetadata`](#colourthememetadata), [`contrastRatio`](#contrastratio), [`createMap`](#createmap), [`createRouteEffect`](#createrouteeffect), [`CreditControl`](#creditcontrol), [`CreditInnerControl`](#creditinnercontrol), [`CreditOptions`](#creditoptions), [`creditTier`](#credittier), [`CreditTier`](#credittier-1), [`DataLayer`](#datalayer), [`DataLayerCategories`](#datalayercategories), [`DataLayerClusterOptions`](#datalayerclusteroptions), [`dataLayerColor`](#datalayercolor), [`DataLayerEvent`](#datalayerevent), [`DataLayerExtrusionOptions`](#datalayerextrusionoptions), [`DataLayerGraduated`](#datalayergraduated), [`DataLayerMode`](#datalayermode), [`DataLayerOptions`](#datalayeroptions), [`DataLayerSimpleStyle`](#datalayersimplestyle), [`DataLayerStyle`](#datalayerstyle), [`DEFAULT_DEBOUNCE_MS`](#default_debounce_ms), [`DEFAULT_GATEWAY_URL`](#default_gateway_url), [`DEFAULT_GLYPHS_URL`](#default_glyphs_url), [`DEFAULT_MAX_MERCATOR_SEGMENT`](#default_max_mercator_segment), [`DEFAULT_TERRAIN_ATTRIBUTION`](#default_terrain_attribution), [`DEFAULT_TERRAIN_URL`](#default_terrain_url), [`DEFAULT_TERRITORY_TILES_URL`](#default_territory_tiles_url), [`defaultNavAlertsDesign`](#defaultnavalertsdesign), [`defaultNavDesign`](#defaultnavdesign), [`defaultPoiDesign`](#defaultpoidesign), [`DiagnosticIssue`](#diagnosticissue), [`directionArrow`](#directionarrow), [`EFFECTS_METADATA_KEY`](#effects_metadata_key), [`effectsFromStyleMetadata`](#effectsfromstylemetadata), [`EndpointMarker`](#endpointmarker), [`EndpointsDesign`](#endpointsdesign), [`extractGuidance`](#extractguidance), [`FeaturesetId`](#featuresetid), [`featuresetLayerIds`](#featuresetlayerids), [`FEATURESETS`](#featuresets), [`FixSet`](#fixset), [`fixSetFromCollection`](#fixsetfromcollection), [`FLOW_DEFAULTS`](#flow_defaults), [`FlowRouteEffectLayer`](#flowrouteeffectlayer), [`flythrough`](#flythrough), [`FlythroughController`](#flythroughcontroller), [`FlythroughOptions`](#flythroughoptions), [`flythroughPose`](#flythroughpose), [`FlythroughPose`](#flythroughpose-1), [`formatCoord`](#formatcoord), [`formatCoords`](#formatcoords), [`FULL_ATTRIBUTION`](#full_attribution), [`GeocodeHit`](#geocodehit), [`GeocodeKind`](#geocodekind), [`GeocodeOptions`](#geocodeoptions), [`Geocoder`](#geocoder), [`GeocoderOptions`](#geocoderoptions), [`GLOBE_BLEND_GLSL`](#globe_blend_glsl), [`GLOBE_RADIUS_M`](#globe_radius_m), [`globeModelMatrix`](#globemodelmatrix), [`gradeColourString`](#gradecolourstring), [`gradeStyleColours`](#gradestylecolours), [`GradeStyleOptions`](#gradestyleoptions), [`GroundReadableMap`](#groundreadablemap), [`GuidanceBanner`](#guidancebanner), [`GuidanceSeverity`](#guidanceseverity), [`GuidanceSpokenPrompt`](#guidancespokenprompt), [`hasBakedMarkers`](#hasbakedmarkers), [`haversineDistanceM`](#haversinedistancem), [`holdLayersOnTop`](#holdlayersontop), [`IDENTITY_LUT`](#identity_lut), [`Interaction`](#interaction), [`InteractionEvent`](#interactionevent), [`InteractionRegistry`](#interactionregistry), [`InteractionTarget`](#interactiontarget), [`InteractionType`](#interactiontype), [`interpolateFixes`](#interpolatefixes), [`isMarkerGlyphId`](#ismarkerglyphid), [`isNameTextField`](#isnametextfield), [`IsochroneError`](#isochroneerror), [`IsochroneFeatureCollection`](#isochronefeaturecollection), [`IsochroneLayer`](#isochronelayer), [`IsochroneLayerOptions`](#isochronelayeroptions), [`isSlot`](#isslot), [`KEYED_TILES_TERRITORY`](#keyed_tiles_territory), [`keyedStyleUrl`](#keyedstyleurl), [`keyedTilesUrl`](#keyedtilesurl), [`laneArrowDirection`](#lanearrowdirection), [`LaneIndication`](#laneindication), [`languageTextField`](#languagetextfield), [`LayerOverride`](#layeroverride), [`layersOnTopMoves`](#layersontopmoves), [`liveTargetLayerIds`](#livetargetlayerids), [`LngLatLike`](#lnglatlike), [`lngLatToMercator`](#lnglattomercator), [`locateProjectionUniforms`](#locateprojectionuniforms), [`LOGO_SVG`](#logo_svg), [`LogoControl`](#logocontrol), [`LogoOptions`](#logooptions), [`LogoPosition`](#logoposition), [`LUT_BYTES`](#lut_bytes), [`LUT_SIZE`](#lut_size), [`lutApply`](#lutapply), [`LutBytes`](#lutbytes), [`lutDigest`](#lutdigest), [`lutFromBase64`](#lutfrombase64), [`lutFromImageData`](#lutfromimagedata), [`lutFromPngData`](#lutfrompngdata), [`LutImageData`](#lutimagedata), [`lutToBase64`](#luttobase64), [`lutToImageData`](#luttoimagedata), [`mapBrandSvg`](#mapbrandsvg), [`MapBrandTheme`](#mapbrandtheme), [`MapDiagnosticsInput`](#mapdiagnosticsinput), [`MapDisplayMode`](#mapdisplaymode), [`mapGroundTheme`](#mapgroundtheme), [`MAPMAP_BASES`](#mapmap_bases), [`MapMapBase`](#mapmapbase), [`MapMapBaseGroup`](#mapmapbasegroup), [`MapMapBaseId`](#mapmapbaseid), [`MapMapMap`](#mapmapmap), [`MapMapOptions`](#mapmapoptions), [`MapMapTheme`](#mapmaptheme), [`MARKER_DEFAULT_COLOUR`](#marker_default_colour), [`MARKER_GLYPH_IDS`](#marker_glyph_ids), [`MARKER_GLYPH_VIEWBOX`](#marker_glyph_viewbox), [`MARKER_LABEL_TEXT_SIZES`](#marker_label_text_sizes), [`MARKER_PIN_BODY_PATH`](#marker_pin_body_path), [`MARKER_SIZES`](#marker_sizes), [`MARKER_SIZES_PX`](#marker_sizes_px), [`MarkerGlyphId`](#markerglyphid), [`markerGlyphPaths`](#markerglyphpaths), [`markerImage`](#markerimage), [`markerImageDataKey`](#markerimagedatakey), [`markerImageKey`](#markerimagekey), [`MarkerItem`](#markeritem), [`MARKERS_ID`](#markers_id), [`MarkersBlock`](#markersblock), [`markersFromTheme`](#markersfromtheme), [`markersFromThemeUrl`](#markersfromthemeurl), [`MarkerSize`](#markersize), [`MarkersLayer`](#markerslayer), [`MarkersLayerIds`](#markerslayerids), [`MarkersLayerOptions`](#markerslayeroptions), [`markerText`](#markertext), [`markerTextImageKey`](#markertextimagekey), [`MAX_MARKER_ID_LENGTH`](#max_marker_id_length), [`MAX_MARKER_IMAGE_BYTES`](#max_marker_image_bytes), [`MAX_MARKER_ITEMS`](#max_marker_items), [`MAX_MARKER_LABEL_LENGTH`](#max_marker_label_length), [`MAX_MARKER_TEXT_LENGTH`](#max_marker_text_length), [`MAX_PUCK_IMAGE_BYTES`](#max_puck_image_bytes), [`MAX_THEME_GEOJSON_SOURCES`](#max_theme_geojson_sources), [`MAX_THEME_SOURCES`](#max_theme_sources), [`mercatorModelMatrix`](#mercatormodelmatrix), [`modelClipMatrices`](#modelclipmatrices), [`multiplyMat4`](#multiplymat4), [`namedBase`](#namedbase), [`NAPTAN_SHORT_CREDIT`](#naptan_short_credit), [`NAV_ALERT_AUDIO_MODES`](#nav_alert_audio_modes), [`NAV_ALERT_CHIP_POSITIONS`](#nav_alert_chip_positions), [`NAV_ALERT_ICON_SETS`](#nav_alert_icon_sets), [`NAV_ALERT_LEAD_DISTANCE_M`](#nav_alert_lead_distance_m), [`NAV_ALERT_LEAD_DISTANCES`](#nav_alert_lead_distances), [`NAV_ALERT_MODES`](#nav_alert_modes), [`NAV_ALERT_SOUND_BASE_URL`](#nav_alert_sound_base_url), [`NAV_ALERT_SOUND_FILES`](#nav_alert_sound_files), [`NAV_ALERT_SOUNDS`](#nav_alert_sounds), [`NAV_CAMERA_DEFAULTS`](#nav_camera_defaults), [`NAV_CAMERA_KINDS`](#nav_camera_kinds), [`NavAlertAudioMode`](#navalertaudiomode), [`NavAlertChipPosition`](#navalertchipposition), [`NavAlertIconSet`](#navalerticonset), [`NavAlertKindDesign`](#navalertkinddesign), [`NavAlertLeadDistance`](#navalertleaddistance), [`navAlertLeadDistanceM`](#navalertleaddistancem), [`NavAlertMode`](#navalertmode), [`NavAlertsDesign`](#navalertsdesign), [`NavAlertSound`](#navalertsound), [`navAlertSoundUrl`](#navalertsoundurl), [`NavBannerDesign`](#navbannerdesign), [`NavCameraDesign`](#navcameradesign), [`navCameraKind`](#navcamerakind), [`NavCameraKind`](#navcamerakind-1), [`NavDesign`](#navdesign), [`navDesignFromTheme`](#navdesignfromtheme), [`navDesignFromThemeUrl`](#navdesignfromthemeurl), [`NavigationCamera`](#navigationcamera), [`NavigationCameraMode`](#navigationcameramode), [`NavigationCameraOptions`](#navigationcameraoptions), [`NavPuckDesign`](#navpuckdesign), [`NavRouteDesign`](#navroutedesign), [`NearestByDriveTimeOptions`](#nearestbydrivetimeoptions), [`NO_ROADS_NEAR_ORIGIN`](#no_roads_near_origin), [`OPENMAPTILES_ATTRIBUTION`](#openmaptiles_attribution), [`OSM_ATTRIBUTION`](#osm_attribution), [`osmAndMoreCredit`](#osmandmorecredit), [`PALETTE_SLOTS`](#palette_slots), [`parseCssColour`](#parsecsscolour), [`ParsedRoute`](#parsedroute), [`parseGeocodeResponse`](#parsegeocoderesponse), [`parseHexColor`](#parsehexcolor), [`parseMarkers`](#parsemarkers), [`parseNavAlertsDesign`](#parsenavalertsdesign), [`parseNavDesign`](#parsenavdesign), [`parseOsrmRoute`](#parseosrmroute), [`parsePoiDesign`](#parsepoidesign), [`parseSuggestResponse`](#parsesuggestresponse), [`PersistentIds`](#persistentids), [`PersistentLayerOptions`](#persistentlayeroptions), [`Place`](#place), [`PlacePointFeature`](#placepointfeature), [`PlacesFeatureCollection`](#placesfeaturecollection), [`placesFromGeoJSON`](#placesfromgeojson), [`PlacesIcon`](#placesicon), [`PlacesInput`](#placesinput), [`PlacesLabelOptions`](#placeslabeloptions), [`PlacesLayer`](#placeslayer), [`PlacesLayerIds`](#placeslayerids), [`PlacesLayerOptions`](#placeslayeroptions), [`PlacesSelectOptions`](#placesselectoptions), [`PlaceWithDistance`](#placewithdistance), [`PlaceWithDriveTime`](#placewithdrivetime), [`POI_CATEGORY_COLORS`](#poi_category_colors), [`POI_CATEGORY_IDS`](#poi_category_ids), [`POI_CLASS_CATEGORIES`](#poi_class_categories), [`PoiCategoryDesign`](#poicategorydesign), [`PoiDesign`](#poidesign), [`poiDesignFromTheme`](#poidesignfromtheme), [`poiDesignFromThemeUrl`](#poidesignfromthemeurl), [`poiDesignIsDefault`](#poidesignisdefault), [`poiTextColorExpression`](#poitextcolorexpression), [`PositionPuck`](#positionpuck), [`PositionPuckOptions`](#positionpuckoptions), [`prefersReducedMotion`](#prefersreducedmotion), [`ProbeUploadOutcome`](#probeuploadoutcome), [`ProgramCache`](#programcache), [`PROJECTION_UNIFORMS`](#projection_uniforms), [`ProjectionArgs`](#projectionargs), [`ProjectionUniform`](#projectionuniform), [`projectionVariant`](#projectionvariant), [`ProjectionVariant`](#projectionvariant-1), [`ProtocolHost`](#protocolhost), [`ReachabilityMode`](#reachabilitymode), [`readProjection`](#readprojection), [`registerPmtilesProtocol`](#registerpmtilesprotocol), [`relativeLuminance`](#relativeluminance), [`removeTerrain`](#removeterrain), [`renderClockMs`](#renderclockms), [`Rendered`](#rendered), [`RenderedMarkerImage`](#renderedmarkerimage), [`renderMarkerImage`](#rendermarkerimage), [`RenderMarkerImageOptions`](#rendermarkerimageoptions), [`resetDiagnostics`](#resetdiagnostics), [`resolveBaseId`](#resolvebaseid), [`resolveSlot`](#resolveslot), [`resolveTerrain`](#resolveterrain), [`resolveTheme`](#resolvetheme), [`ReverseGeocodeOptions`](#reversegeocodeoptions), [`RIBBON_FLOATS_PER_VERTEX`](#ribbon_floats_per_vertex), [`RibbonMesh`](#ribbonmesh), [`ROUTE_EFFECTS`](#route_effects), [`RouteEffectLayer`](#routeeffectlayer), [`RouteEffectName`](#routeeffectname), [`RouteFlowOptions`](#routeflowoptions), [`RouteGeometry`](#routegeometry), [`RouteLayer`](#routelayer), [`RouteLayerIds`](#routelayerids), [`RouteLayerOptions`](#routelayeroptions), [`RouteLineStyle`](#routelinestyle), [`RouteOptions`](#routeoptions), [`RouteProfile`](#routeprofile), [`runMapDiagnostics`](#runmapdiagnostics), [`SearchBox`](#searchbox), [`SearchBoxBias`](#searchboxbias), [`SearchBoxMap`](#searchboxmap), [`SearchBoxOptions`](#searchboxoptions), [`SearchBoxSelectEvent`](#searchboxselectevent), [`SearchBoxSelectListener`](#searchboxselectlistener), [`SearchSuggestion`](#searchsuggestion), [`setMapLanguage`](#setmaplanguage), [`setProjectionUniforms`](#setprojectionuniforms), [`SetRouteEffectOptions`](#setrouteeffectoptions), [`severityProsody`](#severityprosody), [`SeverityProsody`](#severityprosody-1), [`shortCredit`](#shortcredit), [`shortestArcDeg`](#shortestarcdeg), [`shortestArcDelta`](#shortestarcdelta), [`ShowReachabilityOptions`](#showreachabilityoptions), [`SIGNAL_BLUE`](#signal_blue), [`Slot`](#slot), [`SLOT_ANCHORS`](#slot_anchors), [`SLOTS`](#slots), [`SnappedOrigin`](#snappedorigin), [`SOURCE_LAYERS`](#source_layers), [`speak`](#speak), [`SpeakOptions`](#speakoptions), [`sphereFrame`](#sphereframe), [`sphereSurface`](#spheresurface), [`ssmlToText`](#ssmltotext), [`StepGuidance`](#stepguidance), [`styleFromBase`](#stylefrombase), [`styleGroundTheme`](#stylegroundtheme), [`subdivideMercator`](#subdividemercator), [`SuggestUrlOptions`](#suggesturloptions), [`sunTimes`](#suntimes), [`SunTimes`](#suntimes-1), [`supportsTerrainDraping`](#supportsterraindraping), [`TerrainOptions`](#terrainoptions), [`territoryTilesSourceUrl`](#territorytilessourceurl), [`tessellateRouteRibbon`](#tessellaterouteribbon), [`Theme`](#theme), [`ThemeEffects`](#themeeffects), [`ThemeScheduler`](#themescheduler), [`ThemeSchedulerOptions`](#themescheduleroptions), [`ThemeSource`](#themesource), [`tidyPlaceName`](#tidyplacename), [`toLngLat`](#tolnglat), [`toPmtilesUrl`](#topmtilesurl), [`TRANSIT_LINE_FALLBACK_COLOUR`](#transit_line_fallback_colour), [`TRANSIT_TILES_INDEX_URL`](#transit_tiles_index_url), [`transitArchiveUrl`](#transitarchiveurl), [`TransitDrawnVehicle`](#transitdrawnvehicle), [`TransitFeedAnswer`](#transitfeedanswer), [`TransitFeedReport`](#transitfeedreport), [`TransitFeedStatus`](#transitfeedstatus), [`TransitFix`](#transitfix), [`transitFreshnessLabel`](#transitfreshnesslabel), [`TransitSlot`](#transitslot), [`TransitStopFeature`](#transitstopfeature), [`transitStopFromProperties`](#transitstopfromproperties), [`TransitStopsLayer`](#transitstopslayer), [`TransitStopsLayerOptions`](#transitstopslayeroptions), [`transitTileJsonUrl`](#transittilejsonurl), [`transitUrlsFromIndex`](#transiturlsfromindex), [`TransitVehicle`](#transitvehicle), [`TransitVehiclesLayer`](#transitvehicleslayer), [`TransitVehiclesLayerOptions`](#transitvehicleslayeroptions), [`TruckParams`](#truckparams), [`truncateChars`](#truncatechars), [`uploadProbeBatch`](#uploadprobebatch), [`UploadProbeOptions`](#uploadprobeoptions), [`UseTheme`](#usetheme), [`validateColourTheme`](#validatecolourtheme), [`validateUseTheme`](#validateusetheme), [`Vec3`](#vec3), [`VoiceGuidance`](#voiceguidance), [`VoiceGuidanceOptions`](#voiceguidanceoptions), [`VoiceGuidanceUpdate`](#voiceguidanceupdate), [`VoiceInstruction`](#voiceinstruction).

## `@mapmap/maps/avatars`

```ts
import { /* … */ } from "@mapmap/maps/avatars";
```

### Classes (@mapmap/maps/avatars)

[`AvatarPuck`](#avatarpuck)

#### `AvatarPuck`

*Class*

```ts
class AvatarPuck { … }
```

The user's vehicle drawn as a MapMap car. A drop-in for `PositionPuck`:

```ts
import { AvatarPuck } from "@mapmap/maps/avatars";
const car = new AvatarPuck(map, { car: "city", paint: "signal-blue", size: "m" });
car.setLocation({ lat, lon }, headingDeg);
```

The artwork for the current pitch bucket (about 0.4 MB) is fetched when
the puck is created; other buckets load when the camera tilts into them,
while the previous one stays on screen. The layer is re-installed after
`setStyle`. If the artwork cannot be loaded or decoded, a `PositionPuck`
is drawn instead and `onFallback` fires; the API keeps working either way.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly sourceId` | `string` |  | The GeoJSON source id this puck owns. |
| `readonly layerId` | `string` |  | The symbol layer id this puck owns. |
| `readonly car` | [`AvatarCar`](#avatarcar) |  | The current car. |
| `readonly paint` | [`AvatarPaint`](#avatarpaint) |  | The current paint. |
| `readonly fallback` | [`AvatarFallbackEvent \| undefined`](#avatarfallbackevent) |  | The fallback reason, if this puck is drawing the Arrow puck instead of the artwork. |

**Constructor and methods**

- `constructor(map: MapMapMap | Map, options?: AvatarPuckOptions)`
- `whenReady(): Promise<void>`: Resolves once the first pitch bucket's artwork is on the map, or once the puck has fallen back to the Arrow puck (check `onFallback`). Never rejects.
- `setLocation(location: { lat: number; lon: number; }, headingDeg?: number): void`: Moves the vehicle (adding it to the map on the first call). `headingDeg` is the course, clockwise from north; omit it to keep the previous one. Same glide as `PositionPuck`.
- `setAvatar(avatar: { car?: AvatarCar; paint?: AvatarPaint; }): void`: Swaps the car and/or paint, keeping position and heading. The new artwork loads before it replaces the old, so there is no blank frame. A puck that had fallen back tries the artwork again.
- `remove(): void`: Removes the vehicle from the map (cancelling any glide). `setLocation` adds it again.

**Example**

```ts
import { map } from "./map.ts";
import { AvatarPuck } from "@mapmap/maps/avatars";

const car = new AvatarPuck(map, { car: "city", paint: "signal-blue", size: "m" });
await car.whenReady();
car.setLocation({ lat: 51.5072, lon: -0.1276 }, 45);
```

### Functions (@mapmap/maps/avatars)

[`avatarAlignment`](#avataralignment), [`avatarCellRect`](#avatarcellrect), [`avatarFeatureProperties`](#avatarfeatureproperties), [`avatarIconOffset`](#avatariconoffset), [`avatarIconSize`](#avatariconsize), [`avatarImageId`](#avatarimageid), [`avatarModelUrl`](#avatarmodelurl), [`avatarPixelRatio`](#avatarpixelratio), [`avatarSizePx`](#avatarsizepx), [`avatarSpriteUrl`](#avatarspriteurl), [`avatarSymbolLayer`](#avatarsymbollayer), [`headingBucket`](#headingbucket), [`pitchBucket`](#pitchbucket)

#### `avatarAlignment`

*Function*

```ts
function avatarAlignment(pitch: AvatarPitch): {
    "icon-rotation-alignment": "map" | "viewport";
    "icon-pitch-alignment": "map" | "viewport";
};
```

The layout properties that differ between the top-down bucket and the
pitched ones (not data-driven in MapLibre, so they are set on the layer).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `pitch` | [`AvatarPitch`](#avatarpitch) | required |  |

Options and types: [`AvatarPitch`](#avatarpitch).

#### `avatarCellRect`

*Function*

```ts
function avatarCellRect(index: number, pixelRatio?: 1 | 2): {
    x: number;
    y: number;
    width: number;
    height: number;
};
```

The rectangle of heading cell `index` in a bucket's atlas, in atlas pixels.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `index` | `number` | required |  |
| `pixelRatio?` | `1 \| 2` | not stated |  |

#### `avatarFeatureProperties`

*Function*

```ts
function avatarFeatureProperties(car: AvatarCar, paint: AvatarPaint, pitch: AvatarPitch, courseDeg: number, bearingDeg: number, size?: AvatarSize | number): {
    image: string;
    rotate: number;
    size: number;
    offset: [number, number];
};
```

The feature properties that draw one vehicle: the cell image, its
rotation (the course when top-down, 0 when pitched), size and offset.
Pair with [`avatarSymbolLayer`](#avatarsymbollayer); a fleet layer can hold many such
features in one GeoJSON source (all in the same pitch bucket).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `paint` | [`AvatarPaint`](#avatarpaint) | required |  |
| `pitch` | [`AvatarPitch`](#avatarpitch) | required |  |
| `courseDeg` | `number` | required |  |
| `bearingDeg` | `number` | required |  |
| `size?` | `AvatarSize \| number` | not stated |  |

Options and types: [`AvatarCar`](#avatarcar), [`AvatarPaint`](#avatarpaint), [`AvatarPitch`](#avatarpitch), [`AvatarSize`](#avatarsize).

#### `avatarIconOffset`

*Function*

```ts
function avatarIconOffset(car: AvatarCar, pitch: AvatarPitch): [number, number];
```

MapLibre `icon-offset` (1x cell pixels, scaled by `icon-size`) that puts
the disc centre, not the cell centre, on the vehicle's coordinate.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `pitch` | [`AvatarPitch`](#avatarpitch) | required |  |

Options and types: [`AvatarCar`](#avatarcar), [`AvatarPitch`](#avatarpitch).

#### `avatarIconSize`

*Function*

```ts
function avatarIconSize(car: AvatarCar, pitch: AvatarPitch, size?: AvatarSize | number): number;
```

MapLibre `icon-size` that makes the disc `sizePx` CSS pixels wide in a
bucket (each bucket's disc is a different width in the cell, so the scale
differs by pitch).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `pitch` | [`AvatarPitch`](#avatarpitch) | required |  |
| `size?` | `AvatarSize \| number` | not stated |  |

Options and types: [`AvatarCar`](#avatarcar), [`AvatarPitch`](#avatarpitch), [`AvatarSize`](#avatarsize).

#### `avatarImageId`

*Function*

```ts
function avatarImageId(car: AvatarCar, paint: AvatarPaint, pitch: AvatarPitch, headingIndex?: number): string;
```

The style image id of one cell, e.g. `mapmap-avatar:city:signal-blue:p58:h090`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `paint` | [`AvatarPaint`](#avatarpaint) | required |  |
| `pitch` | [`AvatarPitch`](#avatarpitch) | required |  |
| `headingIndex?` | `number` | not stated |  |

Options and types: [`AvatarCar`](#avatarcar), [`AvatarPaint`](#avatarpaint), [`AvatarPitch`](#avatarpitch).

#### `avatarModelUrl`

*Function*

```ts
function avatarModelUrl(car: AvatarCar, options?: {
    lod?: 0 | 1;
    baseUrl?: string;
}): string;
```

URL of a car's glTF binary model, for your own three.js or deck.gl layer.
`lod: 1` is the lighter model (about 5k triangles). Repaint it by setting
the `Paint_GlacierWhite` material's `baseColorFactor` from `models/paints.json`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `options?` | `{ lod?: 0 \| 1; baseUrl?: string; }` | not stated |  |

Options and types: [`AvatarCar`](#avatarcar).

#### `avatarPixelRatio`

*Function*

```ts
function avatarPixelRatio(car: AvatarCar, size: AvatarSize | number, devicePixelRatio: number): 1 | 2;
```

Which atlas resolution to load: the 1x set (256 px cells) unless the
disc needs more device pixels than a 1x cell holds.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `size` | `AvatarSize \| number` | required |  |
| `devicePixelRatio` | `number` | required |  |

Options and types: [`AvatarCar`](#avatarcar), [`AvatarSize`](#avatarsize).

#### `avatarSizePx`

*Function*

```ts
function avatarSizePx(size?: AvatarSize | number): number;
```

A named size or a CSS pixel count, as the disc diameter in CSS pixels.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `size?` | `AvatarSize \| number` | not stated |  |

Options and types: [`AvatarSize`](#avatarsize).

#### `avatarSpriteUrl`

*Function*

```ts
function avatarSpriteUrl(car: AvatarCar, paint: AvatarPaint, pitch: AvatarPitch, options?: {
    baseUrl?: string;
    pixelRatio?: 1 | 2;
    ext?: "webp" | "json";
}): string;
```

URL of one sprite atlas (WebP) or its cell index (`ext: "json"`, the
MapLibre sprite JSON shape, for MapLibre Native and other consumers).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `car` | [`AvatarCar`](#avatarcar) | required |  |
| `paint` | [`AvatarPaint`](#avatarpaint) | required |  |
| `pitch` | [`AvatarPitch`](#avatarpitch) | required |  |
| `options?` | `{ baseUrl?: string; pixelRatio?: 1 \| 2; ext?: "webp" \| "json"; }` | not stated |  |

Options and types: [`AvatarCar`](#avatarcar), [`AvatarPaint`](#avatarpaint), [`AvatarPitch`](#avatarpitch).

#### `avatarSymbolLayer`

*Function*

```ts
function avatarSymbolLayer(source: string, id: string, pitch?: AvatarPitch): Record<string, unknown>;
```

A MapLibre symbol layer that draws avatar features (the `cameraSymbolLayer`
pattern): image, rotation, size and offset come from each feature's
properties ([`avatarFeatureProperties`](#avatarfeatureproperties)); the alignment is the
bucket's ([`avatarAlignment`](#avataralignment)). The vehicle never yields to labels.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `string` | required |  |
| `id` | `string` | required |  |
| `pitch?` | [`AvatarPitch`](#avatarpitch) | not stated |  |

Options and types: [`AvatarPitch`](#avatarpitch).

#### `headingBucket`

*Function*

```ts
function headingBucket(courseDeg: number, bearingDeg: number): number;
```

The heading cell (0 to 35) for a vehicle course and a map bearing, both
degrees clockwise from north: `round(((course - bearing) mod 360) / 10)
mod 36`. The cell is relative to the camera, so it changes when the map
rotates even if the vehicle does not.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `courseDeg` | `number` | required |  |
| `bearingDeg` | `number` | required |  |

#### `pitchBucket`

*Function*

```ts
function pitchBucket(pitchDeg: number): AvatarPitch;
```

The pitch bucket for a map pitch (degrees from straight down): the nearest
of 0, 30, 45 and 58, with boundaries at 15, 37.5 and 51.5. Pitches above
58 use 58 (the render is then slightly under-tilted, which reads fine up
to the SDK's navigation pitch of 60 and beyond).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `pitchDeg` | `number` | required |  |

### Interfaces (@mapmap/maps/avatars)

[`AvatarFallbackEvent`](#avatarfallbackevent), [`AvatarManifest`](#avatarmanifest), [`AvatarManifestPitch`](#avatarmanifestpitch), [`AvatarPuckOptions`](#avatarpuckoptions)

#### `AvatarFallbackEvent`

*Interface*

```ts
interface AvatarFallbackEvent
```

Why an `AvatarPuck` fell back to the Arrow puck.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `reason` | `"load" \| "decode" \| "unsupported"` | required | `load`: the artwork could not be fetched (network, CORS, 404, timeout); `decode`: it arrived but could not be decoded; `unsupported`: the browser has no `fetch` or `createImageBitmap`. |
| `url?` | `string` | not stated | The atlas URL involved, when there was one. |

#### `AvatarManifest`

*Interface*

```ts
interface AvatarManifest
```

The shape of [`AVATAR_MANIFEST`](#avatar_manifest) (generated from the render metadata).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly version` | `string` | required |  |
| `readonly license` | `string` | required |  |
| `readonly headingStepDeg` | `number` | required |  |
| `readonly headingCount` | `number` | required |  |
| `readonly pitches` | `readonly number[]` | required |  |
| `readonly atlas` | `{ readonly cellPx: number; readonly columns: number; readonly gapPx: number; }` | required |  |
| `readonly cars` | `readonly { readonly id: string; readonly name: string; readonly discDiameterM: number; readonly pitches: { readonly [pitch: string]: AvatarManifestPitch; }; }[]` | required |  |
| `readonly paints` | `readonly { readonly id: string; readonly name: string; readonly hex: string; }[]` | required |  |

#### `AvatarManifestPitch`

*Interface*

```ts
interface AvatarManifestPitch
```

One pitch bucket's sprite geometry, in 1x cell pixels.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly anchorPx` | `readonly [number, number]` | required | The disc centre (the vehicle's position) in the cell, origin top left. |
| `readonly discPx` | `number` | required | The disc's width in the cell. |
| `readonly headings` | `number` | required | Cells in this bucket's atlas: 36 when pitched, 1 top-down. |
| `readonly metresPerPx` | `number` | required | Metres per cell pixel at the disc centre. |

#### `AvatarPuckOptions`

*Interface*

```ts
interface AvatarPuckOptions
```

Options for [`AvatarPuck`](#avatarpuck).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `car?` | [`AvatarCar`](#avatarcar) | `"city"` | The vehicle (default `"city"`). |
| `paint?` | [`AvatarPaint`](#avatarpaint) | `"glacier-white"` | The paint (default `"glacier-white"`). |
| `size?` | `AvatarSize \| number` | not stated | Disc diameter: `"s"` 48, `"m"` 64 (default), `"l"` 80 CSS pixels, or a number. |
| `interpolate?` | `boolean` | `true` | Glide between fixes instead of snapping (default `true`), as `PositionPuck`. |
| `assetBaseUrl?` | `string` | [`AVATAR_ASSET_BASE_URL`](#avatar_asset_base_url) | Where the artwork is served from (default [`AVATAR_ASSET_BASE_URL`](#avatar_asset_base_url)). |
| `beforeId?` | `string` | on top of everything, like `PositionPuck` | Insert the layer before this layer id (default: on top of everything, like `PositionPuck`). |
| `prefetch?` | `"current" \| "all-pitches"` | `"current"`: only what is shown | Also fetch the other pitch buckets once the first is drawn (default `"current"`: only what is shown). |
| `onFallback?` | `(event: AvatarFallbackEvent) => void` | not stated | Called once if the artwork cannot be used and the Arrow puck is drawn instead. |
| `loadTimeoutMs?` | `number` | 10000 | Give up on an atlas request after this long, ms (default 10000). |
| `devicePixelRatio?` | `number` | `window.devicePixelRatio` | Device pixel ratio override (default `window.devicePixelRatio`). |
| `now?` | `() => number` | Test seam: clock override, defaults to `Date.now` | Test seam: clock override, defaults to `Date.now`. |
| `requestFrame?` | `(callback: () => void) => number` | Test seam: frame scheduler override, defaults to rAF | Test seam: frame scheduler override, defaults to rAF. |
| `cancelFrame?` | `(handle: number) => void` | Test seam: frame canceller override, defaults to cancelAnimationFrame | Test seam: frame canceller override, defaults to cancelAnimationFrame. |
| `fetch?` | `FetchLike` | not stated | Test seam: `fetch` override. |
| `createImageBitmap?` | `BitmapFactory` | not stated | Test seam: `createImageBitmap` override. |

### Types (@mapmap/maps/avatars)

[`AvatarCar`](#avatarcar), [`AvatarPaint`](#avatarpaint), [`AvatarPitch`](#avatarpitch), [`AvatarSize`](#avatarsize)

#### `AvatarCar`

*Type*

```ts
type AvatarCar = (typeof AVATAR_MANIFEST.cars)[number]["id"];
```

A released vehicle: `"city" | "tourer" | "suv" | "van"`.

#### `AvatarPaint`

*Type*

```ts
type AvatarPaint = (typeof AVATAR_MANIFEST.paints)[number]["id"];
```

A paint: `"glacier-white" | "signal-blue" | "graphite" | ...`.

#### `AvatarPitch`

*Type*

```ts
type AvatarPitch = 0 | 30 | 45 | 58;
```

A pitch bucket, degrees from straight down.

#### `AvatarSize`

*Type*

```ts
type AvatarSize = "s" | "m" | "l";
```

A named size: the disc is 48 (`s`), 64 (`m`) or 80 (`l`) CSS pixels across.

### Constants (@mapmap/maps/avatars)

[`AVATAR_ASSET_BASE_URL`](#avatar_asset_base_url), [`AVATAR_CARS`](#avatar_cars), [`AVATAR_MANIFEST`](#avatar_manifest), [`AVATAR_PAINT_HEX`](#avatar_paint_hex), [`AVATAR_PAINTS`](#avatar_paints), [`AVATAR_PITCHES`](#avatar_pitches), [`AVATAR_SIZES`](#avatar_sizes)

#### `AVATAR_ASSET_BASE_URL`

*Constant*

```ts
const AVATAR_ASSET_BASE_URL: string;
```

Where the artwork is hosted. Public, unmetered, CORS-open and immutable
(the path is versioned). Self-hosters serve the same tree from their own
gateway and pass `assetBaseUrl`.

#### `AVATAR_CARS`

*Constant*

```ts
const AVATAR_CARS: readonly AvatarCar[];
```

Every released car, in catalogue order.

#### `AVATAR_MANIFEST`

*Constant*

```ts
const AVATAR_MANIFEST: {
    readonly version: "v2";
    readonly license: "MapMap Avatar Artwork Licence v1, https://mapmap.ai/docs/licensing#avatars";
    readonly headingStepDeg: 10;
    readonly headingCount: 36;
    readonly pitches: readonly [0, 30, 45, 58];
    readonly atlas: {
        readonly cellPx: 256;
        readonly columns: 6;
        readonly gapPx: 2;
    };
    readonly cars: readonly [
        {
            readonly id: "city";
            readonly name: "City";
            readonly discDiameterM: 4.1628;
            readonly pitches: {
                readonly "0": {
                    readonly anchorPx: readonly [128, 128];
                    readonly discPx: 220.920236;
                    readonly headings: 1;
                    readonly metresPerPx: 0.018843;
                };
                readonly "30": {
                    readonly anchorPx: readonly [128, 138.663];
                    readonly discPx: 243.54684;
                    readonly headings: 36;
                    readonly metresPerPx: 0.017092;
                };
                readonly "45": {
                    readonly anchorPx: readonly [128, 153.804];
                    readonly discPx: 245.768366;
                    readonly headings: 36;
                    readonly metresPerPx: 0.016938;
                };
                readonly "58": {
                    readonly anchorPx: readonly [128, 165.599];
                    readonly discPx: 245.768366;
                    readonly headings: 36;
                    readonly metresPerPx: 0.016938;
                };
            };
        },
        {
            readonly id: "tourer";
            readonly name: "Tourer";
            readonly discDiameterM: 5.1403;
            readonly pitches: {
                readonly "0": {
                    readonly anchorPx: readonly [128, 128];
                    readonly discPx: 210.867666;
                    readonly headings: 1;
                    readonly metresPerPx: 0.024377;
                };
                readonly "30": {
                    readonly anchorPx: readonly [128, 133.791];
                    readonly discPx: 238.185264;
                    readonly headings: 36;
                    readonly metresPerPx: 0.021581;
                };
                readonly "45": {
                    readonly anchorPx: readonly [128, 143.882];
                    readonly discPx: 245.767453;
                    readonly headings: 36;
                    readonly metresPerPx: 0.020915;
                };
                readonly "58": {
                    readonly anchorPx: readonly [128, 151.656];
                    readonly discPx: 245.767453;
                    readonly headings: 36;
                    readonly metresPerPx: 0.020915;
                };
            };
        },
        {
            readonly id: "suv";
            readonly name: "SUV";
            readonly discDiameterM: 4.8917;
            readonly pitches: {
                readonly "0": {
                    readonly anchorPx: readonly [128, 128];
                    readonly discPx: 214.363968;
                    readonly headings: 1;
                    readonly metresPerPx: 0.02282;
                };
                readonly "30": {
                    readonly anchorPx: readonly [128, 138.527];
                    readonly discPx: 237.200935;
                    readonly headings: 36;
                    readonly metresPerPx: 0.020623;
                };
                readonly "45": {
                    readonly anchorPx: readonly [128, 151.373];
                    readonly discPx: 245.772079;
                    readonly headings: 36;
                    readonly metresPerPx: 0.019903;
                };
                readonly "58": {
                    readonly anchorPx: readonly [128, 161.122];
                    readonly discPx: 245.772079;
                    readonly headings: 36;
                    readonly metresPerPx: 0.019903;
                };
            };
        },
        {
            readonly id: "van";
            readonly name: "Van";
            readonly discDiameterM: 4.9951;
            readonly pitches: {
                readonly "0": {
                    readonly anchorPx: readonly [128, 128];
                    readonly discPx: 213.714232;
                    readonly headings: 1;
                    readonly metresPerPx: 0.023373;
                };
                readonly "30": {
                    readonly anchorPx: readonly [128, 148.194];
                    readonly discPx: 236.208446;
                    readonly headings: 36;
                    readonly metresPerPx: 0.021147;
                };
                readonly "45": {
                    readonly anchorPx: readonly [128, 162.414];
                    readonly discPx: 245.769842;
                    readonly headings: 36;
                    readonly metresPerPx: 0.020324;
                };
                readonly "58": {
                    readonly anchorPx: readonly [128, 172.179];
                    readonly discPx: 245.769842;
                    readonly headings: 36;
                    readonly metresPerPx: 0.020324;
                };
            };
        }
   ];
    readonly paints: readonly [
        {
            readonly id: "glacier-white";
            readonly name: "Glacier White";
            readonly hex: "#F4F6FA";
        },
        {
            readonly id: "signal-blue";
            readonly name: "Signal Blue";
            readonly hex: "#3A86FF";
        },
        {
            readonly id: "graphite";
            readonly name: "Graphite";
            readonly hex: "#2A2F3A";
        },
        {
            readonly id: "silver";
            readonly name: "Silver";
            readonly hex: "#A9B3C2";
        },
        {
            readonly id: "racing-red";
            readonly name: "Racing Red";
            readonly hex: "#E5484D";
        },
        {
            readonly id: "sun-yellow";
            readonly name: "Sun Yellow";
            readonly hex: "#F5C518";
        },
        {
            readonly id: "teal";
            readonly name: "Teal";
            readonly hex: "#0F8B8D";
        },
        {
            readonly id: "magenta";
            readonly name: "Magenta";
            readonly hex: "#F845A2";
        }
   ];
};
```

The avatar catalogue and sprite geometry this build of the SDK draws with.

#### `AVATAR_PAINT_HEX`

*Constant*

```ts
const AVATAR_PAINT_HEX: Readonly<Record<AvatarPaint, string>>;
```

Each paint's sRGB hex, for pickers and swatches.

#### `AVATAR_PAINTS`

*Constant*

```ts
const AVATAR_PAINTS: readonly AvatarPaint[];
```

Every paint, in catalogue order.

#### `AVATAR_PITCHES`

*Constant*

```ts
const AVATAR_PITCHES: readonly AvatarPitch[];
```

The pitch buckets, ascending.

#### `AVATAR_SIZES`

*Constant*

```ts
const AVATAR_SIZES: Readonly<Record<AvatarSize, number>>;
```

The disc diameter in CSS pixels for each named size.

## `@mapmap/maps/road-signs`

```ts
import { /* … */ } from "@mapmap/maps/road-signs";
```

### Functions (@mapmap/maps/road-signs)

[`resolveRoadRef`](#resolveroadref), [`resolveSpeedSign`](#resolvespeedsign), [`roadRefLabel`](#roadreflabel), [`roadRefSvg`](#roadrefsvg), [`shieldSpriteName`](#shieldspritename), [`speedSignLabel`](#speedsignlabel), [`speedSignSvg`](#speedsignsvg)

#### `resolveRoadRef`

*Function*

```ts
function resolveRoadRef(input: RoadRefInput): RoadRef[];
```

Resolve the shields for one way (SPEC 4.5): one per concurrent route, at most 3, never invented.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `input` | [`RoadRefInput`](#roadrefinput) | required |  |

Options and types: [`RoadRefInput`](#roadrefinput).

#### `resolveSpeedSign`

*Function*

```ts
function resolveSpeedSign(input: SpeedSignInput): SpeedSign;
```

Resolve the speed sign for one road edge (SPEC 4.4). Never guesses a unit.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `input` | [`SpeedSignInput`](#speedsigninput) | required |  |

Options and types: [`SpeedSignInput`](#speedsigninput).

#### `roadRefLabel`

*Function*

```ts
function roadRefLabel(ref: Pick<RoadRef, "text" | "tier">, locale?: string): string;
```

"Motorway M25", "A road A259", "Interstate 95", "US Route 101", "Route nationale 7"; "Autobahn A 9" in German.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `ref` | `Pick<RoadRef, "text" \| "tier">` | required |  |
| `locale?` | `string` | not stated |  |

Options and types: [`RoadRef`](#roadref).

#### `roadRefSvg`

*Function*

```ts
function roadRefSvg(ref: RoadRef, height: number, opts?: ComposeOptions): string;
```

The shield as an SVG string `height` px high, stretched to its text ("" for text-only refs).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `ref` | [`RoadRef`](#roadref) | required |  |
| `height` | `number` | required |  |
| `opts?` | [`ComposeOptions`](#composeoptions) | not stated |  |

Options and types: [`RoadRef`](#roadref), [`ComposeOptions`](#composeoptions).

#### `shieldSpriteName`

*Function*

```ts
function shieldSpriteName(template: string): string;
```

Sprite image id of a shield blank in map-assets/sprites (`icon-text-fit` metadata merged in).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `template` | `string` | required |  |

#### `speedSignLabel`

*Function*

```ts
function speedSignLabel(sign: Pick<SpeedSign, "kind" | "value" | "unit">, locale?: string): string;
```

"Speed limit 30 miles per hour", "National speed limit, 60 miles per hour", "No speed limit", "20 miles per hour zone".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `sign` | `Pick<SpeedSign, "kind" \| "value" \| "unit">` | required |  |
| `locale?` | `string` | not stated |  |

Options and types: [`SpeedSign`](#speedsign).

#### `speedSignSvg`

*Function*

```ts
function speedSignSvg(sign: SpeedSign, size: number, opts?: ComposeOptions): string;
```

The sign as an SVG string sized `size` px high ("" when nothing renders). 24 drops the unit
caption and legend words; `surface: "dark"` adds the white halo; `fitMode` defaults to
"official" (the drawings' own numeral widths).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `sign` | [`SpeedSign`](#speedsign) | required |  |
| `size` | `number` | required |  |
| `opts?` | [`ComposeOptions`](#composeoptions) | not stated |  |

Options and types: [`SpeedSign`](#speedsign), [`ComposeOptions`](#composeoptions).

### Interfaces (@mapmap/maps/road-signs)

[`ComposeOptions`](#composeoptions), [`CountrySignProfile`](#countrysignprofile), [`FamilySpacing`](#familyspacing), [`GenericCountryEntry`](#genericcountryentry), [`GlyphData`](#glyphdata), [`Plausible`](#plausible), [`ResolvedProfile`](#resolvedprofile), [`RoadRef`](#roadref), [`RoadRefInput`](#roadrefinput), [`ShieldTier`](#shieldtier), [`SignProfileTable`](#signprofiletable), [`SignTables`](#signtables), [`SpeedSign`](#speedsign), [`SpeedSignInput`](#speedsigninput)

#### `ComposeOptions`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface ComposeOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `surface?` | [`Surface`](#surface) | not stated |  |
| `fitMode?` | [`FitMode`](#fitmode) | not stated | Numeral width rule; default "official". |

#### `CountrySignProfile`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface CountrySignProfile
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `unit` | [`Unit`](#unit) | required |  |
| `speedSignForm` | [`SpeedSignForm`](#speedsignform) | required |  |
| `nationalLimitSign` | `"gb_nsl" \| "none"` | required |  |
| `noLimitSign` | `"de_end" \| "none"` | required |  |
| `zoneForm` | [`ZoneForm`](#zoneform) | required |  |
| `driveOnLeft` | `boolean` | required |  |
| `implicit` | `Partial<Record<ImplicitKey, ImplicitValue>>` | required | Implicit values in the DISPLAY unit; a missing key means "no statutory default known" -> unknown. |
| `subdivisions?` | `Record<string, Partial<CountrySignProfile>>` | not stated | ISO 3166-2 overrides keyed by the Valhalla state_code suffix (WLS, QC, NSW, VLG, CA...). |
| `plausible` | [`Plausible`](#plausible) | required | Sanity bounds in the display unit for engine-only values; outside or off-step -> unknown. |
| `shields` | [`ShieldTier[]`](#shieldtier) | required |  |
| `speedSignTemplate?` | [`SpeedTemplateId`](#speedtemplateid) | not stated | Template drawn for a numeric limit when it differs from the form's own (ES ring ratio, SE yellow ground). |
| `numeralFamily?` | [`NumeralFamily`](#numeralfamily) | not stated | Numeral family for the speed sign when it differs from the template's default. |

#### `FamilySpacing`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface FamilySpacing
```

A family's word space and pair kerning (numerals|alphabet/<family>/_spacing.json), on the
100-unit cap grid. Kerning keys are two characters ("43": a 4 followed by a 3) or three with a
space between ("A 6": the A, a space, the 6; the value is added to the space); the value is
added to the left glyph's advance (or to the space).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `space?` | `number` | Word space advance; the composer's default is 28 units | Word space advance; the composer's default is 28 units. |
| `kern?` | `Record<string, number>` | not stated |  |

#### `GenericCountryEntry`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface GenericCountryEntry
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `implicit?` | `Partial<Record<ImplicitKey, ImplicitValue>>` | not stated | Implicit tokens verified for this country (e.g. RO:urban, RU:urban). |

#### `GlyphData`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface GlyphData
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `d` | `string` | required | Single path, cap height 100, baseline at y = 100. |
| `advance` | `number` | required | Advance width in the same units. |

#### `Plausible`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface Plausible
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `min` | `number` | required |  |
| `max` | `number` | required |  |
| `step` | `number` | required |  |

#### `ResolvedProfile`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface ResolvedProfile extends GenericSignProfile
```

A country profile after alias resolution and subdivision merge.

Extends [`GenericSignProfile`](#genericsignprofile).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `country` | `string` | required |  |
| `subdivision?` | `string` | not stated |  |
| `driveOnLeft?` | `boolean` | not stated |  |
| `generic` | `boolean` | required | True when the profile came from the generic Vienna fallback. |

#### `RoadRef`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface RoadRef
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `text` | `string` | required | Text shown on the shield (display prefix stripped where the sign does). |
| `tier` | `string` | required | Tier id, "generic" for a ref with no national tier, "text" for refs shown as text only. |
| `country` | `string` | required | ISO 3166-1 alpha-2, "" when unknown. |
| `a11yLabel` | `string` | required |  |
| `template?` | `string` | not stated | Shield template to draw; absent for text-only refs. |

#### `RoadRefInput`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface RoadRefInput
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `ref?` | `string` | not stated | Way-level ref, `;` separated for concurrencies. |
| `network?` | `string` | not stated | Raw OSM route network when there is a single ref. |
| `roadClass?` | `string` | not stated | Road class (motorway, trunk, primary...). |
| `country?` | `string` | not stated |  |
| `subdivision?` | `string` | not stated |  |
| `tileNetwork?` | `string` | not stated | OpenMapTiles transportation_name.network of the way. |
| `routeNetworks?` | `string[]` | not stated | route_1..route_6 network values. |
| `routeRefs?` | `string[]` | not stated | route_1..route_6 ref values; when present they take precedence over `ref`. |
| `locale?` | `string` | not stated |  |

#### `ShieldTier`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface ShieldTier
```

One road-number tier of a country (SPEC 1.3 and 4.5).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | Stable tier id, also the key of the a11y label table (e.g. "gb-motorway", "us-interstate"). |
| `template` | `string` | required | Shield template id under templates/shield/ (2-digit variant for curved shapes). |
| `template3?` | `string` | not stated | Three-digit variant for curved shapes (stretchOnly), chosen when the shield text has 3 or more characters. |
| `networks?` | `string[]` | not stated | Raw OSM route `network` values that select this tier. `*` matches one colon-free segment. |
| `tileNetworks?` | `string[]` | not stated | OpenMapTiles `transportation_name.network` values (both `gb-*` and `omt-gb-*` spellings). |
| `refPattern?` | `string` | not stated | Ref-prefix fallback (regular expression source) applied to the way-level ref. |
| `roadClasses?` | `string[]` | not stated | The ref-prefix fallback applies only on these road classes (e.g. GB primary route = trunk). |
| `subdivisions?` | `string[]` | not stated | Tile and ref-prefix matching apply only in these subdivisions (Valhalla state_code suffix). |
| `stripPrefix?` | `string` | not stated | Prefix removed from the shield text (regular expression source), e.g. "^I[- ]?" for "I 95". |
| `minzoom` | `number` | required | Minimum map zoom for the shield (SPEC 3.5). |

#### `SignProfileTable`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface SignProfileTable
```

The canonical profiles.json document.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `version` | `1` | required |  |
| `countries` | `Record<string, CountrySignProfile>` | required |  |
| `aliases` | `Record<string, string>` | required | Dependencies and territories that use another country's signs (IM, JE, GG -> GB; PR, GU, VI -> US). |
| `generic` | [`GenericSignProfile`](#genericsignprofile) | required |  |
| `genericCountries` | `Record<string, GenericCountryEntry>` | required | ISO 3166-1 alpha-2 codes known to post limits in km/h; anything absent is unknown and renders nothing. |

#### `SignTables`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface SignTables
```

Tables injected into the composer (lane 3 embeds them in the generated modules).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `numerals` | `Record<string, GlyphFamily>` | required | numerals/<family>/ glyphs keyed by family then glyph name ("0".."9", "km", "per-h", "SPEED"...). |
| `alphabets?` | `Record<string, GlyphFamily>` | not stated | alphabet/<family>/ glyphs keyed by family then character. |
| `templates` | `Record<string, string>` | required | Template SVG strings keyed by template id (speed and shield ids are disjoint). |
| `spacing?` | `Record<string, FamilySpacing>` | not stated | Word space and pair kerning per family (absent: space 28, no kerning). |

#### `SpeedSign`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface SpeedSign
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `kind` | [`SpeedSignKind`](#speedsignkind) | required |  |
| `value?` | `number` | not stated | Display value in `unit` (kept for national and zone for the label). |
| `unit` | [`Unit`](#unit) | required |  |
| `form` | [`SpeedSignForm`](#speedsignform) | required |  |
| `a11yLabel` | `string` | required |  |
| `template?` | [`SpeedTemplateId`](#speedtemplateid) | not stated | Template to draw; absent when nothing renders (unknown, walk, none outside DE, unknown country). |
| `family?` | [`NumeralFamily`](#numeralfamily) | not stated | Numeral family for the number slot (profile override of the template default). |
| `country?` | `string` | not stated | ISO 3166-1 alpha-2 of the profile that resolved the sign. |

#### `SpeedSignInput`

*Interface* · also exported from `@mapmap/maps/asset-markers`

```ts
interface SpeedSignInput
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `maxspeed?` | `string` | not stated | Raw OSM `maxspeed` when the API carries it. |
| `maxspeedType?` | `string` | not stated | Raw OSM `maxspeed:type` when carried. |
| `speedLimitKph?` | `number` | not stated | Engine value: 0 unknown, 255 unlimited, 5 walk. |
| `country?` | `string` | not stated | ISO 3166-1 alpha-2 of the edge. |
| `subdivision?` | `string` | not stated | Valhalla state_code suffix (WLS, QC, TX...). |
| `locale?` | `string` | en-GB | BCP 47 locale for the a11y label (default en-GB). |

### Types (@mapmap/maps/road-signs)

[`FitMode`](#fitmode), [`GenericSignProfile`](#genericsignprofile), [`GlyphFamily`](#glyphfamily), [`ImplicitKey`](#implicitkey), [`ImplicitValue`](#implicitvalue), [`NumeralFamily`](#numeralfamily), [`ShieldTemplateId`](#shieldtemplateid), [`SpeedSignForm`](#speedsignform), [`SpeedSignKind`](#speedsignkind), [`SpeedSignTemplateId`](#speedsigntemplateid), [`SpeedTemplateId`](#speedtemplateid), [`Surface`](#surface), [`Unit`](#unit), [`ZoneForm`](#zoneform)

#### `FitMode`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type FitMode = "official" | "figma60";
```

How wide the numerals may run inside the sign (lead decision 1, 30 Sep 2026; a decision for Matt).
- "official" (default): the cap height and width of the official drawing wherever one exists
  (GB P 670, IE TSM ch 5, ES R-301, US R2-1, AU R4-1); where no dimensioned drawing exists
  (Vienna working values, FR, JP, NZ, CA) the run is capped at 0.62 D (SPEC 3.3).
- "figma60": the Figma rule of 30 Sep 2026, numerals within 0.60 of the inner disc or box.

#### `GenericSignProfile`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type GenericSignProfile = Omit<CountrySignProfile, "driveOnLeft" | "subdivisions">;
```

Generic Vienna profile used for km/h countries outside the 20-country table.

#### `GlyphFamily`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type GlyphFamily = Record<string, GlyphData>;
```

#### `ImplicitKey`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type ImplicitKey = "urban" | "rural" | "trunk" | "motorway" | "living_street" | "bicycle_road" | "nsl_restricted" | "nsl_single" | "nsl_dual" | "zone20" | "zone30" | "zone40";
```

#### `ImplicitValue`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type ImplicitValue = number | "none" | "walk";
```

#### `NumeralFamily`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type NumeralFamily = "transport" | "motorway" | "din" | "fhwa" | "fhwa-c" | "fhwa-d" | "l1" | "fr-l1" | "jp" | "ccrige" | "pl" | "no-362" | "alfabeto-stretto" | "se-c31" | "dk";
```

#### `ShieldTemplateId`

*Type*

```ts
type ShieldTemplateId = "au-a" | "au-b" | "au-m" | "au-nh-3" | "au-nh" | "au-nr-3" | "au-nr" | "au-sr-3" | "au-sr" | "ca-on" | "ca-qc-a" | "ca-tch" | "de-a" | "de-b" | "dk-e" | "dk-primary" | "dk-secondary" | "e-road" | "es-a" | "es-n" | "fr-a" | "fr-d" | "fr-e41" | "fr-e42" | "fr-e43" | "fr-e47" | "fr-n" | "gb-motorway" | "gb-nonprimary" | "gb-primary" | "generic" | "ie-m" | "ie-n" | "ie-r" | "it-a-3" | "it-a" | "it-s" | "it-ss" | "jp-e" | "jp-national-3" | "jp-national" | "jp-pref-3" | "jp-pref" | "nl-a" | "nl-n" | "no-e" | "no-fv" | "no-rv" | "nz-sh-3" | "nz-sh" | "pl-a" | "pl-dk" | "pl-dw" | "pl-s" | "pt-a" | "pt-ip" | "pt-other" | "se-e" | "se-rv" | "us-interstate-2" | "us-interstate-3" | "us-route-2" | "us-route-3" | "us-state-3" | "us-state";
```

Shield template ids (templates/shield/); curved shapes have a "-3" three-digit variant.

#### `SpeedSignForm`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type SpeedSignForm = "gb" | "ie_kmh" | "vienna" | "mutcd" | "ca_maximum" | "ca_qc" | "au" | "jp";
```

How a numeric limit is drawn (SPEC 4.3).

#### `SpeedSignKind`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type SpeedSignKind = "numeric" | "national" | "none" | "walk" | "zone" | "unknown";
```

#### `SpeedSignTemplateId`

*Type*

```ts
type SpeedSignTemplateId = SpeedTemplateId;
```

Speed-sign template ids (templates/speed/).

#### `SpeedTemplateId`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type SpeedTemplateId = SpeedSignForm | "es" | "vienna_se" | "vienna_nz" | "vienna_pl" | "vienna_fr" | "vienna_no" | "de" | "at" | "vienna_dk" | "vienna_pt" | "it" | "gb_nsl" | "de_end" | "gb_zone" | "vienna_zone" | "mutcd_zone";
```

Speed template ids under templates/speed/ (the forms plus the special glyphs and country variants).

#### `Surface`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type Surface = "light" | "dark";
```

#### `Unit`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type Unit = "mph" | "kmh";
```

MapMap road signs: speed-limit signs and road-number shields drawn from each country's official
dimensions with MapMap's own numerals (no font file), plus the resolvers that turn route data
into a sign. Published as `@mapmap/maps/road-signs`; the website keeps an identical copy.

  const sign = resolveSpeedSign({ speedLimitKph: 48, country: "GB" });  // numeric 30 mph
  const svg = speedSignSvg(sign, 48, { surface: "dark" });             // "" when nothing renders
  const [ref] = resolveRoadRef({ ref: "M25", country: "GB" });
  const badge = roadRefSvg(ref, 24);

Rules: the unit comes from the road's country, never the device; an unknown country, an unknown
limit and "walk" render nothing; numerals are paths, so signs never follow font scale.
Where a sign is shown, ship `signAttributions` for GB, IE, AU, NZ and JP (SPEC 2.1).

#### `ZoneForm`

*Type* · also exported from `@mapmap/maps/asset-markers`

```ts
type ZoneForm = "gb_zone" | "vienna_zone" | "mutcd_zone" | "none";
```

### Constants (@mapmap/maps/road-signs)

[`shieldTemplates`](#shieldtemplates), [`signAlphabets`](#signalphabets), [`signAttributions`](#signattributions), [`signNumerals`](#signnumerals), [`signProfiles`](#signprofiles), [`signSpacing`](#signspacing), [`speedSignTemplates`](#speedsigntemplates)

#### `shieldTemplates`

*Constant*

```ts
const shieldTemplates: Record<ShieldTemplateId, string>;
```

Shield templates with stretch metadata (100 units high at their two-digit width).

#### `signAlphabets`

*Constant*

```ts
const signAlphabets: Record<string, GlyphFamily>;
```

MapMap shield alphabets (alphabet/).

#### `signAttributions`

*Constant*

```ts
const signAttributions: Readonly<Record<"GB" | "IE" | "AU" | "NZ" | "JP", string>>;
```

Attribution to ship wherever signs of these countries are shown (app licences screen, SDK
NOTICE; SPEC 2.1). Other countries need none.

#### `signNumerals`

*Constant*

```ts
const signNumerals: Record<string, GlyphFamily>;
```

MapMap numeral families (numerals/): single-path glyphs on a 100-unit cap grid.

#### `signProfiles`

*Constant*

```ts
const signProfiles: SignProfileTable;
```

The canonical country table (map-assets/signs/profiles.json, SPEC 4.3).

#### `signSpacing`

*Constant*

```ts
const signSpacing: Record<string, FamilySpacing>;
```

Word space and pair kerning per family (numerals|alphabet/<family>/_spacing.json).

#### `speedSignTemplates`

*Constant*

```ts
const speedSignTemplates: Record<SpeedTemplateId, string>;
```

Speed-sign templates with their named slots (100 units high).

## `@mapmap/maps/asset-markers`

```ts
import { /* … */ } from "@mapmap/maps/asset-markers";
```

Also re-exports, documented above: [`ComposeOptions`](#composeoptions), [`CountrySignProfile`](#countrysignprofile), [`FamilySpacing`](#familyspacing), [`FitMode`](#fitmode), [`GenericCountryEntry`](#genericcountryentry), [`GenericSignProfile`](#genericsignprofile), [`GlyphData`](#glyphdata), [`GlyphFamily`](#glyphfamily), [`ImplicitKey`](#implicitkey), [`ImplicitValue`](#implicitvalue), [`NumeralFamily`](#numeralfamily), [`Plausible`](#plausible), [`ResolvedProfile`](#resolvedprofile), [`RoadRef`](#roadref), [`RoadRefInput`](#roadrefinput), [`ShieldTier`](#shieldtier), [`SignProfileTable`](#signprofiletable), [`SignTables`](#signtables), [`SpeedSign`](#speedsign), [`SpeedSignForm`](#speedsignform), [`SpeedSignInput`](#speedsigninput), [`SpeedSignKind`](#speedsignkind), [`SpeedTemplateId`](#speedtemplateid), [`Surface`](#surface), [`Unit`](#unit), [`ZoneForm`](#zoneform).

### Functions (@mapmap/maps/asset-markers)

[`assetGroupOf`](#assetgroupof), [`assetIconFor`](#asseticonfor), [`beamImageId`](#beamimageid), [`borderPx`](#borderpx), [`faceShape`](#faceshape), [`faceSvg`](#facesvg), [`facingSvg`](#facingsvg), [`layoutFace`](#layoutface), [`markerImageId`](#markerimageid), [`puckBox`](#puckbox), [`puckGroup`](#puckgroup), [`puckImageId`](#puckimageid), [`puckSvg`](#pucksvg), [`resolveFace`](#resolveface), [`roadsideMarkerFor`](#roadsidemarkerfor), [`signMarker`](#signmarker)

#### `assetGroupOf`

*Function*

```ts
function assetGroupOf(cat: string): AssetGroupId;
```

The legend group of a category.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `cat` | `string` | required |  |

#### `assetIconFor`

*Function*

```ts
function assetIconFor(cat: string, subtype: string | undefined): AssetGlyphId | "face" | "dot";
```

The glyph, "face" or "dot" for a (category, subtype); an unknown category is the generic puck.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `cat` | `string` | required |  |
| `subtype` | `string \| undefined` | required |  |

#### `beamImageId`

*Function*

```ts
function beamImageId(surface: MarkerSurface): string;
```

Sprite and image-registry id of the facing beam.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `surface` | [`MarkerSurface`](#markersurface) | required |  |

Options and types: [`MarkerSurface`](#markersurface).

#### `borderPx`

*Function*

```ts
function borderPx(tier: MarkerTier, surface: MarkerSurface): number;
```

The die-cut border around a face, in px: Day a white border with an ink keyline, Night the 0.9 white rim.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `tier` | [`MarkerTier`](#markertier) | required |  |
| `surface` | [`MarkerSurface`](#markersurface) | required |  |

Options and types: [`MarkerTier`](#markertier), [`MarkerSurface`](#markersurface).

#### `faceShape`

*Function*

```ts
function faceShape(id: string): FaceShape | undefined;
```

The shape of a face id.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |

#### `faceSvg`

*Function*

```ts
function faceSvg(ref: FaceRef, opts?: {
    size?: number;
    surface?: MarkerSurface;
    tier?: MarkerTier;
    label?: string;
}): string;
```

A face alone (the detail card header at 48 px by default), with its border. "" when it cannot be drawn.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `ref` | [`FaceRef`](#faceref) | required |  |
| `opts?` | `{ size?: number; surface?: MarkerSurface; tier?: MarkerTier; label?: string; }` | not stated |  |

Options and types: [`FaceRef`](#faceref), [`MarkerSurface`](#markersurface), [`MarkerTier`](#markertier).

#### `facingSvg`

*Function*

```ts
function facingSvg(surface?: MarkerSurface): string;
```

The facing beam: a soft wedge from the post's foot towards where the face looks, drawn pointing up
(north) for `icon-rotate` by the facing bearing with `icon-rotation-alignment: map`, anchored at its
centre on the measured point, so it always springs from the post's foot. The same ink wedge in both
themes. Draw it only where facing is known.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `surface?` | [`MarkerSurface`](#markersurface) | not stated |  |

Options and types: [`MarkerSurface`](#markersurface).

#### `layoutFace`

*Function*

```ts
function layoutFace(ref: FaceRef, size: number, surface?: MarkerSurface): FaceLayout | undefined;
```

Lay out one face (template units) at a nominal size: what was drawn, for tests and legends.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `ref` | [`FaceRef`](#faceref) | required |  |
| `size` | `number` | required |  |
| `surface?` | [`MarkerSurface`](#markersurface) | not stated |  |

Options and types: [`FaceRef`](#faceref), [`MarkerSurface`](#markersurface).

#### `markerImageId`

*Function*

```ts
function markerImageId(refs: FaceRef[], tier: MarkerTier, surface: MarkerSurface): string;
```

A stable image id for a marker: one per faces, values and words, tier and surface.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `refs` | [`FaceRef[]`](#faceref) | required |  |
| `tier` | [`MarkerTier`](#markertier) | required |  |
| `surface` | [`MarkerSurface`](#markersurface) | required |  |

Options and types: [`FaceRef`](#faceref), [`MarkerTier`](#markertier), [`MarkerSurface`](#markersurface).

#### `puckBox`

*Function*

```ts
function puckBox(tier: PuckTier): number;
```

Image box of a puck, px (square; the disc's centre is the image centre, the anchor).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `tier` | [`PuckTier`](#pucktier) | required |  |

Options and types: [`PuckTier`](#pucktier).

#### `puckGroup`

*Function*

```ts
function puckGroup(id: AssetGlyphId, group?: string): AssetGroupId;
```

The group a glyph's puck is drawn in: `group` when the sprite has that variant, else the glyph's dominant group.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | [`AssetGlyphId`](#assetglyphid) | required |  |
| `group?` | `string` | not stated |  |

Options and types: [`AssetGlyphId`](#assetglyphid).

#### `puckImageId`

*Function*

```ts
function puckImageId(id: string, surface: MarkerSurface, group?: string): string;
```

Sprite and image-registry id of a puck: `rs-puck-<glyph>-<group>-day|night` (a group the glyph has no variant for: its dominant group).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |
| `surface` | [`MarkerSurface`](#markersurface) | required |  |
| `group?` | `string` | not stated |  |

Options and types: [`MarkerSurface`](#markersurface).

#### `puckSvg`

*Function*

```ts
function puckSvg(id: AssetGlyphId, surface?: MarkerSurface, group?: AssetGroupId): string;
```

A puck image (SVG) for a glyph: its tier's size and weight, in its legend group's colour (default the dominant group).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | [`AssetGlyphId`](#assetglyphid) | required |  |
| `surface?` | [`MarkerSurface`](#markersurface) | not stated |  |
| `group?` | [`AssetGroupId`](#assetgroupid) | not stated |  |

Options and types: [`AssetGlyphId`](#assetglyphid), [`MarkerSurface`](#markersurface), [`AssetGroupId`](#assetgroupid).

#### `resolveFace`

*Function*

```ts
function resolveFace(cat: string, subtype: string | undefined, value: string | number | null | undefined): ResolvedFace | undefined;
```

The face for a finding (category, subtype, read value); undefined when the category is not a sign.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `cat` | `string` | required |  |
| `subtype` | `string \| undefined` | required |  |
| `value` | `string \| number \| null \| undefined` | required |  |

#### `roadsideMarkerFor`

*Function*

```ts
function roadsideMarkerFor(cat: string, subtype: string | undefined, value: string | number | null | undefined): RoadsideMarker;
```

What to draw for a finding: a sign face (sort key 1 regulatory, 2 warning, 3 information), an
asset puck (its tier's sort key and minimum zoom, and the legend group whose colour it wears), or a dot. A finding the face map says is not a
face (a marking misfiled as a sign, a fingerpost) is drawn as the glyph the rule names.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `cat` | `string` | required |  |
| `subtype` | `string \| undefined` | required |  |
| `value` | `string \| number \| null \| undefined` | required |  |

**Example**

```ts
import { roadsideMarkerFor, signMarker, signShortCredit } from "@mapmap/maps/asset-markers";

// A finding from the asset register: category, subtype (if any) and the value read.
const marker = roadsideMarkerFor("speed_limit", undefined, "30");
if (marker.kind === "face") {
  console.log(marker.face);
} else if (marker.kind === "puck") {
  console.log(marker.name, marker.group, marker.minzoom);
}
console.log(signMarker([{ face: "gb-670", value: "30" }])?.width, signShortCredit);
```

#### `signMarker`

*Function*

```ts
function signMarker(refs: FaceRef[], opts?: {
    tier?: MarkerTier;
    surface?: MarkerSurface;
    lift?: number;
}): SignMarker | undefined;
```

A sign marker: one face or a stack on one post (top first). See SignMarker for the anchor.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `refs` | [`FaceRef[]`](#faceref) | required |  |
| `opts?` | `{ tier?: MarkerTier; surface?: MarkerSurface; lift?: number; }` | not stated |  |

Options and types: [`FaceRef`](#faceref), [`MarkerTier`](#markertier), [`MarkerSurface`](#markersurface).

**Example**

```ts
import { signAttribution, signMarker } from "@mapmap/maps/asset-markers";

// A 30 limit (TSRGD diagram 670) at the full tier, drawn on its post.
const sign = signMarker([{ face: "gb-670", value: "30" }], { tier: "full" });
if (sign) {
  const el = document.createElement("div");
  el.innerHTML = sign.svg; // anchor: sign.anchor ("bottom" on a post)
  el.title = signAttribution; // show the OGL attribution wherever a face is shown
  document.body.append(el);
}
```

### Interfaces (@mapmap/maps/asset-markers)

[`FaceLayout`](#facelayout), [`FaceRef`](#faceref), [`FaceRule`](#facerule), [`FaceTables`](#facetables), [`ResolvedFace`](#resolvedface), [`SignMarker`](#signmarker-1)

#### `FaceLayout`

*Interface*

```ts
interface FaceLayout
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `W` | `number` | required | Width and height in template units (height 100). |
| `H` | `number` | required |  |
| `body` | `string` | required | Inner markup in template units (no root). |
| `outline` | `string` | required | The outline element alone (no paint), for the die-cut border. |
| `scale` | `number` | required | px per template unit at the requested size. |
| `drawn` | `Array<{ slot: string; text: string; capPx: number; }>` | required | What was drawn, for tests: words and values, and the rendered cap height of each run in px. |

#### `FaceRef`

*Interface*

```ts
interface FaceRef
```

One face: a template id (`gb-616`) or a state (`unresolved-roundel`, `rear-rect`), with its read value or words.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `face` | `string` | required |  |
| `value?` | `string` | not stated | A limit's value as read ("30", "2.6m", "7.5T", `6'6"`). |
| `text?` | `string` | not stated | Words for a plate that carries them (a street name). |

#### `FaceRule`

*Interface*

```ts
interface FaceRule
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `cat` | `string` | required |  |
| `subtype` | `string` | required | A taxonomy subtype, or "*" for any. |
| `value?` | `string` | not stated | Case-insensitive pattern on the read value; its named group `v` fills `value` when `arg` is "$v". |
| `face` | `string` | required | A face id, a state, or "none" (not a sign face: draw the asset glyph named in `glyph`). |
| `glyph?` | `string` | not stated | With face "none": the asset glyph (icons/assets) or "dot". |
| `arg?` | `string` | not stated | "$v" (the pattern's named group `(?<v>...)`: the value is never positional, so a guard's lookahead can never supply it), a literal value, or "text" (the read value as words on a plate). |
| `priority` | `1 \| 2 \| 3` | required | 1 regulatory, 2 warning, 3 information. |
| `note?` | `string` | not stated | Why the rule was added (its provenance); the resolver never reads it. |

#### `FaceTables`

*Interface*

```ts
interface FaceTables
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `templates` | `Record<string, string>` | required | templates/face/*.svg keyed by id. |
| `numerals` | [`GlyphFamily`](#glyphfamily) | required | numerals/transport (digits, k, m, h, /). |
| `letters` | [`GlyphFamily`](#glyphfamily) | required | faces/alphabet/transport (capitals). |
| `nightWhite` | `string` | required | The Night white for white grounds (templates/palette.json night "sign/white"). |
| `space?` | `number` | not stated | Word space of the transport family. |

#### `ResolvedFace`

*Interface*

```ts
interface ResolvedFace extends FaceRef
```

Extends [`FaceRef`](#faceref).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `priority` | `1 \| 2 \| 3` | required |  |
| `glyph?` | `string` | not stated | With face "none": the asset glyph to draw instead. |
| `rule` | `number` | required | The rule's index in the map (for tests and debugging). |

#### `SignMarker`

*Interface*

```ts
interface SignMarker
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `svg` | `string` | required |  |
| `width` | `number` | required |  |
| `height` | `number` | required |  |
| `anchor` | `"bottom" \| "center"` | required | Where the measured point is: the bottom centre (on a post) or the centre (small tier). |

### Types (@mapmap/maps/asset-markers)

[`AssetGlyphId`](#assetglyphid), [`AssetGroupId`](#assetgroupid), [`FaceShape`](#faceshape-1), [`FaceTemplateId`](#facetemplateid), [`MarkerSurface`](#markersurface), [`MarkerTier`](#markertier), [`PlateShape`](#plateshape), [`PuckSurface`](#pucksurface), [`PuckTier`](#pucktier), [`RoadsideMarker`](#roadsidemarker)

#### `AssetGlyphId`

*Type*

```ts
type AssetGlyphId = "accessibility" | "barrier" | "bench" | "bicycle" | "billboard" | "board" | "bollard" | "bus_stop" | "cabinet" | "cafe" | "cctv" | "communal_bin" | "cone" | "construction" | "crane" | "crossing" | "drinking_fountain" | "ev_charger" | "fence" | "fingerpost" | "gate" | "grit_bin" | "gully" | "hedge" | "heritage_light" | "hydrant" | "info" | "level_crossing" | "litter_bin" | "manhole" | "mast" | "no_waiting" | "other" | "palm" | "parking" | "planter" | "postbox" | "railings" | "road_marking" | "scaffolding" | "shelter" | "skip" | "stall" | "street_light" | "telegraph_pole" | "traffic_calming" | "traffic_signal" | "tree" | "wall";
```

An asset glyph id (map-assets/icons/assets/).

#### `AssetGroupId`

*Type*

```ts
type AssetGroupId = "carriageway" | "furniture" | "green" | "waste" | "commercial" | "works" | "heritage";
```

A Roadside legend group id.

#### `FaceShape`

*Type*

```ts
type FaceShape = "roundel" | "triangle" | "triangle-down" | "octagon" | "rect" | "plate";
```

A face shape; unread and rear plates are drawn in the shape of the sign they stand for.

#### `FaceTemplateId`

*Type*

```ts
type FaceTemplateId = "gb-504-1" | "gb-505-1" | "gb-510" | "gb-512-left" | "gb-512-right" | "gb-543" | "gb-544-1" | "gb-545" | "gb-557-1" | "gb-562" | "gb-601-1" | "gb-602" | "gb-606-ahead" | "gb-606-left" | "gb-606-right" | "gb-609-left" | "gb-609-right" | "gb-610-left" | "gb-610-right" | "gb-611-1" | "gb-612" | "gb-613" | "gb-614" | "gb-615" | "gb-616" | "gb-622-1a" | "gb-629-2a" | "gb-629a" | "gb-642" | "gb-652" | "gb-670" | "gb-671" | "gb-674" | "gb-801" | "gb-811" | "gb-816" | "gb-950" | "gb-956" | "gb-957" | "gb-cycle-route" | "gb-no-waiting" | "gb-parking-plate" | "gb-street-plate";
```

A face template id (templates/face/).

#### `MarkerSurface`

*Type*

```ts
type MarkerSurface = "light" | "dark";
```

The ground a marker is drawn on: "light" (Day map) or "dark" (Night map).

#### `MarkerTier`

*Type*

```ts
type MarkerTier = "small" | "full" | "card";
```

Zoom tier: "small" (z 16 to 17.5, shape and colour only), "full" (z 17.5 and above, on its post), "card" (the detail card header).

#### `PlateShape`

*Type*

```ts
type PlateShape = "roundel" | "triangle" | "rect";
```

Shapes an unread or rear plate can take.

#### `PuckSurface`

*Type*

```ts
type PuckSurface = "light" | "dark";
```

#### `PuckTier`

*Type*

```ts
type PuckTier = "safety" | "furniture" | "ground";
```

#### `RoadsideMarker`

*Type*

```ts
type RoadsideMarker = {
    kind: "face";
    face: ResolvedFace;
} | {
    kind: "puck";
    glyph: AssetGlyphId;
    group: AssetGroupId;
    tier: PuckTier;
    name: string;
    sort: number;
    minzoom: number;
} | {
    kind: "dot";
};
```

What to draw for a finding.

### Constants (@mapmap/maps/asset-markers)

[`assetFallbackGroup`](#assetfallbackgroup), [`assetGlyphGroups`](#assetglyphgroups), [`assetGlyphInfo`](#assetglyphinfo), [`assetGlyphs`](#assetglyphs), [`assetGroupColours`](#assetgroupcolours), [`assetGroups`](#assetgroups), [`assetMap`](#assetmap), [`assetTiers`](#assettiers), [`BEAM_PX`](#beam_px), [`faceRules`](#facerules), [`faceTemplates`](#facetemplates), [`MARKER_COLOURS`](#marker_colours), [`MIN_CAP_PX`](#min_cap_px), [`PLATE_MAX_WIDTH`](#plate_max_width), [`PUCK_COLOURS`](#puck_colours), [`PUCK_STYLES`](#puck_styles), [`SHAPE_SCALE`](#shape_scale), [`signAttribution`](#signattribution), [`signShortCredit`](#signshortcredit), [`STEM`](#stem), [`TIER_PX`](#tier_px)

#### `assetFallbackGroup`

*Constant*

```ts
const assetFallbackGroup: AssetGroupId;
```

The group a category outside every group falls into.

#### `assetGlyphGroups`

*Constant*

```ts
const assetGlyphGroups: Readonly<Record<AssetGlyphId, readonly AssetGroupId[]>>;
```

Each glyph's legend groups, the dominant one first; the sprite holds one puck per (glyph, group).

#### `assetGlyphInfo`

*Constant*

```ts
const assetGlyphInfo: Readonly<Record<AssetGlyphId, {
    name: string;
    tier: PuckTier;
}>>;
```

Each glyph's name (legend, card) and tier.

#### `assetGlyphs`

*Constant*

```ts
const assetGlyphs: Readonly<Record<AssetGlyphId, string>>;
```

Asset glyphs: 24-unit SVG, currentColor, secondary `class="mm2"` at 0.35 opacity.

#### `assetGroupColours`

*Constant*

```ts
const assetGroupColours: Readonly<Record<MarkerSurface, Readonly<Record<AssetGroupId, string>>>>;
```

The design system's asset-group palette (`--asset-<group>`, website/lib/asset-group-palette.ts) by map ground.

#### `assetGroups`

*Constant*

```ts
const assetGroups: readonly {
    id: AssetGroupId;
    label: string;
    cats: readonly string[];
}[];
```

The Roadside legend, in order (a payload stores a group's index): each group's label and categories.

#### `assetMap`

*Constant*

```ts
const assetMap: Readonly<Record<string, Readonly<Record<string, AssetGlyphId | "face" | "dot">>>>;
```

(category, subtype) to a glyph, "face" (a sign) or "dot"; "*" is the category's fallback.

#### `assetTiers`

*Constant*

```ts
const assetTiers: Readonly<Record<PuckTier, {
    puck: number;
    minzoom: number;
    sort: number;
}>>;
```

Tiers: puck size (px), the zoom pucks appear from, and their symbol-sort-key (faces are 1 to 3).

#### `BEAM_PX`

*Constant*

```ts
const BEAM_PX = 44;
```

Size of the facing beam image, px (square; the beam's apex is its centre).

#### `faceRules`

*Constant*

```ts
const faceRules: readonly FaceRule[];
```

The face map rules (map-assets/signs/sources/roadside-face-map.json): first match wins.

#### `faceTemplates`

*Constant*

```ts
const faceTemplates: Readonly<Record<FaceTemplateId, string>>;
```

Face templates (SVG, 100 units high, with slots).

#### `MARKER_COLOURS`

*Constant*

```ts
const MARKER_COLOURS: {
    readonly light: {
        readonly ink: "#0f1115";
        readonly ground: "#ffffff";
        readonly unreadFill: "#d4d6d9";
        readonly unreadRing: "#6b6f75";
        readonly rearFill: "#6b6f75";
        readonly rearRail: "#d4d6d9";
        readonly stemOpacity: 0.62;
        readonly keylineOpacity: 0.18;
        readonly shadowOpacity: 0.28;
        readonly beamOpacity: 0.5;
    };
    readonly dark: {
        readonly ink: "#e9eef8";
        readonly ground: "#ffffff";
        readonly unreadFill: "#3e4a66";
        readonly unreadRing: "#a8b4cc";
        readonly rearFill: "#7c89a6";
        readonly rearRail: "#1f2c48";
        readonly stemOpacity: 0.8;
        readonly keylineOpacity: 0;
        readonly shadowOpacity: 0.55;
        readonly beamOpacity: 0.5;
    };
};
```

Marker colours: the design-system tokens of website/app/globals.css (Day `:root`, Night
`[data-theme="dark"]`). Faces keep the sign palette; everything around a face is ink.

#### `MIN_CAP_PX`

*Constant*

```ts
const MIN_CAP_PX = 4.5;
```

The smallest rendered cap height, in px, at which a plate's words are still drawn.

#### `PLATE_MAX_WIDTH`

*Constant*

```ts
const PLATE_MAX_WIDTH = 4;
```

A worded plate (a street name) never grows wider than this many nominal sizes.

#### `PUCK_COLOURS`

*Constant*

```ts
const PUCK_COLOURS: {
    readonly light: {
        readonly ink: "#0f1115";
        readonly ground: "#ffffff";
        readonly onColour: "#ffffff";
        readonly ringGlyph: "colour";
        readonly shadow: 0.22;
    };
    readonly dark: {
        readonly ink: "#e9eef8";
        readonly ground: "#0a1020";
        readonly onColour: "#ffffff";
        readonly ringGlyph: "ink";
        readonly shadow: 0.5;
    };
};
```

Design-system tokens (website/app/globals.css): Day `:root`, Night `[data-theme="dark"]`. `ground`
is the disc behind a ring puck (and a solid puck's rim): white on Day, Night `--ground`, so a group
colour keeps its 3:1 against it. `ink` is the colour of a puck with no group (a neutral puck).
`ringGlyph` is the glyph's colour inside a ring or tint puck: the group colour on a Day map; ink on
a Night map, where a mid-tone glyph of thin strokes on navy reads dim (the ring carries the colour).

#### `PUCK_STYLES`

*Constant*

```ts
const PUCK_STYLES: Readonly<Record<PuckTier, PuckStyle>>;
```

Tier styles (run marker-colours, 4 Nov 2026: pucks wear their legend group's colour). Safety pucks
are the largest and solid; street furniture is smaller, a coloured ring and glyph on the ground;
things on the ground are smallest, flat (no shadow: they lie on the ground) and quietest.

#### `SHAPE_SCALE`

*Constant*

```ts
const SHAPE_SCALE: Readonly<Record<FaceShape, number>>;
```

Optical size per shape: a square plate at the nominal size outweighs a roundel and a triangle reads
smaller, so a face's longer side is the nominal size times this. Tuned on the review sheet (3 Nov 2026).

#### `signAttribution`

*Constant*

```ts
const signAttribution = "Contains public sector information licensed under the Open Government Licence v3.0. Traffic sign images are Crown copyright.";
```

Show wherever a sign face is shown (OGL v3 attribution A).

#### `signShortCredit`

*Constant*

```ts
const signShortCredit = "Sign images Crown copyright, OGL v3";
```

The one-line map credit form of the attribution.

#### `STEM`

*Constant*

```ts
const STEM: {
    readonly length: 9;
    readonly width: 1.5;
    readonly gap: 2;
};
```

Stem geometry of the full tier, px.

#### `TIER_PX`

*Constant*

```ts
const TIER_PX: Readonly<Record<MarkerTier, number>>;
```

Nominal face size per tier, in px: the longer side of a face, the height of a worded plate twice over.

## `@mapmap/maps/models`

```ts
import { /* … */ } from "@mapmap/maps/models";
```

### Classes (@mapmap/maps/models)

[`ModelLayer`](#modellayer), [`TransitVehicles3dLayer`](#transitvehicles3dlayer)

#### `ModelLayer`

*Class*

```ts
class ModelLayer { … }
```

glTF models as one MapLibre custom layer (`renderingMode: "3d"`, so the
basemap's own extrusions occlude the models and the models occlude
them). Each model is drawn at its own anchor through
`modelClipMatrices`, so models a continent apart are each placed
exactly, on mercator and on the globe. The layer survives `setStyle`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly id` | `string` |  |  |
| `readonly type` | `"custom"` |  |  |
| `readonly renderingMode` | `"3d"` |  |  |
| `readonly scene` | `THREE.Scene<THREE.Object3DEventMap>` |  | The three.js scene (lights and one root per model): an escape hatch. |

**Constructor and methods**

- `constructor(map: MapMapMap, opts?: ModelLayerOptions)`
- `onAdd(map: Map, gl: WebGLRenderingContext | WebGL2RenderingContext): void`
- `onRemove(): void`
- `render(_gl: WebGLRenderingContext | WebGL2RenderingContext, args: unknown): void`
- `add(model: ModelSpec): Promise<void>`: Load a model and place it. Resolves when it is on the map; rejects with the loader's error (no silent fallback). Adding an id that exists replaces that model.
- `remove(id: string): void`: Remove a model (and its clip). Unknown ids are ignored.
- `setPosition(id: string, lngLat: [number, number], altitudeM?: number, bearingDeg?: number): void`: Move a model; a `clip: true` footprint moves with it.
- `models(): string[]`: The ids of the models on the map, in the order they were added.
- `dispose(): void`: Remove the layer, every model and every clip it added.

**Example**

```ts
import { map } from "./map.ts";
import { ModelLayer } from "@mapmap/maps/models";

// Needs the optional peer dependency three.
const models = new ModelLayer(map);
map.map.addLayer(models);
await models.add({ id: "kiosk", url: "https://example.com/kiosk.glb", lngLat: [-0.1276, 51.5072], bearingDeg: 30 });
```

#### `TransitVehicles3dLayer`

*Class*

```ts
class TransitVehicles3dLayer implements CustomLayerInterface { … }
```

Live buses as 3D models when the map is tilted at street zoom.

Implements `CustomLayerInterface`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly id` | `string` |  |  |
| `readonly type` | `"custom"` |  |  |
| `readonly renderingMode` | `"3d"` |  |  |
| `readonly stats` | `{ rebuilds: number; buses: number; drawCalls: number; }` |  | Counters for tests and meters: instance rebuilds, buses drawn, draw calls in the last frame. |
| `readonly active` | `boolean` |  | Whether this device draws 3D buses at all (false on a touch screen unless `touch: true`). |

**Constructor and methods**

- `constructor(layer2d: TransitVehiclesLayer, options?: TransitVehicles3dOptions)`
- `currentFade(): number`: How strongly the 3D buses draw right now, 0 to 1.
- `setFixes(vehicles: readonly TransitDrawnVehicle[]): void`: The 2D layer's latest drawn vehicles. Rebuilds the instance matrices when the buses can be seen; otherwise keeps them for when they can. Called by the 2D layer's draw tick; there is no need to call it.
- `onAdd(map: Map, gl: WebGLRenderingContext | WebGL2RenderingContext): void`
- `onRemove(): void`
- `render(_gl: WebGLRenderingContext | WebGL2RenderingContext, args: unknown): void`
- `dispose(): void`: Remove the layer, hand the sprite back to the 2D layer and free the GPU buffers.

**Example**

```ts
import { map } from "./map.ts";
import { TransitVehiclesLayer } from "@mapmap/maps";
import { TransitVehicles3dLayer } from "@mapmap/maps/models";

// 3D buses when the map is tilted at street zoom, fed by the 2D layer.
const buses = new TransitVehiclesLayer(map, { bbox: "viewport" });
buses.start();
map.map.addLayer(new TransitVehicles3dLayer(buses));
```

### Functions (@mapmap/maps/models)

[`bearingToRotationY`](#bearingtorotationy), [`bus3dFade`](#bus3dfade), [`busDrawType`](#busdrawtype), [`busGeometry`](#busgeometry), [`busInstances`](#businstances), [`busScaleFloor`](#busscalefloor), [`decodeBusModel`](#decodebusmodel), [`defaultLights`](#defaultlights), [`doubleDeckerGeometry`](#doubledeckergeometry), [`followedRingGeometry`](#followedringgeometry), [`footprintPolygon`](#footprintpolygon), [`lngLatFromLocal`](#lnglatfromlocal), [`loadGlb`](#loadglb), [`localOffset`](#localoffset), [`metresPerPixel`](#metresperpixel), [`placeModel`](#placemodel), [`roundedRectOutline`](#roundedrectoutline)

#### `bearingToRotationY`

*Function*

```ts
function bearingToRotationY(bearingDeg: number): number;
```

A compass bearing (degrees clockwise from north, seen from above) as a
three.js yaw about +Y. NEGATIVE: clockwise from above is a negative
rotation about an up axis (the San Francisco landmark sign trap).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `bearingDeg` | `number` | required |  |

#### `bus3dFade`

*Function*

```ts
function bus3dFade(view: {
    zoom: number;
    pitch: number;
}, opts: {
    fadeIn: [number, number];
    minPitch: number;
}): number;
```

How strongly the 3D buses draw, 0 to 1: the zoom fade (`fadeIn`) times
the pitch fade (full from `minPitch`, nothing 5 degrees below). The 2D
sprite draws at `1 - fade`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `view` | `{ zoom: number; pitch: number; }` | required |  |
| `opts` | `{ fadeIn: [number, number]; minPitch: number; }` | required |  |

#### `busDrawType`

*Function*

```ts
function busDrawType(v: Partial<Pick<TransitDrawnVehicle, "drawAs" | "vehicleType">>, defaultType?: BusVehicleType): BusVehicleType;
```

The body type a drawn vehicle is drawn as: the 2D layer's `drawAs`, else its feed type, else `defaultType`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `v` | `Partial<Pick<TransitDrawnVehicle, "drawAs" \| "vehicleType">>` | required |  |
| `defaultType?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |

Options and types: [`TransitDrawnVehicle`](#transitdrawnvehicle), [`BusVehicleType`](#busvehicletype-1).

#### `busGeometry`

*Function*

```ts
function busGeometry(type?: BusVehicleType, livery?: "day" | "night"): THREE.BufferGeometry;
```

A modelled UK bus of body `type` (default the generic one), front facing
-Z (north at bearing 0), wheels on y = 0, the door on the nearside (-X),
as ONE indexed geometry with vertex colours from the `livery` slots
([`BUS_3D_LIVERY`](#bus_3d_livery)): body, glass, a light roof (what reads from
above), blank blind, trim, tyres, head and tail lights. Our own geometry,
generated by tools/bus-model/build_bus.py.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |
| `livery?` | `"day" \| "night"` | not stated |  |

Options and types: [`BusVehicleType`](#busvehicletype-1).

#### `busInstances`

*Function*

```ts
function busInstances(vehicles: readonly TransitDrawnVehicle[], anchor: [number, number], scale: number, max: number, defaultType?: BusVehicleType): BusInstances;
```

Instance matrices (column-major 4x4, local metres from `anchor`, X east,
Z south, front facing north at bearing 0) for the `max` drawable buses
nearest the anchor, split by body type ([`busDrawType`](#busdrawtype)). A bus
without a heading is left to the 2D layer. A bus is faded (its fleet's
`dim` matrices) when it is stale, or when another vehicle is followed.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `vehicles` | [`readonly TransitDrawnVehicle[]`](#transitdrawnvehicle) | required |  |
| `anchor` | `[number, number]` | required |  |
| `scale` | `number` | required |  |
| `max` | `number` | required |  |
| `defaultType?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |

Options and types: [`TransitDrawnVehicle`](#transitdrawnvehicle), [`BusVehicleType`](#busvehicletype-1).

#### `busScaleFloor`

*Function*

```ts
function busScaleFloor(zoom: number, lat: number, minLengthPx: number, lengthM?: number): number;
```

The scale on the true-size bus so it is at least `minLengthPx` long on screen (1 from then on).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `zoom` | `number` | required |  |
| `lat` | `number` | required |  |
| `minLengthPx` | `number` | required |  |
| `lengthM?` | `number` | not stated |  |

#### `decodeBusModel`

*Function*

```ts
function decodeBusModel(type?: BusVehicleType): DecodedBusModel;
```

One modelled body type's buffers, decoded once and cached: positions from
Int16 millimetres, normals from Int8, the slot of every vertex from the
slot runs, and the triangle indices from their varint coding (each
corner is (next unused vertex - index), so a vertex's first use is 0).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `type?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |

Options and types: [`BusVehicleType`](#busvehicletype-1).

#### `defaultLights`

*Function*

```ts
function defaultLights(): THREE.Light[];
```

The London demo's daylight: a sky/ground hemisphere and an afternoon sun from the south-west.

#### `doubleDeckerGeometry`

*Function*

```ts
function doubleDeckerGeometry(livery?: "day" | "night"): THREE.BufferGeometry;
```

The modelled UK double-decker, 10.9 x 2.55 x 4.3 m ([`busGeometry`](#busgeometry)
with `"double"`): front facing -Z, wheels on y = 0, one indexed geometry
with vertex colours from the `livery` slots.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `livery?` | `"day" \| "night"` | not stated |  |

#### `followedRingGeometry`

*Function*

```ts
function followedRingGeometry(scale: number, bandM: number, clearanceM?: number, type?: BusVehicleType): THREE.BufferGeometry;
```

The followed bus's ground ring, in the 2D marker's colours: a white band
then an ink band around the footprint of a bus of body `type` (`scale` times the true
footprint, plus `clearanceM`), each `bandM` metres wide, flat on the
ground. One geometry with vertex colours.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `scale` | `number` | required |  |
| `bandM` | `number` | required |  |
| `clearanceM?` | `number` | not stated |  |
| `type?` | [`BusVehicleType`](#busvehicletype-1) | not stated |  |

Options and types: [`BusVehicleType`](#busvehicletype-1).

#### `footprintPolygon`

*Function*

```ts
function footprintPolygon(object: THREE.Object3D, lngLat: [number, number], maxHeightM?: number): Polygon | null;
```

The lng/lat footprint polygon of a placed model: the convex hull of its
base ring, closed. When the ring is degenerate (fewer than 3 distinct
points, a model with no flat base) the XZ bounding rectangle of every
vertex is used instead.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `object` | `THREE.Object3D` | required |  |
| `lngLat` | `[number, number]` | required |  |
| `maxHeightM?` | `number` | not stated |  |

#### `lngLatFromLocal`

*Function*

```ts
function lngLatFromLocal(anchor: [number, number], x: number, z: number): [number, number];
```

The inverse of [`localOffset`](#localoffset): the `[lng, lat]` at local metres `(x, z)` from `anchor`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `anchor` | `[number, number]` | required |  |
| `x` | `number` | required |  |
| `z` | `number` | required |  |

#### `loadGlb`

*Function*

```ts
function loadGlb(url: string): Promise<THREE.Group>;
```

Load a `.glb` (meshopt or plain) and return its scene. Rejects with the loader's own error.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |

#### `localOffset`

*Function*

```ts
function localOffset(lngLat: [number, number], anchor: [number, number]): {
    x: number;
    z: number;
};
```

Local offset in metres (X east, Z south, the three.js frame every MapMap
3D scene uses) of `lngLat` from `anchor`, through web mercator at the
anchor's scale: `(merc - anchorMerc) / meterInMercatorCoordinateUnits(anchor)`,
the same expression the London and San Francisco demos evaluate with
`maplibregl.MercatorCoordinate`.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |
| `anchor` | `[number, number]` | required |  |

#### `metresPerPixel`

*Function*

```ts
function metresPerPixel(zoom: number, lat: number): number;
```

Metres per CSS pixel at `zoom` and latitude `lat` (512 px tiles).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `zoom` | `number` | required |  |
| `lat` | `number` | required |  |

#### `placeModel`

*Function*

```ts
function placeModel(object: THREE.Object3D, placement: Placement, anchor?: [number, number]): THREE.Object3D;
```

Seat a loaded model: bearing as `rotation.y = -bearing`, uniform scale,
and its origin at `localOffset(lngLat, anchor)` with `y = altitudeM`. The
model's own origin is its ground point (glTF bakes export Y-up metres with
the origin at ground centre). With no `anchor` the model sits at the
scene origin, which is how [`ModelLayer`](#modellayer) places each model (every
model is drawn at its own anchor). For apps that keep their own scene.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `object` | `THREE.Object3D` | required |  |
| `placement` | [`Placement`](#placement) | required |  |
| `anchor?` | `[number, number]` | not stated |  |

Options and types: [`Placement`](#placement).

#### `roundedRectOutline`

*Function*

```ts
function roundedRectOutline(halfL: number, halfW: number, r: number, n?: number): [number, number][];
```

The outline of a rounded rectangle centred on the origin in the XZ
plane, `halfL` along Z, `halfW` along X, corner radius `r`, `n` points
per corner. Offsetting `halfL`, `halfW` and `r` by the same distance
gives the parallel outline point for point, which is how the ring's
bands are built.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `halfL` | `number` | required |  |
| `halfW` | `number` | required |  |
| `r` | `number` | required |  |
| `n?` | `number` | not stated |  |

### Interfaces (@mapmap/maps/models)

[`BusFleet`](#busfleet), [`BusInstances`](#businstances-1), [`DecodedBusModel`](#decodedbusmodel), [`ModelLayerOptions`](#modellayeroptions), [`ModelSpec`](#modelspec), [`Placement`](#placement), [`TransitVehicles3dOptions`](#transitvehicles3doptions)

#### `BusFleet`

*Interface*

```ts
interface BusFleet
```

One body type's instance matrices: the buses at full strength and the faded ones.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `full` | `Float32Array` | required |  |
| `fullCount` | `number` | required |  |
| `dim` | `Float32Array` | required |  |
| `dimCount` | `number` | required |  |

#### `BusInstances`

*Interface*

```ts
interface BusInstances
```

The instance data for one rebuild: a fleet per body type, and where the followed bus is.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `fleets` | `Record<BusVehicleType, BusFleet>` | required |  |
| `count` | `number` | required | Buses placed, every type. |
| `followed` | `{ x: number; z: number; rotationY: number; type: BusVehicleType; } \| null` | required | The followed bus, in local metres from the anchor, or null when it is not drawn. |

#### `DecodedBusModel`

*Interface*

```ts
interface DecodedBusModel
```

A modelled bus decoded from the generated data, in metres.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `positions` | `Float32Array` | required |  |
| `normals` | `Float32Array` | required |  |
| `slots` | `Uint8Array` | required | The colour slot of each vertex, an index into the generated slot names (body, glass, roof, blind, trim, tyre, head, tail). |
| `index` | `Uint16Array` | required |  |

#### `ModelLayerOptions`

*Interface*

```ts
interface ModelLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` | `"mapmap-models"` | Default `"mapmap-models"`. |
| `models?` | [`ModelSpec[]`](#modelspec) | not stated | Models to add straight away (errors are logged; use `add` to await them). |
| `slot?` | [`Slot`](#slot) | directly above `building-3d` when present, else `"middle"` | Insertion slot. Default: directly above `building-3d` when present, else `"middle"`. |
| `beforeId?` | `string` | not stated | Insert below this layer id while it exists (then the slot applies). |
| `terrain?` | `"clamp" \| "absolute"` | not stated | `"clamp"` (default): each model's altitude is above the terrain under its anchor (`queryTerrainElevation`), so it stands on 3D terrain. Only the anchor is clamped: a long model does not follow the ground (the MapLibre 6.13 draping hooks are the fix, see `supportsTerrainDraping`). `"absolute"`: altitude is metres above sea level. |
| `lights?` | `"default" \| THREE.Light[]` | not stated | `"default"` (the London demo's daylight) or your own lights. |
| `frustumCulled?` | `false` | not stated | Always `false`: every object is drawn with frustum culling off. The option only documents it. |

#### `ModelSpec`

*Interface*

```ts
interface ModelSpec
```

One glTF model on the map.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required | Unique within the layer. |
| `url` | `string` | required | A `.glb` (meshopt-compressed or plain), https or same-origin. |
| `lngLat` | `[number, number]` | required | Where the model's origin (its ground point) stands. |
| `altitudeM?` | `number` | 0 | Metres above the ground (`terrain: "clamp"`) or above sea level (`"absolute"`). Default 0. |
| `bearingDeg?` | `number` | 0 | Compass bearing, degrees clockwise from north. Default 0. |
| `scale?` | `number` | 1 | Uniform scale; model units are metres. Default 1. |
| `clip?` | `boolean \| Polygon` | not stated | Hide the basemap's buildings under the model. `true` uses the convex hull of the model's base ring (vertices within 0.5 m of its lowest point) projected to lng/lat; a Polygon is used as given. |

#### `Placement`

*Interface*

```ts
interface Placement
```

A model's placement, in the shape [`placeModel`](#placemodel) reads.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `lngLat` | `[number, number]` | required |  |
| `altitudeM?` | `number` | not stated |  |
| `bearingDeg?` | `number` | not stated |  |
| `scale?` | `number` | not stated |  |

#### `TransitVehicles3dOptions`

*Interface*

```ts
interface TransitVehicles3dOptions
```

Options for [`TransitVehicles3dLayer`](#transitvehicles3dlayer).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` | `"mapmap-transit-vehicles-3d"` | Layer id. Default `"mapmap-transit-vehicles-3d"`. |
| `fadeIn?` | `[number, number]` | `[15.5, 16.5]` | Zoom band over which the 3D buses fade in and the sprite fades out. Default `[15.5, 16.5]`. |
| `minPitch?` | `number` | 30 | Camera pitch, degrees, from which the 3D buses draw fully (faded in over the 5 degrees below). Default 30. |
| `max?` | `number` | 200 | Most buses drawn, nearest the centre of the view first. Default 200. |
| `touch?` | `boolean` | false: desktop only | Draw on touch devices too (a `(pointer: coarse)` screen). Default false: desktop only. |
| `touchMax?` | `number` | 80 | Most buses drawn on a touch screen (when `touch` is on), nearest the centre of the view first. Default 80. |
| `minLengthPx?` | `number` | 40 | Shortest on-screen length of a bus, CSS px, before it reaches true size. Default 40. |
| `slot?` | [`Slot`](#slot) | the SDK's `middle` slot, under the labels | Where in the style the layer sits. Default: the SDK's `middle` slot, under the labels. |
| `beforeId?` | `string` | not stated | Insert before this layer id instead of a slot. |

### Constants (@mapmap/maps/models)

[`BUS_3D_LIVERY`](#bus_3d_livery), [`BUS_DIMENSIONS`](#bus_dimensions), [`DOUBLE_DECKER`](#double_decker), [`TRANSIT_3D_DEFAULTS`](#transit_3d_defaults), [`TRANSIT_3D_PITCH_RAMP`](#transit_3d_pitch_ramp)

#### `BUS_3D_LIVERY`

*Constant*

```ts
const BUS_3D_LIVERY: {
    readonly day: {
        readonly body: "#15181e";
        readonly glass: "#8fa3bb";
        readonly roof: "#f4f6f8";
        readonly blind: "#2b3038";
        readonly trim: "#4a515c";
    };
    readonly night: {
        readonly body: "#aab3c0";
        readonly glass: "#1c222b";
        readonly roof: "#f1f3f6";
        readonly blind: "#0e1116";
        readonly trim: "#6b7480";
    };
};
```

The body colours per map ground. Day: the 2D marker's ink with light
blue-grey glass and a near-white roof, so a bus stands off the light
basemap and reads as a bus (the 0.21.0 slate read grey and small).
Night: light grey-blue with dark glass, because an ink bus vanishes on
the Night base, whose roads are slate. `blind` is the blank destination
display, `trim` the bumpers, hatches, hubs and mirror arms.

#### `BUS_DIMENSIONS`

*Constant*

```ts
const BUS_DIMENSIONS: Record<BusVehicleType, {
    lengthM: number;
    widthM: number;
    heightM: number;
}>;
```

Each modelled body type's length, width and height, metres (height to the top of any roof hatch or pod).

#### `DOUBLE_DECKER`

*Constant*

```ts
const DOUBLE_DECKER: {
    readonly lengthM: 10.9;
    readonly widthM: 2.55;
    readonly heightM: 4.3;
};
```

A UK double-decker, metres (the modelled one, roof hatches left out of the height).

#### `TRANSIT_3D_DEFAULTS`

*Constant*

```ts
const TRANSIT_3D_DEFAULTS: {
    readonly fadeIn: [number, number];
    readonly minPitch: 30;
    readonly max: 200;
    readonly touch: false;
    readonly touchMax: 80;
    readonly minLengthPx: 40;
};
```

Defaults for [`TransitVehicles3dOptions`](#transitvehicles3doptions).

#### `TRANSIT_3D_PITCH_RAMP`

*Constant*

```ts
const TRANSIT_3D_PITCH_RAMP = 5;
```

The pitch band, degrees, below `minPitch` over which the buses fade in.

## `@mapmap/maps/tiles`

```ts
import { /* … */ } from "@mapmap/maps/tiles";
```

### Classes (@mapmap/maps/tiles)

[`TilesLayer`](#tileslayer)

#### `TilesLayer`

*Class*

```ts
class TilesLayer { … }
```

A 3D Tiles tileset as one MapLibre custom layer (`renderingMode: "3d"`).
Survives `setStyle`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly id` | `string` |  |  |
| `readonly type` | `"custom"` |  |  |
| `readonly renderingMode` | `"3d"` |  |  |
| `readonly tiles` | `TilesRenderer` |  | The 3DTilesRendererJS renderer: an escape hatch (plugins, events, `errorTarget`). |
| `readonly scene` | `THREE.Scene<THREE.Object3DEventMap>` |  | The three.js scene the tiles are drawn in. |

**Constructor and methods**

- `constructor(map: MapMapMap, opts: TilesLayerOptions)`
- `getAnchor(): [number, number] | null`: The `[lng, lat]` the tileset is drawn at, once its root has loaded (or given).
- `onAdd(map: Map, gl: WebGLRenderingContext | WebGL2RenderingContext): void`
- `onRemove(): void`
- `render(_gl: WebGLRenderingContext | WebGL2RenderingContext, args: unknown): void`
- `dispose(): void`: Remove the layer, its clip and every loaded tile.

**Example**

```ts
import { map } from "./map.ts";
import { TilesLayer } from "@mapmap/maps/tiles";

// Needs the optional peer dependencies three and 3d-tiles-renderer.
const tiles = new TilesLayer(map, { url: "https://example.com/tileset/tileset.json" });
map.map.addLayer(tiles);
```

### Interfaces (@mapmap/maps/tiles)

[`TilesLayerOptions`](#tileslayeroptions)

#### `TilesLayerOptions`

*Interface*

```ts
interface TilesLayerOptions
```

`@mapmap/maps/tiles`: OGC 3D Tiles 1.0 / 1.1 on a MapMap map through
3DTilesRendererJS (`3d-tiles-renderer`, Apache-2.0, NASA JPL / AMMOS):
`TilesLayer`.

```ts
import { TilesLayer } from "@mapmap/maps/tiles";
const tiles = new TilesLayer(map, { url: "https://example.com/tileset.json" });
```

Its own subpath (from 0.19.0; it was in `@mapmap/maps/models` before),
because it is the only importer of `3d-tiles-renderer`: a bundler resolves
every import of a module it includes, static or dynamic, so an app that
draws glTF models alone must not have to install it. Needs both optional
peers, `three` and `3d-tiles-renderer`.

The tileset is re-centred so its anchor (by default the centre of its own
root bounding volume) is the local origin, with X east, Y up and Z south,
the frame every MapMap 3D scene uses, and then drawn through the same
bridge as `ModelLayer` (`modelClipMatrices`, `GLOBE_BLEND_GLSL` injected
into every tile's materials as it loads).

Level of detail and culling in `TilesRenderer` need a camera with a real
view matrix, which a MapLibre custom layer does not get. The layer
reconstructs one each frame: `projectionMatrix` is MapLibre's view-to-clip
matrix `P`, and the view in the tileset's local metres is
`inverse(P) * ClipMatrices.mercator`, so `P * view` is exactly the matrix
the tiles are drawn with. On the globe below the globe-to-mercator zoom
band the mercator reconstruction is an approximation (the tileset is a
few pixels across there); cap `maxDepth` if a tileset over-refines.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `id?` | `string` | `"mapmap-tiles"` | Default `"mapmap-tiles"`. |
| `url` | `string` | required | The tileset's `tileset.json` URL. |
| `anchor?` | `[number, number]` | the centre of its root bounding volume | `[lng, lat]` the tileset is re-centred on. Default: the centre of its root bounding volume. |
| `errorTarget?` | `number` | 16 | Screen-space error target in pixels (3DTilesRendererJS `errorTarget`). Default 16. |
| `maxDepth?` | `number` | unlimited | Deepest tile level to load. Default unlimited. |
| `clip?` | `Polygon` | not stated | Hide the basemap's buildings inside this polygon (`map.addClip`). |
| `slot?` | [`Slot`](#slot) | not stated |  |
| `beforeId?` | `string` | not stated |  |
| `terrain?` | `"clamp" \| "absolute"` | not stated | `"absolute"` (default): the tileset's own heights (above the WGS84 ellipsoid) are used as metres above the map's datum. `"clamp"`: the tileset is lifted by the terrain elevation under its anchor, for tilesets authored with height 0 at the ground. |

## `@mapmap/maps/live`

```ts
import { /* … */ } from "@mapmap/maps/live";
```

### Classes (@mapmap/maps/live)

[`AuroraLayer`](#auroralayer), [`EarthEventsLayer`](#eartheventslayer), [`WeatherLayer`](#weatherlayer), [`WindLayer`](#windlayer)

#### `AuroraLayer`

*Class*

```ts
class AuroraLayer { … }
```

The NOAA SWPC aurora forecast, drawn on the night side.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ source: string; layers: string[]; }` |  |  |

**Constructor and methods**

- `constructor(map: MapMapMap, options?: AuroraLayerOptions)`
- `start(): void`: Read the forecast now and every `refreshMs`. Safe to call before the style has loaded.
- `stop(): void`: Stop polling. What is drawn stays.
- `remove(): void`: Stop, and remove the layer, the credit carrier and every listener. Safe to call twice.
- `setTime(atMs: number | null): void`: Move the sun (the night gate) to a scene time; null is now. The aurora is drawn only within 3 hours of now.
- `report(): AuroraReport`

**Example**

```ts
import { map } from "./map.ts";
import { AuroraLayer } from "@mapmap/maps/live";

const aurora = new AuroraLayer(map);
aurora.start();
```

#### `EarthEventsLayer`

*Class*

```ts
class EarthEventsLayer { … }
```

Live Earth events: quakes, volcano alerts and fire cells.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ source: Record<EventType, string>; layers: string[]; }` |  | The source ids by type, and every layer id this layer owns. |

**Constructor and methods**

- `constructor(map: MapMapMap, options?: EarthEventsLayerOptions)`
- `start(): void`: Start reading and drawing. Safe to call before the style has loaded.
- `stop(): void`: Stop polling. What is drawn stays.
- `remove(): void`: Stop, and remove every layer, source, image and listener. Safe to call twice.
- `setTypes(types: EventType[]): void`: The types drawn from now on; a type switched on is read at once.
- `setBase(base: EventsBase): void`: The map ink; quake rings re-paint without a refetch.
- `setBbox(bbox: EarthEventsLayerOptions["bbox"]): void`: The area to ask the gateway for (a refetch).
- `report(): EarthEventsReport`: The feeds' state after the latest poll.
- `select(detail: EventDetail | null): void`: Ring a quake (or clear with null); other kinds only open `onSelect`.
- `pulsing(): string[]`: Quake ids pulsing now or waiting to (for tests).

**Example**

```ts
import { map } from "./map.ts";
import { EarthEventsLayer } from "@mapmap/maps/live";

const events = new EarthEventsLayer(map, { types: ["quake", "volcano"], minMagnitude: 4 });
events.start();
```

#### `WeatherLayer`

*Class*

```ts
class WeatherLayer { … }
```

Forecast colour fields and pressure lines.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ source: string[]; layers: string[]; }` |  | The credit carrier source, and every layer id currently installed. |

**Constructor and methods**

- `constructor(map: MapMapMap, options?: WeatherLayerOptions)`
- `start(): void`: Load the manifest and show the frame nearest now. Never plays. Safe before the style has loaded.
- `stop(): void`: Stop playback and any fetch. What is drawn stays.
- `remove(): void`: Stop, and remove every layer, source and listener. Safe to call twice.
- `setField(field: WeatherField | null): void`: Draw a field (or null for none). Opening a field with nothing else on shows the live hour.
- `setIsobars(on: boolean): void`: Draw the pressure lines.
- `seek(index: number): void`: Ask for frame `index` (clamped). The old frame stays up until it is drawable.
- `setTime(atMs: number | null): void`: Show the frame whose valid time is nearest `atMs` (null: now).
- `play(on: boolean): void`: Play the frames, 750 ms each with a 1.5 s hold on the last, looping. A failed frame stops it.
- `setBase(base: "light" | "dark"): void`: The map ink; fields and lines are rebuilt for it without a refetch of the manifest.
- `frames(): WeatherFrame[]`: The 17 frames of the newest run, oldest first.
- `report(): WeatherReport`

**Example**

```ts
import { map } from "./map.ts";
import { WeatherLayer } from "@mapmap/maps/live";

const weather = new WeatherLayer(map, { field: "rain" });
weather.start();
weather.play(true);
```

#### `WindLayer`

*Class*

```ts
class WindLayer { … }
```

Wind particles from the NOAA GFS forecast. Forecast, not an observation.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `readonly ids` | `{ source: string[]; layers: string[]; }` |  | The layer ids this layer owns while installed. |

**Constructor and methods**

- `constructor(map: MapMapMap, options?: WindLayerOptions)`
- `start(): void`: Load the manifest and the frame for now. Safe to call before the style has loaded.
- `stop(): void`: Stop fetching. What is drawn stays (the particles keep moving).
- `remove(): void`: Stop, and remove every layer, source, image and listener. Safe to call twice.
- `setTime(atMs: number | null): void`: Show the frame whose valid time is nearest `atMs` (null: now). A new frame crossfades the velocity (280 ms) with no particle reset.
- `setDim(factor: number): void`: Multiply the trails' alpha, 0 to 1 (under a colour field the host dims the wind; see `windDimUnderField`).
- `setVisible(on: boolean): void`
- `setFollowing(on: boolean): void`: True while a follow camera drives the map: small programmatic moves keep the trails.
- `setBase(base: "light" | "dark"): void`: The map ink; the particles and arrows are rebuilt for it without a refetch.
- `report(): WindReport`
- `sample(lngLat: [number, number]): { u: number; v: number; speedMs: number; headingDeg: number; } | null`: The wind at a point from the frame shown, read back from its pixels once; null before a frame is ready.

**Example**

```ts
import { map } from "./map.ts";
import { WindLayer } from "@mapmap/maps/live";

const wind = new WindLayer(map);
wind.start();
console.log(wind.sample([-0.1276, 51.5072])?.speedMs);
```

### Functions (@mapmap/maps/live)

[`auroraLabel`](#auroralabel), [`buildFieldMesh`](#buildfieldmesh), [`decodeValue`](#decodevalue), [`detailFromFeature`](#detailfromfeature), [`equirectUv`](#equirectuv), [`eventIds`](#eventids), [`fieldStops`](#fieldstops), [`hexRgb01`](#hexrgb01), [`liveFetch`](#livefetch), [`mercatorY`](#mercatory), [`nearestFrameIndex`](#nearestframeindex), [`parseAuroraFile`](#parseaurorafile), [`pickWindFrame`](#pickwindframe), [`preferHalf`](#preferhalf), [`quakeRadius`](#quakeradius), [`solarElevationDeg`](#solarelevationdeg), [`subsolarPoint`](#subsolarpoint), [`timeAgo`](#timeago), [`windDimUnderField`](#winddimunderfield), [`windHeadingDeg`](#windheadingdeg), [`windSpeed`](#windspeed)

#### `auroraLabel`

*Function*

```ts
function auroraLabel(kp: number | null | undefined): string;
```

The row's readout: "Quiet" under Kp 3, else "Kp 4" (rounded); "Unavailable" without one.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `kp` | `number \| null \| undefined` | required |  |

#### `buildFieldMesh`

*Function*

```ts
function buildFieldMesh(stepDeg?: number): FieldMesh;
```

A lng/lat grid over the whole sphere, `stepDeg` per cell, with its top
and bottom rows at the poles. Pole vertices carry the flag MapLibre's
prelude reads (`rawPos.y` beyond +-32767.5) so they land exactly on the
pole on the globe and are culled on the flat map.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `stepDeg?` | `number` | not stated |  |

#### `decodeValue`

*Function*

```ts
function decodeValue(r: number, g: number, b: number, encoding: Pick<FieldEncoding, "scale" | "offset">): number;
```

The value a 24-bit pixel encodes.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `r` | `number` | required |  |
| `g` | `number` | required |  |
| `b` | `number` | required |  |
| `encoding` | `Pick<FieldEncoding, "scale" \| "offset">` | required |  |

Options and types: [`FieldEncoding`](#fieldencoding).

#### `detailFromFeature`

*Function*

```ts
function detailFromFeature(ids: Pick<EventIds, "quakeRing" | "quakeCore" | "volcano" | "fire">, f: {
    layer?: {
        id?: string;
    };
    properties?: Record<string, unknown> | null;
    geometry?: {
        type?: string;
        coordinates?: unknown;
    };
}): EventDetail | null;
```

What a tapped feature opens, or null. `layerId` is the layer the feature came from.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `ids` | `Pick<EventIds, "quakeRing" \| "quakeCore" \| "volcano" \| "fire">` | required |  |
| `f` | `{ layer?: { id?: string; }; properties?: Record<string, unknown> \| null; geometry?: { type?: string; coordinates?: unknown; }; }` | required |  |

Options and types: [`EventIds`](#eventids-1).

#### `equirectUv`

*Function*

```ts
function equirectUv(lng: number, lat: number, width?: number, height?: number): [number, number];
```

Texture coordinates of a point in the equirect PNG (texel centres:
column c is at 180 W + c x 0.25 deg, row r at 90 N - r x 0.25 deg). The
u coordinate wraps (REPEAT), so 180 E lands back on column 0.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `lng` | `number` | required |  |
| `lat` | `number` | required |  |
| `width?` | `number` | not stated |  |
| `height?` | `number` | not stated |  |

#### `eventIds`

*Function*

```ts
function eventIds(id: string): EventIds;
```

The source, layer and image ids for a prefix.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `id` | `string` | required |  |

#### `fieldStops`

*Function*

```ts
function fieldStops(field: WeatherFieldId, base: "light" | "dark"): FieldStop[];
```

The ramp a field is drawn with on a base.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `field` | `WeatherFieldId` | required |  |
| `base` | `"light" \| "dark"` | required |  |

#### `hexRgb01`

*Function*

```ts
function hexRgb01(hex: string): [number, number, number];
```

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `hex` | `string` | required |  |

#### `liveFetch`

*Function*

```ts
function liveFetch(url: string, options?: LiveFetchOptions): Promise<Response>;
```

`fetch` for a live file or endpoint. Rejects on a non-2xx answer with the
problem+json `code` in the message (`"HTTP 501 weather-frames-not-enabled"`),
or `"HTTP 404"` when the body carries none.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `url` | `string` | required |  |
| `options?` | [`LiveFetchOptions`](#livefetchoptions) | not stated |  |

Options and types: [`LiveFetchOptions`](#livefetchoptions).

#### `mercatorY`

*Function*

```ts
function mercatorY(latDeg: number): number;
```

Web-mercator y (0..1, 0 at 85.05 N) for a latitude; beyond +-85.05 it
runs past 0..1, which the globe prelude maps back to the true latitude.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `latDeg` | `number` | required |  |

#### `nearestFrameIndex`

*Function*

```ts
function nearestFrameIndex(frames: WeatherFrame$1[], atMs: number): number;
```

The frame whose valid time is nearest `atMs` (the earlier on a tie), or -1.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `frames` | `WeatherFrame$1[]` | required |  |
| `atMs` | `number` | required |  |

#### `parseAuroraFile`

*Function*

```ts
function parseAuroraFile(body: unknown): AuroraFile | null;
```

The file's fields, or null when it is not one. Both shapes parse.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `body` | `unknown` | required |  |

#### `pickWindFrame`

*Function*

```ts
function pickWindFrame(manifest: WeatherManifest$1, atMs: number, base: string, { half }?: {
    half?: boolean;
}): ChosenWindFrame | null;
```

The wind frame whose valid time is nearest `atMs`, from the newest run
that has wind at all. Frame paths are relative to the weather base.
Returns null when the manifest has no usable wind frame.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `manifest` | `WeatherManifest$1` | required |  |
| `atMs` | `number` | required |  |
| `base` | `string` | required |  |
| `{ half }?` | `{ half?: boolean; }` | not stated |  |

#### `preferHalf`

*Function*

```ts
function preferHalf(zoom: number, touch: boolean): boolean;
```

Half-resolution frames at globe zoom and on touch screens; full from zoom 4
on a pointer device (CONTRACT.md 3.2, the shared client rule; lane 1B's
weather-field.ts carries the same rule for the colour fields).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `zoom` | `number` | required |  |
| `touch` | `boolean` | required |  |

#### `quakeRadius`

*Function*

```ts
function quakeRadius(mag: number): number;
```

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `mag` | `number` | required |  |

#### `solarElevationDeg`

*Function*

```ts
function solarElevationDeg([lng, lat]: [number, number], atMs: number): number;
```

Solar elevation in degrees at `[lng, lat]` for a moment: 90 minus the
great-circle distance to the subsolar point (spherical cosine rule).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `[lng, lat]` | `[number, number]` | required |  |
| `atMs` | `number` | required |  |

#### `subsolarPoint`

*Function*

```ts
function subsolarPoint(atMs: number): [number, number];
```

Subsolar point `[lng, lat]` for a moment (epoch milliseconds).

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `atMs` | `number` | required |  |

#### `timeAgo`

*Function*

```ts
function timeAgo(then: number, now: number): string;
```

"Just now", "38 min ago", "5 h ago", "3 days ago".

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `then` | `number` | required |  |
| `now` | `number` | required |  |

#### `windDimUnderField`

*Function*

```ts
function windDimUnderField(zoom: number): number;
```

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `zoom` | `number` | required |  |

#### `windHeadingDeg`

*Function*

```ts
function windHeadingDeg(u: number, v: number): number;
```

The compass bearing the air moves TOWARDS, degrees clockwise from north
(an arrow's `icon-rotate`). Meteorology quotes where wind comes FROM;
an arrow on a map points where it goes.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `u` | `number` | required |  |
| `v` | `number` | required |  |

#### `windSpeed`

*Function*

```ts
function windSpeed(u: number, v: number): number;
```

Speed in metres per second.

| Parameter | Type | Default | Description |
| --- | --- | --- | --- |
| `u` | `number` | required |  |
| `v` | `number` | required |  |

### Interfaces (@mapmap/maps/live)

[`AuroraFile`](#aurorafile), [`AuroraLayerOptions`](#auroralayeroptions), [`AuroraReport`](#aurorareport), [`AuroraSource`](#aurorasource), [`EarthEventsFeedReport`](#eartheventsfeedreport), [`EarthEventsLayerOptions`](#eartheventslayeroptions), [`EarthEventsReport`](#eartheventsreport), [`EventIds`](#eventids-1), [`FieldEncoding`](#fieldencoding), [`FieldMesh`](#fieldmesh), [`FieldStop`](#fieldstop), [`LiveFetchOptions`](#livefetchoptions), [`WeatherFrame`](#weatherframe), [`WeatherLayerOptions`](#weatherlayeroptions), [`WeatherManifest`](#weathermanifest), [`WeatherReport`](#weatherreport), [`WeatherSource`](#weathersource), [`WindFrameRef`](#windframeref), [`WindLayerOptions`](#windlayeroptions), [`WindMeta`](#windmeta), [`WindReport`](#windreport), [`WindSource`](#windsource)

#### `AuroraFile`

*Interface*

```ts
interface AuroraFile
```

The aurora forecast's fields: the box's `latest.json`, or the gateway's
`GET /v1/space-weather` envelope (either parses to this).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `observation_time` | `string` | required |  |
| `forecast_time` | `string` | required |  |
| `kp` | `number` | required |  |
| `kp_time?` | `string \| null` | not stated |  |
| `max_probability?` | `number` | not stated |  |
| `generated_at?` | `string` | not stated |  |
| `attribution?` | `string` | not stated |  |
| `png?` | `string` | not stated | The grid PNG URL when the envelope names one (`aurora.png`); the envelope's wins. |

#### `AuroraLayerOptions`

*Interface*

```ts
interface AuroraLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `nightOnly?` | `boolean` | true | Draw only where the sun is below the horizon, eased by the night factor. Default true. |
| `breathe?` | `boolean` | true | The 12 s breathe (alpha x 0.85 to 1). Default true; off under reduced motion. |
| `refreshMs?` | `number` | 300,000 | Poll interval, milliseconds. Default 300,000; at least 60,000. |
| `onReport?` | `(report: AuroraReport) => void` | not stated |  |
| `source?` | [`AuroraSource`](#aurorasource) | not stated |  |
| `baseUrl?` | `string` | not stated |  |
| `apiKey?` | `string` | not stated |  |
| `id?` | `string` | `"mapmap-aurora"` | Id prefix. Default `"mapmap-aurora"`. |
| `slot?` | `LiveSlot` | `"middle"` | Where in the style the layer sits. Default `"middle"`. |
| `beforeId?` | `LiveBeforeId` | not stated | Insert before this layer (or the layer a function returns) instead of the slot's. |

#### `AuroraReport`

*Interface*

```ts
interface AuroraReport
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | `"off" \| "loading" \| "ready" \| "unavailable" \| "now-only"` | required |  |
| `kp` | `number \| null` | required |  |
| `forecastTime` | `string \| null` | required |  |
| `observationTime` | `string \| null` | required |  |
| `label` | `string` | required | `auroraLabel(kp)`: "Kp 4", "Quiet", "Unavailable", or "Now only". |

#### `AuroraSource`

*Interface*

```ts
interface AuroraSource
```

Where the layer reads from, other than the map's gateway.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `jsonUrl` | `string` | required | `latest.json` on a box host, or a `GET /v1/space-weather` URL. |
| `pngUrl?` | `string` | the JSON's `aurora.png`, or `latest.png` beside a box JSON | The grid PNG. Default: the JSON's `aurora.png`, or `latest.png` beside a box JSON. |

#### `EarthEventsFeedReport`

*Interface*

```ts
interface EarthEventsFeedReport
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | `"ok" \| "stale" \| "unknown" \| "error"` | required |  |
| `count` | `number \| null` | required | Quakes and volcano alerts in the answer; fire detections (not cells) for fires. Null before the first answer. |
| `fetchedAt` | `string \| null` | required |  |
| `ageS` | `number \| null` | required |  |
| `label` | `string` | required | "48 today", "6 alerts", "23,499 detections", "Unavailable", "Loading". |

#### `EarthEventsLayerOptions`

*Interface*

```ts
interface EarthEventsLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `types?` | [`EventType[]`](#eventtype) | all three | Which types to draw. Default all three. |
| `bbox?` | `[number, number, number, number] \| "world"` | one fetch, the files are global | `[west, south, east, north]` for the gateway's `bbox`, or `"world"` (default: one fetch, the files are global). |
| `minMagnitude?` | `number` | 2.5 | Smallest quake magnitude, 2.5 to 9. Default 2.5. |
| `pollMs?` | `Partial<Record<EventType, number>>` | 60,000 (quakes), 600,000 (volcanoes), 1,800,000 (fires) | Poll interval per type, milliseconds. Defaults 60,000 (quakes), 600,000 (volcanoes), 1,800,000 (fires); at least 30,000. |
| `base?` | [`EventsBase`](#eventsbase) | not stated | The map ink: `"light"` (default) or `"dark"`. |
| `pulse?` | `boolean` | true | One 6 s ring per quake first seen this session and under 2 h old. Default true; never under reduced motion. |
| `popup?` | `(detail: EventDetail) => string \| HTMLElement` | not stated | A tap opens a popup built from this. Omit for none. |
| `onSelect?` | `(detail: EventDetail \| null) => void` | not stated | Called with the tapped event, or null when a tap hits none (see `clearOnEmptyClick`). |
| `clearOnEmptyClick?` | `boolean` | true | Whether a tap on empty map clears the selection: it calls `onSelect(null)`, drops the quake ring and closes the popup. Default true. Set false and an empty tap does none of those: an open card and its ring stay until the host calls `select(null)` (its close button, Esc). |
| `deferToLayers?` | `string[]` | not stated | Layer ids that win a tap. When one of them has a feature under the tap (a satellite or bus layer, say), the layer ignores the tap entirely: no `onSelect`, no popup change, the ring stays. Ids not on the map are skipped. |
| `onReport?` | `(report: EarthEventsReport) => void` | not stated | Called after every poll and every change of what is drawn. |
| `source?` | [`EarthEventsSource`](#eartheventssource) | not stated |  |
| `baseUrl?` | `string` | not stated |  |
| `apiKey?` | `string` | not stated |  |
| `id?` | `string` | `"mapmap-events"` | Id prefix. Default `"mapmap-events"`. |
| `slot?` | `LiveSlot` | `"middle"` | Where in the style the layer sits. Default `"middle"`. |
| `beforeId?` | `LiveBeforeId` | not stated | Insert before this layer (or the layer a function returns) instead of the slot's. |

#### `EarthEventsReport`

*Interface*

```ts
interface EarthEventsReport
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `quake` | [`EarthEventsFeedReport`](#eartheventsfeedreport) | required |  |
| `volcano` | [`EarthEventsFeedReport`](#eartheventsfeedreport) | required |  |
| `fire` | [`EarthEventsFeedReport`](#eartheventsfeedreport) | required |  |
| `receivedAt` | `number \| null` | required | When the last answer arrived, epoch milliseconds. |
| `error` | `boolean` | required | True when the last poll of any drawn type failed. |

#### `EventIds`

*Interface*

```ts
interface EventIds
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `Record<EventType, string>` | required |  |
| `quakeCore` | `string` | required |  |
| `quakePulse` | `string` | required |  |
| `quakeRing` | `string` | required |  |
| `volcano` | `string` | required |  |
| `fire` | `string` | required |  |
| `fireHeat` | `string` | required |  |
| `icon` | `string` | required |  |
| `layers` | `Record<EventType, string[]>` | required |  |

#### `FieldEncoding`

*Interface*

```ts
interface FieldEncoding
```

`encodings.<field>` in the manifest.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `unit` | `string` | required |  |
| `scale` | `number` | required |  |
| `offset` | `number` | required |  |
| `maplibre` | `{ encoding: "custom"; redFactor: number; greenFactor: number; blueFactor: number; baseShift: number; }` | required |  |

#### `FieldMesh`

*Interface*

```ts
interface FieldMesh
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `vertices` | `Float32Array` | required | Per vertex: mercator x, mercator y, u, v, pole flag (-1 N, 1 S, 0). |
| `indices` | `Uint32Array` | required |  |
| `columns` | `number` | required |  |
| `rows` | `number` | required |  |

#### `FieldStop`

*Interface*

```ts
interface FieldStop
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `value` | `number` | required |  |
| `colour` | `string` | required |  |
| `alpha` | `number` | required |  |

#### `LiveFetchOptions`

*Interface*

```ts
interface LiveFetchOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `apiKey?` | `string \| undefined` | not stated | The API key. Sent as `Authorization: Bearer <key>` under `baseUrl` only. |
| `baseUrl?` | `string \| undefined` | not stated | The gateway origin the key belongs to (the map's `baseUrl`). |
| `signal?` | `AbortSignal \| undefined` | not stated |  |

#### `WeatherFrame`

*Interface*

```ts
interface WeatherFrame
```

One frame of the newest run.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `run` | `string` | required | The run's time (ISO). |
| `hour` | `number` | required | Forecast hour. |
| `validAt` | `string` | required | The valid time (ISO). |
| `key` | `string` | required | The frame's id in layer and source names, e.g. `2026101700-f006`. |

#### `WeatherLayerOptions`

*Interface*

```ts
interface WeatherLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `field?` | [`WeatherField \| null`](#weatherfield) | null: nothing until `setField` | The field to draw. Default null: nothing until `setField`. |
| `isobars?` | `boolean` | false | Draw the pressure lines (with the polar fade). Default false. |
| `resolution?` | `"auto" \| "full" \| "half"` | not stated | `"auto"` (default): half at globe zoom and on touch, full from zoom 4 on a pointer device. |
| `base?` | `"light" \| "dark"` | not stated | The map ink: `"light"` (default) or `"dark"`. |
| `opacity?` | `number` | 1 | Field opacity multiplier, 0 to 1. Default 1. |
| `stops?` | `Partial<Record<WeatherField, FieldStop[]>>` | not stated | Your own colour ramp per field. |
| `prefetch?` | `number` | 2 | Frames installed hidden ahead of the shown one. Default 2. |
| `maxTextures?` | `number` | 6 | Baked frames kept on the GPU. Default 6. |
| `onReport?` | `(report: WeatherReport) => void` | not stated | Called after every change of what is shown or loaded. |
| `onError?` | `(message: string, frameKeys: string[]) => void` | not stated | Called when a frame's file cannot be loaded. |
| `source?` | [`WeatherSource`](#weathersource) | not stated |  |
| `baseUrl?` | `string` | not stated |  |
| `apiKey?` | `string` | not stated |  |
| `id?` | `string` | `"mapmap-weather"` | Id prefix. Default `"mapmap-weather"`. |
| `slot?` | `LiveSlot` | `"middle"` | Where in the style the layer sits. Default `"middle"`. |
| `beforeId?` | `LiveBeforeId` | not stated | Insert before this layer (or the layer a function returns) instead of the slot's. |

#### `WeatherManifest`

*Interface*

```ts
interface WeatherManifest extends Omit<WeatherManifest$1, "models">
```

The manifest as the weather views read it: wind-field.ts's shape plus
the encodings and the field keys. Extends, never replaces, the wind one.

Extends `Omit<WeatherManifest$1, "models">`.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `encodings?` | `Partial<Record<WeatherFieldId, FieldEncoding>>` | not stated |  |
| `models?` | `{ gfs?: { runs?: { run: string; generated_at?: string; frames?: WeatherFrameRef[]; }[]; }; }` | not stated |  |

#### `WeatherReport`

*Interface*

```ts
interface WeatherReport
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | `"loading" \| "ready" \| "unavailable"` | required |  |
| `manifestStatus` | `"ok" \| "stale" \| "unknown"` | required | The manifest's freshness: stale when older than 7 hours. |
| `field` | [`WeatherField \| null`](#weatherfield) | required |  |
| `isobars` | `boolean` | required |  |
| `frames` | [`WeatherFrame[]`](#weatherframe) | required |  |
| `index` | `number` | required | The frame asked for. |
| `playing` | `boolean` | required |  |
| `resolution` | `"full" \| "half" \| null` | required | The resolution on screen, or null when no field is drawn. |
| `failed` | `{ field: WeatherField; frameKey: string; reason: string; } \| null` | required | Set while the hour asked for could not be loaded. |
| `unavailable` | `Partial<Record<WeatherField, string>>` | required | Fields that cannot be drawn for the hour asked for, with plain words. |
| `legend` | `{ unit: string; stops: { value: number; colour: string; alpha: number; }[]; ticks: number[]; } \| null` | required |  |
| `label` | `"Forecast, not an observation"` | required | Always `"Forecast, not an observation"`. |

#### `WeatherSource`

*Interface*

```ts
interface WeatherSource
```

Where the layer reads from, other than the map's gateway.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `manifestUrl` | `string` | required | The manifest, or a `GET /v1/weather/frames` URL. |
| `base` | `string` | required | The base the frame paths resolve against (ends with a slash). |
| `tiles?` | `(gatewayId: string, ext: "png" \| "mvt") => string` | `${baseUrl}/tiles/${id}/{z}/{x}/{y}.${ext}` | The isobars tile source for a gateway tile id. A template with `{z}/{x}/{y}` becomes a vector `tiles` source; anything else (`pmtiles://...`, a TileJSON URL) becomes its `url`. Default: `${baseUrl}/tiles/${id}/{z}/{x}/{y}.${ext}`. |

#### `WindFrameRef`

*Interface*

```ts
interface WindFrameRef
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `hour` | `number` | required |  |
| `valid_at` | `string` | required |  |
| `wind` | `{ png: string; json: string; } \| null` | required |  |
| `half?` | `{ wind?: { png: string; json: string; } \| null; } \| null` | not stated | Manifest v3 (lane 1E, CONTRACT.md 3.2): the 720 x 361 frame set, same orientation and encodings. Optional: absent until the pipeline builds it. |

#### `WindLayerOptions`

*Interface*

```ts
interface WindLayerOptions
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `particles?` | `number` | 65,536, or 16,384 on touch | Particle count; rounded up to a square state texture. Default 65,536, or 16,384 on touch. |
| `fade?` | `number` | 0.94 | Trail fade per frame, 0 to 1. Default 0.94. |
| `base?` | `"light" \| "dark"` | not stated | `"dark"` draws pale particles (the night globe), `"light"` (default) dark ink. |
| `lowPower?` | `boolean` | on touch screens | Cap the animation at about 30 fps. Default: on touch screens. |
| `resolution?` | `"auto" \| "full" \| "half"` | not stated | `"auto"` (default): half-resolution frames at globe zoom and on touch. |
| `fallback?` | `"arrows" \| "none"` | not stated | What to draw under reduced motion or when a shader fails: `"arrows"` (default) or `"none"`. |
| `firstPictureWaitMs?` | `number` | 2500 | How long the first frame waits for the map's first complete picture (`idle`) before it is drawn, in ms. Default 2500. `0` draws it as soon as it is decoded, for a page where the wind is the first picture. The frame downloads during the wait either way. |
| `onReport?` | `(report: WindReport) => void` | not stated |  |
| `onError?` | `(message: string) => void` | not stated |  |
| `source?` | [`WindSource`](#windsource) | not stated |  |
| `baseUrl?` | `string` | not stated |  |
| `apiKey?` | `string` | not stated |  |
| `id?` | `string` | `"mapmap-wind"` | Id prefix. Default `"mapmap-wind"`. |
| `slot?` | `LiveSlot` | `"middle"` | Where in the style the layer sits. Default `"middle"`. |
| `beforeId?` | `LiveBeforeId` | not stated | Insert before this layer (or the layer a function returns) instead of the slot's. |

#### `WindMeta`

*Interface*

```ts
interface WindMeta
```

One wind frame's sidecar (`f<hhh>.json`).

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `source` | `string` | required |  |
| `run` | `string` | required |  |
| `forecast_hour` | `number` | required |  |
| `valid_at` | `string` | required |  |
| `width` | `number` | required |  |
| `height` | `number` | required |  |
| `uMin` | `number` | required |  |
| `uMax` | `number` | required |  |
| `vMin` | `number` | required |  |
| `vMax` | `number` | required |  |
| `label?` | `string` | not stated |  |

#### `WindReport`

*Interface*

```ts
interface WindReport
```

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `status` | `"loading" \| "ready" \| "unavailable"` | required |  |
| `mode` | `"particles" \| "arrows" \| null` | required | `"particles"`, or `"arrows"` for the static fallback; null before a frame is ready. |
| `reason` | `"reduced-motion" \| "gpu" \| null` | required |  |
| `frame` | `{ run: string; hour: number; validAt: string; } \| null` | required |  |
| `resolution` | `"full" \| "half" \| null` | required |  |
| `label` | `"Forecast, not an observation"` | required | Always `"Forecast, not an observation"`. |

#### `WindSource`

*Interface*

```ts
interface WindSource
```

Where the layer reads from, other than the map's gateway.

| Property | Type | Default | Description |
| --- | --- | --- | --- |
| `manifestUrl` | `string` | required | The manifest, or a `GET /v1/weather/frames` URL. |
| `base` | `string` | required | The base the frame paths resolve against (ends with a slash). |

### Types (@mapmap/maps/live)

[`EarthEventsSource`](#eartheventssource), [`EventDetail`](#eventdetail), [`EventsBase`](#eventsbase), [`EventType`](#eventtype), [`WeatherField`](#weatherfield)

#### `EarthEventsSource`

*Type*

```ts
type EarthEventsSource = {
    url: string;
} | {
    files: Record<EventType, string>;
    status: string;
};
```

Where the layer reads from, other than the map's gateway.

#### `EventDetail`

*Type*

```ts
type EventDetail = {
    kind: "quake";
    id: string;
    lng: number;
    lat: number;
    mag: number;
    place: string;
    time: number;
    depthKm: number | null;
    url: string;
} | {
    kind: "volcano";
    id: string;
    lng: number;
    lat: number;
    name: string;
    colorCode: string;
    alertLevel: string;
    sentUtc: string | null;
    noticeUrl: string;
    observatory: string;
    coverage: string;
} | {
    kind: "fire";
    cell: string;
    lng: number;
    lat: number;
    count: number;
    maxFrp: number;
    highConfidenceShare: number;
    acqDate: string;
    label: string;
};
```

What a tap on an event opens. A fire cell always carries `label`; a volcano `coverage`.

#### `EventsBase`

*Type*

```ts
type EventsBase = "light" | "dark";
```

The map ink the quake ring is drawn in.

#### `EventType`

*Type*

```ts
type EventType = "quake" | "volcano" | "fire";
```

The three kinds of event.

#### `WeatherField`

*Type*

```ts
type WeatherField = WeatherFieldId;
```

The four colour fields.

### Constants (@mapmap/maps/live)

[`AURORA_SHORT_CREDIT`](#aurora_short_credit), [`AURORA_STALE_MS`](#aurora_stale_ms), [`DEFAULT_LIVE_LABEL`](#default_live_label), [`EVENT_TYPES`](#event_types), [`EVENTS_SHORT_CREDITS`](#events_short_credits), [`FIELD_PNG_HEIGHT`](#field_png_height), [`FIELD_PNG_WIDTH`](#field_png_width), [`FIRES_HONEST`](#fires_honest), [`VOLCANOES_HONEST`](#volcanoes_honest), [`WEATHER_FIELDS`](#weather_fields), [`WEATHER_SHORT_CREDIT`](#weather_short_credit), [`WIND_PARTICLES_DESKTOP`](#wind_particles_desktop), [`WIND_PARTICLES_TOUCH`](#wind_particles_touch)

#### `AURORA_SHORT_CREDIT`

*Constant*

```ts
const AURORA_SHORT_CREDIT = "Aurora: NOAA SWPC forecast";
```

#### `AURORA_STALE_MS`

*Constant*

```ts
const AURORA_STALE_MS: number;
```

A forecast older than this is not drawn and reads "Unavailable": the
box timer has stopped, and an hour-old forecast is no longer one.

#### `DEFAULT_LIVE_LABEL`

*Constant*

```ts
const DEFAULT_LIVE_LABEL = "Feed status unknown";
```

The label a live layer reports before anything vouches for its feed.

#### `EVENT_TYPES`

*Constant*

```ts
const EVENT_TYPES: readonly EventType[];
```

All three event types, in draw order (bottom to top: fires, quakes, volcanoes).

#### `EVENTS_SHORT_CREDITS`

*Constant*

```ts
const EVENTS_SHORT_CREDITS: Record<EventsFeed, string>;
```

Each source's short credit, carried on its map source (`attribution`),
so the one credit line names it while the layer is on.

#### `FIELD_PNG_HEIGHT`

*Constant*

```ts
const FIELD_PNG_HEIGHT = 721;
```

#### `FIELD_PNG_WIDTH`

*Constant*

```ts
const FIELD_PNG_WIDTH = 1440;
```

A weather colour field drawn from the pipeline's global equirectangular
PNG (lane E, `<field>_png`: 1440 by 721, row 0 at 90 N, column 0 at
180 W, value-encoded 24-bit RGB), on the globe and the flat map, to the
poles. Lane D's primary renderer (coordinator note, 13 Oct 2026); the
PMTiles + color-relief path in weather-layers.ts is the per-frame
fallback.

Why a custom layer: the tiled field costs 36 to 53 range requests and
2.5 to 3.7 s per frame at globe zoom (spike section 5) and smears its top
row into a fan at the pole. One PNG per frame is one request, and a mesh
that runs to the poles covers them.

How:
 - **Bake once per frame.** The PNG is uploaded as bytes (no colour
   management, no premultiply) and one off-screen pass decodes every
   texel with the manifest's formula, `(R*65536 + G*256 + B) * scale +
   offset`, and colours it with the design ramp (the same piecewise-linear
   rule as `color-relief`), into a premultiplied RGBA8 texture with
   mipmaps. Decoding before filtering is the point: filtering the encoded
   bytes would blend the digits of two numbers.
 - **Draw every frame through MapLibre's projection prelude** (tier 1 in
   web-sdk custom-layer-projection.ts, the wind layer's path): a 1.5
   degree mesh in web-mercator 0..1, plus pole vertices flagged the way
   MapLibre flags its own, so `projectTile` puts it on the sphere, the
   flat map, or the blend between them.
 - **Crossfade in one pass.** Up to two frames are bound at once and
   summed by their opacities, so the field never dims mid-fade.

The layer never fetches on the main thread's critical path: the blob is
fetched and decoded (`createImageBitmap`) off it, and only the upload and
the bake (one quad over 1440 by 721) run inside `prerender`.

#### `FIRES_HONEST`

*Constant*

```ts
const FIRES_HONEST = "Satellite-detected heat, not confirmed fires";
```

The honest labels (contract section 8).

#### `VOLCANOES_HONEST`

*Constant*

```ts
const VOLCANOES_HONEST = "US-monitored volcanoes only";
```

#### `WEATHER_FIELDS`

*Constant*

```ts
const WEATHER_FIELDS: readonly WeatherField[];
```

The fields, in the order a control would list them.

#### `WEATHER_SHORT_CREDIT`

*Constant*

```ts
const WEATHER_SHORT_CREDIT = "Weather: NOAA GFS forecast, processed by MapMap";
```

The credit on the map line for everything drawn from NOAA GFS: wind,
rain, temperature and pressure. One string for all of them, so the
credit line carries it once however many weather layers are on. NOAA
asks that processed data is never presented as unaltered, so it says
processed.

#### `WIND_PARTICLES_DESKTOP`

*Constant*

```ts
const WIND_PARTICLES_DESKTOP = 65536;
```

Particle counts by device class (contract section 11).

#### `WIND_PARTICLES_TOUCH`

*Constant*

```ts
const WIND_PARTICLES_TOUCH = 16384;
```
