Documentation menu
@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 inwebsite/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, Maps, tiles & Studio and Live layers.
npm install @mapmap/maps@0.25.1 maplibre-gl
Other SDK references: @mapmap/points, @mapmap/core, @mapmap/tile, @mapmap/react-native, @mapmap/voice, MapMapKit, ai.mapmap:core.
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.
// 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: 441 exports@mapmap/maps/direction-icons: no exports of its own, 7 re-exported from an earlier entry point@mapmap/maps/avatars: 29 exports@mapmap/maps/road-signs: 42 exports@mapmap/maps/asset-markers: 53 exports, plus 26 re-exported from an earlier entry point@mapmap/maps/models: 31 exports@mapmap/maps/tiles: 2 exports@mapmap/maps/live: 65 exports
@mapmap/maps
import { /* … */ } from "@mapmap/maps";
Classes (@mapmap/maps)
AdrCheck, CameraAlertChip, ColourThemeLayer, CreditControl, DataLayer, FlowRouteEffectLayer, Geocoder, GuidanceBanner, InteractionRegistry, IsochroneError, IsochroneLayer, LogoControl, MapMapMap, MarkersLayer, NavigationCamera, PlacesLayer, PositionPuck, ProgramCache, RouteLayer, SearchBox, ThemeScheduler, TransitStopsLayer, TransitVehiclesLayer, VoiceGuidance
AdrCheck
Class
class AdrCheck { … }
Calls POST /adr/check and returns the parsed compliance decision.
Constructor and methods
constructor(options: AdrCheckOptions)check(request: AdrCheckRequest): Promise<AdrCheckResult>
Example
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
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; head units should read 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 withnull).state.speedingdrives the escalated state; the chip itself shows regardless, because the visual alert is unconditional. A kind whose design turnsalertWhileDrivingoff hides the chip: the customer chose not to surface that kind while driving.
Example
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
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): voidonRemove(_map: Map, gl: WebGL2RenderingContext): voidrender(glArg: CustomLayerGl, _args: unknown): void
Example
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
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 | The variant on show. |
Constructor and methods
constructor(attribution: T, options?: CreditOptions)getDefaultPosition(): ChromeCorneronAdd(map: unknown): HTMLElementonRemove(map?: unknown): voidsetTheme(theme: MapBrandTheme): void: Switch between the Day and Night credit (same size and shape; the colour pair follows the ground).
Example
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
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 (orpromoteId); the change survives a theme swap.setFilter(filter: FilterSpecification | null): void: Filter every layer of this DataLayer (nullclears). Survives a theme swap.selected(): GeoJSON.Feature[]: The selected features.select(ids: (string | number)[]): void: Select features by id (feature id, or thepromoteIdvalue).clearSelection(): void: Clear the selection.on(event: DataLayerEventType, handler: (e: DataLayerEvent) => void): () => void: Listen forclick,hoverorselect. Returns an unsubscribe function.remove(): void: Remove the source, its layers, the popup and every listener.
Example
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
class FlowRouteEffectLayer implements CustomLayerInterface { … }
A MapLibre custom layer (CustomLayerInterface) drawing the flowing
route ribbon. Construct via 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): voidonRemove(_map: Map, gl: WebGL2RenderingContext): voidrender(gl: CustomLayerGl, args: unknown): void
Example
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
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
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
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 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 withnull).stepis icon context: pass theStepGuidanceentry the banner came from. ItsbannerIconis 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. WithoutbannerIcon(a hand-built{ mode, drivingSide }), the glyph comes from the banner's own type and modifier with thatdrivingSide; with no side a roundabout draws the plain turn glyph for its direction.
Example
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
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 interactionid.remove(id: string): void: Remove the interactionid. 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 behindtarget: a featureset sets it once per distinct source and source layer (buildingandbuilding-3dshare 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
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
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. | |
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
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
class IsochroneLayer { … }
Renders reachability rings from the gateway's POST /isochrone.
| Property | Type | Default | Description |
|---|---|---|---|
readonly current | IsochroneFeatureCollection | undefined | 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 acontourproperty 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'sstyle.loadlistener and any pending wait for the style.
Example
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
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 | The variant on show. |
Constructor and methods
constructor(options?: LogoOptions)getDefaultPosition(): LogoPositiononAdd(): HTMLElementonRemove(): voidsetTheme(theme: MapBrandTheme): void: Switch between the Day and Night mark.
Example
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
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 | 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 | 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 | 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.falseremoves the DEM sources and any hillshade layer this SDK added. -
setRouteEffect(effect: RouteEffectName | null, options?: SetRouteEffectOptions): voidSet (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.tsconst 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 lineGeometry: pass
{ geometry }explicitly, or omit it and the effect attaches to whatever route aRouteLayeron this map draws (current and future - RouteLayer reports every drawn line viasetRouteEffectGeometry).Styles whose theme carried an
effectsblock auto-enable their effect from the compiled style's metadata; calling this method (with any value, includingnull) 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 withnull).RouteLayercalls this on everydraw()/clear(), so apps normally never do - passgeometrytosetRouteEffectfor routes drawn outside a RouteLayer. -
setBuildings3d(enabled: boolean): void: Toggle 3D building extrusions at runtime, the live equivalent of compiling the style withbuildings_3d: true(seeTheme): adds or removes thebuilding-3dfill-extrusion layer and hands the flatbuildingfootprint layer off at zoom 15. The extrusion takes its colour from the currentbuildingfill, so it follows the theme. Idempotent; safe to call before the style has loaded (it applies on load). Requires the MapMap skeleton'sbuildinglayer, 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 StudioThemedocument, a fullStyleSpecificationor a style URL, the same values thestyleoption 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.navDesignandpoiDesignstay as they were read at construction. -
slot(slot: Slot): string | undefinedThe
beforeIdfor an insertion slot on the live style, orundefinedto append on top. SeeSLOT_ANCHORSfor what each slot means.tsmap.map.addLayer(myLayer, map.slot("bottom"));A layer added this way is gone after the next
setStyle; useaddPersistentLayerfor one that stays. -
addPersistentSource(id: string, source: SourceSpecification): void: Add a source that survivessetStyle. Calling it again with the same id replaces the registered source: a GeoJSON source swaps its data in place (setData); any other change needsremovePersistentfirst, because MapLibre cannot change a source's type or URL under the layers drawing it. -
addPersistentLayer(layer: LayerSpecification, opts?: PersistentLayerOptions): voidAdd 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.tsmap.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 labelsThe 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: Runinteraction.handleron clicks or hovers over a layer, a basemap featureset (poi,place-labels,buildings) or the map itself. Shaped like Mapbox GL JSaddInteraction; a handler returningtruestops the event reaching interactions further down. Adding an id that exists replaces it. Interactions survivesetStyle. -
removeInteraction(id: string): void: Remove the interactionid. 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 onpoiandplace-labelswithoutpromoteId. Onbuildingsit 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) whileloadonly ever fires once, so this waits onidletoo 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
setColorThemeequivalent.nullremoves it.tsawait 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 aftersetStyle. Strength 0 removes the layer (no identity pass), whilegetColorTheme()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 inmetadata["mapmap:color-theme"]) and strength, ornullwith none. -
addClip(id: string, opts: ClipOptions): voidHide the basemap's buildings (or POI labels) inside a polygon, the Mapbox
cliplayer equivalent. Works on its own, with no model:tsmap.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
distancefilter, which MapLibre GL JS and Native both evaluate). Clips survivesetStyle, apply tobuilding-3dwhensetBuildings3d(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
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
class MarkersLayer { … }
Draws a theme's custom markers & labels on a MapLibre map.
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[] | The layer's current markers. | |
readonly ids | 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 (undefinedor 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 whateveritemshappens to hold now, deriving them from the items meantsetMarkers(undefined)followed byclear()/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
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
class NavigationCamera { … }
A follow-mode chase camera for MapMap maps.
| Property | Type | Default | Description |
|---|---|---|---|
readonly mode | NavigationCameraMode | The camera's current behaviour: "follow", "overview" or "free". |
Constructor and methods
static isSupported(map: MapMapMap | Map): boolean:falsewhen 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 aPositionPuck: everyfollowcall alsosetLocations the puck, so camera and puck never diverge.follow(fix: CameraFix, courseDeg?: number): void: Glide the camera to a fix, course-up whencourseDegis given (bearing is left unchanged when it isundefined). Each call eases with linear timing overeaseMscapped 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 forresume.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
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
class PlacesLayer { … }
Draws customer places on a MapLibre map, with clustering and popups.
| Property | Type | Default | Description |
|---|---|---|---|
readonly current | Place[] | The layer's current places (normalised to Place[]). | |
readonly ids | 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 - aPlace[]or a GeoJSON FeatureCollection of Points. Never silently drops an update: once the source exists the data is applied immediately, even whileisStyleLoaded()is transientlyfalsemid-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 latersetPlacesor style swap needed. WithfitBounds: truethe 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 configuredpopupat the place (popup: falseto skip, no-op without apopupoption) and eases the camera to it (flyTo: falseto skip;zoomto also zoom). Returns the selected place, orundefinedfor an unknown id (in which case nothing happens). Does NOT invokeonPlaceClick- a programmatic selection is not a user click.deselect(): void: Close the popup opened byselector a place click, if any.nearest(origin: { lat: number; lon: number; }, n?: number): PlaceWithDistance[]: The nearestnplaces (default1) tooriginby straight-line (haversine) distance, each withdistanceMattached. Pure and instant - no network. For road-network answers usenearestByDriveTime.nearestByDriveTime(origin: { lat: number; lon: number; }, options?: NearestByDriveTimeOptions): Promise<PlaceWithDriveTime[]>: The nearestnplaces (default1) tooriginby drive time, via the gateway'sPOST /matrix(one source, all places as targets). Resolves to places sorted bydurationSascending, with the drivendistanceMattached; unreachable places (nullduration) are dropped. Needs the gatewaybaseUrl/apiKey- picked up from aMapMapMapautomatically, or pass them as layer options. Answers always describe the places that were asked about: the list is snapshotted at call time, so asetPlaceslanding 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
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
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 firstsetLocation). The design defaults to the map'snavDesign.puckwhen given aMapMapMapwhose theme carried anextra.navblock, 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).headingDegrotates 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).setLocationre-adds it.
Example
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
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): Tdelete(gl: WebGL2RenderingContext, dispose: (t: T) => void): void: Dispose of every entry and empty the cache (call fromonRemove).
Example
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
class RouteLayer { … }
Draws MapMap routes on a MapLibre map.
| Property | Type | Default | Description |
|---|---|---|---|
readonly ids | 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 | The most recently drawn route, if any. | |
readonly currentWaypoints | [number, number][] | The intermediate stops currently drawn, [lng, lat] each. | |
readonly alternativeRoutes | readonly 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 fromfromtoto(with optional intermediateviapoints passed throughoptionsis not supported here - pass a coordinate list toroutePathfor 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.clearlikewise 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.routePathfills 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 todraw). An empty array clears them.drawGeometry(coordinates: [number, number][]): void: Draw a bare line from coordinates, without a parsed OSRM route.RouteLayer.drawexpects aParsedRoutebecause it is normally fed byRouteLayer.route. Callers that already have geometry and nothing else, an agent tool result, a stored polyline, a hand-built preview, had to invent aParsedRoutewith zeroed distance and duration thatdrawnever 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 ofRouteLayer.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 todraw, 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 toRouteLayer.drawAlternatives, so a caller can re-issue that call with the new selection. Passingundefinedremoves 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, becauseline-dasharraycannot 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 toprogressColor(the "vanishing route line");0restores the untinted line. The value is remembered acrossdrawcalls 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 atlngLatrotated tobearingDeg(clockwise from north). Survives style swaps untilclearManeuver.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'sstyle.loadlistener and any pending wait for the style. Call when disposing of the layer (the map itself is left untouched).
Example
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
class SearchBox { … }
A MapLibre IControl search bar: debounced suggestions, keyboard
navigation, and a typed "select" event carrying the picked feature.
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: TheIControlhook: build the DOM and take the map handle.onRemove(): void: TheIControlhook: tear everything down.
Example
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
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
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
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
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
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 | The map this layer draws on (for companion layers such as the 3D buses). | |
readonly defaultVehicleType | BusVehicleType | 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 lastTransitFeedReport).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, ornullto stop. Its feature carriesfollowed: true; withmarker: "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 tosetFollowed, ornull.onDraw(listener: (vehicles: readonly TransitDrawnVehicle[]) => void): () => void: Calllistenerwith 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), byfactor(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
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
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 itsutteranceIdappears; 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
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, alertChipContent, alertContrastIssues, alertIconSvg, alertIconUrl, alertPresentation, alertSoundUrl, alertSpeedReadoutColor, applyColourTheme, applyGeoJSONDiff, applyPoiDesign, applyTerrain, bannerLanes, baseTheme, bearingBetween, bindFlythroughToScroll, buildAdrCheckBody, buildGeocodeUrl, buildLut, buildProbeUrl, buildRetrieveUrl, buildReverseGeocodeUrl, buildRouteQuery, buildRouteUrl, buildStyle, buildSuggestUrl, buildVertexSource, builtInPoiColor, busMarkerLayers, busPuckImage, busPuckImageOptions, busSilhouetteSdf, busSpriteBody, busTickImage, busVehicleType, cameraKindLabel, cameraKindsShownOnMap, cameraShownOnMap, cameraSpriteName, cameraSymbolLayer, capTransitBbox, checkSourceUrl, clipFilter, colourThemeMetadata, contrastRatio, createMap, createRouteEffect, creditTier, dataLayerColor, defaultNavAlertsDesign, defaultNavDesign, defaultPoiDesign, directionArrow, directionIconSvg, effectsFromStyleMetadata, extractGuidance, featuresetLayerIds, fixSetFromCollection, flythrough, flythroughPose, formatCoord, formatCoords, globeModelMatrix, gradeColourString, gradeStyleColours, hasBakedMarkers, haversineDistanceM, holdLayersOnTop, iconNameForManeuver, iconNameForStep, iconNamesForSteps, interpolateFixes, isMarkerGlyphId, isNameTextField, isSlot, keyedStyleUrl, keyedTilesUrl, laneArrowDirection, languageTextField, layersOnTopMoves, liveTargetLayerIds, lngLatToMercator, locateProjectionUniforms, lutApply, lutDigest, lutFromBase64, lutFromImageData, lutFromPngData, lutToBase64, lutToImageData, mapBrandSvg, mapGroundTheme, markerImage, markerImageDataKey, markerImageKey, markersFromTheme, markersFromThemeUrl, markerText, markerTextImageKey, mercatorModelMatrix, modelClipMatrices, multiplyMat4, namedBase, navAlertLeadDistanceM, navAlertSoundUrl, navCameraKind, navDesignFromTheme, navDesignFromThemeUrl, osmAndMoreCredit, parseCssColour, parseGeocodeResponse, parseHexColor, parseMarkers, parseNavAlertsDesign, parseNavDesign, parseOsrmRoute, parsePoiDesign, parseSuggestResponse, placesFromGeoJSON, poiDesignFromTheme, poiDesignFromThemeUrl, poiDesignIsDefault, poiTextColorExpression, prefersReducedMotion, projectionVariant, readProjection, registerPmtilesProtocol, relativeLuminance, removeTerrain, renderClockMs, renderMarkerImage, resetDiagnostics, resolveBaseId, resolveSlot, resolveTerrain, resolveTheme, runMapDiagnostics, setMapLanguage, setProjectionUniforms, severityProsody, shortCredit, shortestArcDeg, shortestArcDelta, speak, sphereFrame, sphereSurface, ssmlToText, styleFromBase, styleGroundTheme, subdivideMercator, sunTimes, supportsTerrainDraping, territoryTilesSourceUrl, tessellateRouteRibbon, tidyPlaceName, toLngLat, toPmtilesUrl, transitArchiveUrl, transitFreshnessLabel, transitStopFromProperties, transitTileJsonUrl, transitUrlsFromIndex, truncateChars, uploadProbeBatch, validateColourTheme, validateUseTheme
addBusMarkerImages
Function
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
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 | required | |
alert | CameraAlert | required |
Options and types: NavAlertsDesign, CameraAlert.
alertContrastIssues
Function
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 | required |
Options and types: NavAlertsDesign.
alertIconSvg
Function
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 is what actually resolves one.
| Parameter | Type | Default | Description |
|---|---|---|---|
design | NavAlertsDesign | required | |
kind | NavCameraKind | string | required |
Options and types: NavAlertsDesign, NavCameraKind.
alertIconUrl
Function
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 | required | |
kind | NavCameraKind | string | required |
Options and types: NavAlertsDesign, NavCameraKind.
alertPresentation
Function
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 | required | |
alert | CameraAlert | required | |
state? | { speeding?: boolean; } | not stated |
Options and types: NavAlertsDesign, CameraAlert.
alertSoundUrl
Function
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 | required | |
escalated | boolean | required |
Options and types: NavAlertsDesign.
alertSpeedReadoutColor
Function
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 | required | |
escalated | boolean | required |
Options and types: NavAlertsDesign.
applyColourTheme
Function
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.
applyGeoJSONDiff
Function
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
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 | required | |
fallbackTextColor? | string | not stated |
Options and types: PoiDesign.
applyTerrain
Function
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
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 | required |
Options and types: BannerInstruction.
baseTheme
Function
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
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
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.
const replay = flythrough(map, route);
const unbind = bindFlythroughToScroll(replay, document.querySelector("#story")!);
| Parameter | Type | Default | Description |
|---|---|---|---|
controller | FlythroughController | required | |
target? | HTMLElement | Window | not stated |
Options and types: FlythroughController.
buildAdrCheckBody
Function
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 | required |
Options and types: AdrCheckRequest.
buildGeocodeUrl
Function
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.
| Parameter | Type | Default | Description |
|---|---|---|---|
baseUrl | string | required | |
query | string | required | |
options? | GeocodeOptions | not stated |
Options and types: GeocodeOptions.
buildLut
Function
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
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
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
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 | required | |
options? | ReverseGeocodeOptions | not stated |
Options and types: LngLatLike, ReverseGeocodeOptions.
buildRouteQuery
Function
function buildRouteQuery(truck?: TruckParams, guidance?: GuidanceQuery): string;
Build the OSRM query string (without the leading ?) for a request.
| Parameter | Type | Default | Description |
|---|---|---|---|
truck? | TruckParams | not stated | |
guidance? | GuidanceQuery | not stated |
Options and types: TruckParams.
buildRouteUrl
Function
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 | required | |
points | LngLatLike[] | required | |
truck? | TruckParams | not stated | |
guidance? | GuidanceQuery | not stated |
Options and types: RouteProfile, LngLatLike, TruckParams.
buildStyle
Function
function buildStyle(options?: BuildStyleOptions): StyleSpecification;
| Parameter | Type | Default | Description |
|---|---|---|---|
options? | BuildStyleOptions | not stated |
Options and types: BuildStyleOptions.
buildSuggestUrl
Function
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.
| Parameter | Type | Default | Description |
|---|---|---|---|
gatewayUrl | string | required | |
query | string | required | |
options? | SuggestUrlOptions | not stated |
Options and types: SuggestUrlOptions.
buildVertexSource
Function
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 | required | |
body | string | required |
Options and types: ProjectionArgs.
builtInPoiColor
Function
function builtInPoiColor(categoryId: string): string | undefined;
The built-in dot colour for a category id.
| Parameter | Type | Default | Description |
|---|---|---|---|
categoryId | string | required |
busMarkerLayers
Function
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
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).
| Parameter | Type | Default | Description |
|---|---|---|---|
size? | number | not stated | |
options? | { followed?: boolean; } | not stated |
busPuckImageOptions
Function
function busPuckImageOptions(size?: number, options?: {
followed?: boolean;
}): Partial<StyleImageMetadata>;
addImage options for a 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
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 | not stated |
Options and types: BusVehicleType.
busSpriteBody
Function
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 | not stated |
Options and types: BusVehicleType.
busTickImage
Function
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
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
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.
cameraKindsShownOnMap
Function
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); an empty array means "draw nothing", which
a caller must honour by removing or hiding the layer.
| Parameter | Type | Default | Description |
|---|---|---|---|
design | NavAlertsDesign | required | |
mode? | MapDisplayMode | not stated |
Options and types: NavAlertsDesign, MapDisplayMode.
cameraShownOnMap
Function
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 | required | |
kind | NavCameraKind | string | required | |
mode? | MapDisplayMode | not stated |
Options and types: NavAlertsDesign, NavCameraKind, MapDisplayMode.
cameraSpriteName
Function
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.
cameraSymbolLayer
Function
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 or any wire spelling 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 | required | |
source | string | required | GeoJSON source id holding the camera features. |
mode? | 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, MapDisplayMode.
capTransitBbox
Function
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
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
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
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 | required | |
lut | LutBytes | required |
Options and types: ColourTheme, LutBytes.
contrastRatio
Function
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
function createMap(options: MapMapOptions): MapMapMap;
Functional alias for MapMapMap, mirroring the Mapbox new Map feel.
| Parameter | Type | Default | Description |
|---|---|---|---|
options | MapMapOptions | required |
Options and types: MapMapOptions.
Example
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
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 | required | |
geometry | RouteGeometry | required | |
options? | RouteFlowOptions | not stated |
Options and types: RouteEffectName, RouteGeometry, RouteFlowOptions.
Example
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
function creditTier(room: number, fullWidth: number, shortWidth: number): CreditTier;
The CreditTier 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
function dataLayerColor(style?: DataLayerStyle): string | ExpressionSpecification;
The colour expression for style.
| Parameter | Type | Default | Description |
|---|---|---|---|
style? | DataLayerStyle | not stated |
Options and types: DataLayerStyle.
defaultNavAlertsDesign
Function
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
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
function defaultPoiDesign(): PoiDesign;
No overrides: every category keeps its built-in dot and label colours.
directionArrow
Function
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
function directionIconSvg(type?: string, modifier?: string, drivingSide?: string): string;
Inline SVG markup for an OSRM-shaped maneuver (see iconNameForManeuver).
| Parameter | Type | Default | Description |
|---|---|---|---|
type? | string | not stated | |
modifier? | string | not stated | |
drivingSide? | string | not stated |
effectsFromStyleMetadata
Function
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
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 | required |
Options and types: ParsedRoute.
featuresetLayerIds
Function
function featuresetLayerIds(id: FeaturesetId): readonly string[];
The layer ids a featureset covers (all of them, present or not).
| Parameter | Type | Default | Description |
|---|---|---|---|
id | FeaturesetId | required |
Options and types: FeaturesetId.
fixSetFromCollection
Function
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
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().
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 | not stated |
Options and types: MapMapMap, RouteGeometry, ParsedRoute, FlythroughOptions.
Example
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
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
function formatCoord(point: LngLatLike): string;
Format one point as an OSRM lon,lat coordinate.
| Parameter | Type | Default | Description |
|---|---|---|---|
point | LngLatLike | required |
Options and types: LngLatLike.
formatCoords
Function
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[] | required |
Options and types: LngLatLike.
globeModelMatrix
Function
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
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 | required | |
value | string | required | |
strength? | number | not stated |
Options and types: LutBytes.
gradeStyleColours
Function
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'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 matches instead.
Returns a new style; the input is not modified.
| Parameter | Type | Default | Description |
|---|---|---|---|
style | StyleSpecification | required | |
lut | LutBytes | required | |
opts? | GradeStyleOptions | not stated |
Options and types: LutBytes, GradeStyleOptions.
hasBakedMarkers
Function
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 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.
haversineDistanceM
Function
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
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
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
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
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 instead, a
roundabout's icon cannot be resolved from one step alone.
| Parameter | Type | Default | Description |
|---|---|---|---|
step | DirectionIconStep | required |
Options and types: DirectionIconStep.
iconNamesForSteps
Function · also exported from @mapmap/maps/direction-icons
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 untouched.
| Parameter | Type | Default | Description |
|---|---|---|---|
steps | readonly DirectionIconStep[] | required |
Options and types: DirectionIconStep.
interpolateFixes
Function
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.
Options and types: FixSet.
isMarkerGlyphId
Function
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
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
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
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
function keyedTilesUrl(apiKey: string, baseUrl?: string): string;
The keyed basemap source URL: the gateway's TileJSON for
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
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 | required |
Options and types: LaneIndication.
languageTextField
Function
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 gets 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
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
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 | required |
Options and types: InteractionTarget.
lngLatToMercator
Function
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
function locateProjectionUniforms(gl: WebGL2RenderingContext, program: WebGLProgram): Record<ProjectionUniform, WebGLUniformLocation | null>;
Uniform locations for 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
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 | required | |
rgb | readonly [number, number, number] | required | |
strength? | number | not stated |
Options and types: LutBytes.
lutDigest
Function
function lutDigest(lut: LutBytes): string;
FNV-1a 64-bit of the LUT bytes, as 16 lowercase hex characters.
| Parameter | Type | Default | Description |
|---|---|---|---|
lut | LutBytes | required |
Options and types: LutBytes.
lutFromBase64
Function
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
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 | required |
Options and types: LutImageData.
lutFromPngData
Function
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
function lutToBase64(lut: LutBytes): string;
Encode a LUT as standard padded base64.
| Parameter | Type | Default | Description |
|---|---|---|---|
lut | LutBytes | required |
Options and types: LutBytes.
lutToImageData
Function
function lutToImageData(lut: LutBytes): {
width: number;
height: number;
data: Uint8ClampedArray;
};
The inverse of 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 | required |
Options and types: LutBytes.
mapBrandSvg
Function
function mapBrandSvg(theme: MapBrandTheme): string;
The composed v2/MapBrand SVG for one theme.
| Parameter | Type | Default | Description |
|---|---|---|---|
theme | MapBrandTheme | required |
Options and types: MapBrandTheme.
mapGroundTheme
Function
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 | required | |
whenUnreadable | MapBrandTheme | required |
Options and types: GroundReadableMap, MapBrandTheme.
markerImage
Function
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
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
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 | required | |
colour | string | required | |
size | MarkerSize | required |
Options and types: MarkerGlyphId, MarkerSize.
markersFromTheme
Function
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.
| Parameter | Type | Default | Description |
|---|---|---|---|
theme | Theme | undefined | null | required |
Options and types: Theme.
markersFromThemeUrl
Function
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:
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
function markerText(value: unknown): string | undefined;
Normalise a short pin text: trimmed and cut to
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
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 so glyph pins keep their existing keys unchanged.
| Parameter | Type | Default | Description |
|---|---|---|---|
text | string | required | |
colour | string | required | |
size | MarkerSize | required |
Options and types: MarkerSize.
mercatorModelMatrix
Function
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
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 | required | |
lngLat | [number, number] | required | |
altitudeM | number | required |
Options and types: ProjectionArgs.
multiplyMat4
Function
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
function namedBase(id: string): MapMapBase | undefined;
The catalogue entry for an id, following BASE_ID_ALIASES.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | string | required |
navAlertLeadDistanceM
Function
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
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
function navCameraKind(value: unknown): NavCameraKind;
Normalises a camera kind from the wire (or any host classifier) onto a
NavCameraKind. 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
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 | required |
Options and types: Theme.
navDesignFromThemeUrl
Function
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:
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);
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
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
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
function parseGeocodeResponse(body: unknown): GeocodeHit[];
Parse a geocode response body (either endpoint) into GeocodeHits.
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
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
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
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
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
function parseOsrmRoute(body: unknown): ParsedRoute;
Parse an OSRM route response into a 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
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
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
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 | required |
Options and types: PlacesFeatureCollection.
poiDesignFromTheme
Function
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 | required |
Options and types: Theme.
poiDesignFromThemeUrl
Function
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.
| Parameter | Type | Default | Description |
|---|---|---|---|
url | string | required | |
fetchImpl? | typeof fetch | not stated | Optional fetch replacement (tests, Node polyfills). |
poiDesignIsDefault
Function
function poiDesignIsDefault(design: PoiDesign): boolean;
True when the design changes nothing.
| Parameter | Type | Default | Description |
|---|---|---|---|
design | PoiDesign | required |
Options and types: PoiDesign.
poiTextColorExpression
Function
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
servicesfallback arm - never inherit another category's colour. LOCKSTEP: mirrorspoiTextColorExpressioninwebsite/lib/studio-poi.ts.
| Parameter | Type | Default | Description |
|---|---|---|---|
design | PoiDesign | required | |
fallback | string | required |
Options and types: PoiDesign.
prefersReducedMotion
Function
function prefersReducedMotion(): boolean;
Whether the user has asked for reduced motion. Safe without a DOM.
projectionVariant
Function
function projectionVariant(args: ProjectionArgs): ProjectionVariant;
Which projection the frame is drawn with; unknown names read as mercator.
| Parameter | Type | Default | Description |
|---|---|---|---|
args | ProjectionArgs | required |
Options and types: ProjectionArgs.
readProjection
Function
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
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 | not stated |
Options and types: ProtocolHost.
Example
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
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
function removeTerrain(map: Map): void;
Turn terrain off and remove what applyTerrain added.
| Parameter | Type | Default | Description |
|---|---|---|---|
map | Map | required |
renderClockMs
Function
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
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 | not stated |
Options and types: RenderMarkerImageOptions.
resetDiagnostics
Function
function resetDiagnostics(): void;
Reset the once-per-page dedup - for tests (and hot-reload tooling).
resolveBaseId
Function
function resolveBaseId(id: string): MapMapBaseId | undefined;
Resolve an id to its canonical form, following BASE_ID_ALIASES.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | string | required |
resolveSlot
Function
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.
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 | required |
Options and types: Slot.
resolveTerrain
Function
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.
resolveTheme
Function
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
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 | required |
Options and types: MapDiagnosticsInput.
setMapLanguage
Function
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.
setProjectionUniforms
Function
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 | required |
Options and types: ProjectionUniform, ProjectionArgs.
severityProsody
Function
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 | required | |
baseRate? | number | not stated |
Options and types: GuidanceSeverity.
shortCredit
Function
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
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
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
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 | required | |
options? | SpeakOptions | not stated |
Options and types: VoiceInstruction, SpeakOptions.
sphereFrame
Function
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
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
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. &lt;, the gateway's
escape of a literal <, round-trips correctly. Decodes the five
named XML entities plus decimal numeric references (').
| Parameter | Type | Default | Description |
|---|---|---|---|
ssml | string | required |
styleFromBase
Function
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.
Example
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
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 | required |
Options and types: MapBrandTheme.
subdivideMercator
Function
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
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
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
function territoryTilesSourceUrl(options?: Pick<BuildStyleOptions, "territoryTilesUrl" | "apiKey" | "baseUrl">): string;
The URL a compiled style's territory source points at.
- An explicit
territoryTilesUrlalways wins (pmtiles://added when missing), keyed or not. - Otherwise, with an
apiKey, the keyed gateway TileJSON (keyedTilesUrl), which works from any website. - Otherwise the keyless
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.
tessellateRouteRibbon
Function
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 (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
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
function toLngLat(point: LngLatLike): [number, number];
Normalise any accepted shape to a [lng, lat] tuple.
| Parameter | Type | Default | Description |
|---|---|---|---|
point | LngLatLike | required |
Options and types: LngLatLike.
toPmtilesUrl
Function
function toPmtilesUrl(url: string): string;
Ensure a tiles URL carries the pmtiles:// protocol prefix.
| Parameter | Type | Default | Description |
|---|---|---|---|
url | string | required |
transitArchiveUrl
Function
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
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 | required | |
ageS | number | null | required |
Options and types: TransitFeedStatus.
transitStopFromProperties
Function
function transitStopFromProperties(props: Record<string, unknown>): TransitStopFeature;
The clicked feature's properties as a TransitStopFeature.
| Parameter | Type | Default | Description |
|---|---|---|---|
props | Record<string, unknown> | required |
transitTileJsonUrl
Function
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
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
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
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 | not stated |
Options and types: UploadProbeOptions.
validateColourTheme
Function
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
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, AdrCheckRequest, AdrCheckResult, AdrDimensions, AlertChipContent, AlertContrastIssue, AlertPresentation, BannerComponent, BannerContent, BannerInstruction, BuildStyleOptions, CameraAlert, CameraFix, ClipMatrices, ClipOptions, ColorThemeSpec, ColourTheme, ColourThemeLayerOptions, CreditInnerControl, CreditOptions, DataLayerCategories, DataLayerClusterOptions, DataLayerEvent, DataLayerExtrusionOptions, DataLayerGraduated, DataLayerOptions, DataLayerSimpleStyle, DataLayerStyle, DirectionIconStep, EndpointMarker, EndpointsDesign, FlythroughController, FlythroughOptions, FlythroughPose, GeocodeHit, GeocodeOptions, GeocoderOptions, GradeStyleOptions, GroundReadableMap, GuidanceSpokenPrompt, Interaction, InteractionEvent, InteractionTarget, IsochroneFeatureCollection, IsochroneLayerOptions, LaneIndication, LayerOverride, LogoOptions, LutImageData, MapDiagnosticsInput, MapMapBase, MapMapOptions, MarkerItem, MarkersBlock, MarkersLayerIds, MarkersLayerOptions, NavAlertKindDesign, NavAlertsDesign, NavBannerDesign, NavCameraDesign, NavDesign, NavigationCameraOptions, NavPuckDesign, NavRouteDesign, NearestByDriveTimeOptions, ParsedRoute, PersistentIds, PersistentLayerOptions, Place, PlacePointFeature, PlacesFeatureCollection, PlacesIcon, PlacesLabelOptions, PlacesLayerIds, PlacesLayerOptions, PlacesSelectOptions, PoiCategoryDesign, PoiDesign, PositionPuckOptions, ProjectionArgs, Rendered, RenderedMarkerImage, RenderMarkerImageOptions, ReverseGeocodeOptions, RibbonMesh, RouteEffectLayer, RouteFlowOptions, RouteGeometry, RouteLayerIds, RouteLayerOptions, RouteLineStyle, RouteOptions, SearchBoxMap, SearchBoxOptions, SearchBoxSelectEvent, SearchSuggestion, SetRouteEffectOptions, SeverityProsody, ShowReachabilityOptions, SnappedOrigin, SpeakOptions, StepGuidance, SuggestUrlOptions, TerrainOptions, Theme, ThemeEffects, ThemeSchedulerOptions, TransitFeedAnswer, TransitFeedReport, TransitFix, TransitStopFeature, TransitStopsLayerOptions, TransitVehicle, TransitVehiclesLayerOptions, TruckParams, UploadProbeOptions, VoiceGuidanceOptions, VoiceGuidanceUpdate, VoiceInstruction
AdrCheckOptions
Interface
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
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 | the EU standard artic maximums | Vehicle dimensions. Defaults to the EU standard artic maximums. |
tunnelCategory | AdrTunnelCategory | required | The ADR category of the tunnel to check, "A" … "E". |
AdrCheckResult
Interface
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
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
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 | 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
interface AlertContrastIssue
A chip colour pair that falls below 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
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 | 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
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).
| 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
interface BannerContent
One banner line.
| Property | Type | Default | Description |
|---|---|---|---|
text | string | required | |
type? | string | not stated | |
modifier? | string | not stated | |
components? | BannerComponent[] | not stated |
BannerInstruction
Interface
interface BannerInstruction
The visual banner shown while travelling a step.
| Property | Type | Default | Description |
|---|---|---|---|
distanceAlongGeometry | number | required | |
primary | BannerContent | required | |
secondary? | BannerContent | not stated | |
sub? | BannerContent | not stated |
BuildStyleOptions
Interface
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 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) 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 | Gateway for the keyed tiles. Defaults to 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
interface CameraAlert
One camera alert to present. kind is normalised through
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
interface CameraFix
A GPS fix to follow.
| Property | Type | Default | Description |
|---|---|---|---|
lat | number | required | |
lon | number | required |
ClipMatrices
Interface
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
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 vertices in total. |
types? | 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 | 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. |
ColorThemeSpec
Interface
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
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
interface ColourThemeLayerOptions
Options for ColourThemeLayer.
| Property | Type | Default | Description |
|---|---|---|---|
id? | string | "mapmap-colour-theme" | Layer id. Default "mapmap-colour-theme". |
lut | LutBytes | required | The LUT, LUT_BYTES bytes (see colour-theme.ts). |
strength? | number | not stated | 0..1, default 1. |
CreditInnerControl
Interface
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): HTMLElementonRemove(map?: unknown): voidgetDefaultPosition?(): unknown
CreditOptions
Interface
interface CreditOptions
Options for 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 | not stated | Initial variant. MapMapMap keeps it in step with the drawn style; default day. |
DataLayerCategories
Interface
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
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
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
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
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
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 | "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 | not stated | |
extrusion? | DataLayerExtrusionOptions | height property height | Required for mode: "extrusion" (default height property height). |
cluster? | 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 | "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
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
interface DataLayerStyle
Styling: categories wins over graduated, which wins over simple.
| Property | Type | Default | Description |
|---|---|---|---|
simple? | DataLayerSimpleStyle | not stated | |
categories? | DataLayerCategories | not stated | |
graduated? | DataLayerGraduated | not stated |
DirectionIconStep
Interface · also exported from @mapmap/maps/direction-icons
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
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 | 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 | 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
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
interface FlythroughController
The handle a 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 progresst(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
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
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
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
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 | 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
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
interface GradeStyleOptions
Options for 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
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?(): unknowngetLayer(id: string): { type?: string; } | undefinedgetPaintProperty(id: string, name: string): unknowngetLayoutProperty(id: string, name: string): unknown
GuidanceSpokenPrompt
Interface
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
interface Interaction
One interaction, as passed to MapMapMap.addInteraction.
| Property | Type | Default | Description |
|---|---|---|---|
type | InteractionType | required | |
target? | 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
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
interface InteractionTarget
What an interaction listens on: one layer, or a featureset.
| Property | Type | Default | Description |
|---|---|---|---|
layerId? | string | not stated | |
featuresetId? | FeaturesetId | not stated |
IsochroneFeatureCollection
Interface
interface IsochroneFeatureCollection
The GeoJSON FeatureCollection the gateway returns, passed through.
| Property | Type | Default | Description |
|---|---|---|---|
type | "FeatureCollection" | required | |
snapped_origin? | 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
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
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). |
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. |
LayerOverride
Interface
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 | 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
interface LogoOptions
Options for LogoControl.
| Property | Type | Default | Description |
|---|---|---|---|
position? | 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 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 | not stated | Initial variant: day (default) for light maps, night for dark. MapMapMap sets and updates this itself from the drawn style. |
LutImageData
Interface
interface LutImageData
The shape of ImageData that lutFromImageData reads.
| Property | Type | Default | Description |
|---|---|---|---|
width | number | required | |
height | number | required | |
data | Uint8ClampedArray | Uint8Array | required |
MapDiagnosticsInput
Interface
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
interface MapMapBase
One entry in the catalogue.
| Property | Type | Default | Description |
|---|---|---|---|
id | MapMapBaseId | required | Stable id: what 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 | 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 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
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 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 | "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
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 | "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 | "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
interface MarkersBlock
A parsed extra.markers block.
| Property | Type | Default | Description |
|---|---|---|---|
version | 1 | required | Block schema version; currently always 1. |
items | MarkerItem[] | required | The valid markers, in input order, capped at 200. |
MarkersLayerIds
Interface
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
interface MarkersLayerOptions
Options for 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
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 | required | When the AUDIBLE alert fires. The chip itself follows alertWhileDriving. |
audioMode | NavAlertAudioMode | required | Which audio channel this kind uses. |
NavAlertsDesign
Interface
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 | 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 | required | The earcon played for a calm alert. One sound for the whole design, never one per kind - see 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 | 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 | required | Lead-distance preset; see NAV_ALERT_LEAD_DISTANCE_M. |
chipPosition | 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
interface NavBannerDesign
Banner-instruction tokens (defaults match 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
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
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 | required | |
puck | NavPuckDesign | required | |
banner | NavBannerDesign | required | |
camera? | 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 | 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
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
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
interface NavRouteDesign
Route-line tokens (defaults match 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
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
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 | 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
interface PersistentIds
The ids MapMapMap.persistentIds reports, in registry order.
| Property | Type | Default | Description |
|---|---|---|---|
sources | string[] | required | |
layers | string[] | required |
PersistentLayerOptions
Interface
interface PersistentLayerOptions
Where MapMapMap.addPersistentLayer puts a layer.
| Property | Type | Default | Description |
|---|---|---|---|
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
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
interface PlacePointFeature
A GeoJSON Point feature, as accepted in a 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
interface PlacesFeatureCollection
A plain GeoJSON FeatureCollection of Points, accepted anywhere a Place[]
is (structurally compatible with @types/geojson's
FeatureCollection<Point>). See placesFromGeoJSON for how ids and
names are derived.
| Property | Type | Default | Description |
|---|---|---|---|
type | "FeatureCollection" | required | |
features | PlacePointFeature[] | required |
PlacesIcon
Interface
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
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). 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
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
interface PlacesLayerOptions
| Property | Type | Default | Description |
|---|---|---|---|
places? | 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). 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
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
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
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
interface PositionPuckOptions
Options for 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
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
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 | required | The newest fix's vehicle record (for labels and popups). |
RenderedMarkerImage
Interface
interface RenderedMarkerImage
What 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
interface RenderMarkerImageOptions
Options for renderMarkerImage.
| Property | Type | Default | Description |
|---|---|---|---|
icon? | MarkerGlyphId | "pin" | Glyph to draw. Default "pin". |
colour? | string | the marker blue | Pin (or dot) colour. Default the marker blue. |
size? | 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; 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
interface ReverseGeocodeOptions
Options for reverse geocoding (GET /geocode/reverse).
| Property | Type | Default | Description |
|---|---|---|---|
limit? | number | not stated | Maximum results. |
kinds? | 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
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 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
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
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
interface RouteGeometry
A parsed GeoJSON LineString geometry ([lng, lat] positions).
| Property | Type | Default | Description |
|---|---|---|---|
type | "LineString" | required | |
coordinates | [number, number][] | required |
RouteLayerIds
Interface
interface RouteLayerIds
The MapLibre ids a 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
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 | 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 | 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 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.Markers, so they appear in canvas exports and match the native SDKs. |
RouteLineStyle
Interface
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
interface RouteOptions
Options for a single route request.
| Property | Type | Default | Description |
|---|---|---|---|
profile? | RouteProfile | "driving" | Routing profile. Defaults to "driving". |
truck? | 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
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; }): unknowngetSource(id: string): unknownaddSource(id: string, source: unknown): unknowngetLayer(id: string): unknownaddLayer(layer: unknown): unknownremoveLayer(id: string): unknownremoveSource(id: string): unknownisStyleLoaded(): booleanon(type: string, listener: () => void): unknownoff(type: string, listener: () => void): unknown
SearchBoxOptions
Interface
interface SearchBoxOptions
Options for 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 | Milliseconds of quiet before a keystroke becomes a request. Default 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 | 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
interface SearchBoxSelectEvent
What SearchBox emits when the user picks a result.
| Property | Type | Default | Description |
|---|---|---|---|
suggestion | SearchSuggestion | required | The row the user picked, exactly as the suggest endpoint sent it. |
hit? | 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
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
interface SetRouteEffectOptions extends RouteFlowOptions
Options for MapMapMap.setRouteEffect.
Extends RouteFlowOptions.
| Property | Type | Default | Description |
|---|---|---|---|
geometry? | 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
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
interface ShowReachabilityOptions
| Property | Type | Default | Description |
|---|---|---|---|
origin | LngLatLike | required | Where the rings radiate from. |
mode? | 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
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
interface SpeakOptions
Options for 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
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[] | required | Spoken announcements, ordered by descending trigger distance. |
banners | 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 | 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 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
interface SuggestUrlOptions
Query parameters 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
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
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 | "light" | Built-in palette to start from. Defaults to "light". |
palette? | Record<string, string> | not stated | Palette overrides by slot name (see 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 | Override the glyph endpoint (defaults to 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. 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 | not stated | Optional visual effects (route ribbon etc.). Compiled into metadata["mapmap:effects"] on the style, see 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 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 | 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
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
interface ThemeSchedulerOptions
Options for 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
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
interface TransitFeedReport
What onFeedStatus reports after every poll (0.22.1: answer and error added).
| Property | Type | Default | Description |
|---|---|---|---|
status | 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). |
answer | TransitFeedAnswer | null | 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
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 | required | The vehicle this fix belongs to, as the gateway described it. |
TransitStopFeature
Interface
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
interface TransitStopsLayerOptions
Options for TransitStopsLayer.
| Property | Type | Default | Description |
|---|---|---|---|
stops? | boolean | true | Draw the stops. Default true. |
lines? | boolean | true | Draw the lines. Default true. |
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 | The tiles index to read. Default 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
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 | 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
interface TransitVehiclesLayerOptions
Options for 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 | "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 | "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
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
interface UploadProbeOptions
Options for 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
interface VoiceGuidanceOptions
Options for 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
interface VoiceGuidanceUpdate
The subset of a core guidance update that VoiceGuidance reads.
| Property | Type | Default | Description |
|---|---|---|---|
state | "navigating" | "arrived" | "offRoute" | required | Guidance state, as emitted by the wasm GuidanceSession. |
spoken? | GuidanceSpokenPrompt | null | not stated | Spoken prompt currently due (repeated until superseded). |
severity? | 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
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, AttributionPosition, BusVehicleType, ClipType, CreditTier, DataLayerMode, DiagnosticIssue, DirectionIconName, FeaturesetId, FixSet, GeocodeKind, GuidanceSeverity, InteractionType, LngLatLike, LogoPosition, LutBytes, MapBrandTheme, MapDisplayMode, MapMapBaseGroup, MapMapBaseId, MapMapTheme, MarkerGlyphId, MarkerSize, NavAlertAudioMode, NavAlertChipPosition, NavAlertIconSet, NavAlertLeadDistance, NavAlertMode, NavAlertSound, NavCameraKind, NavigationCameraMode, PlacesInput, PlaceWithDistance, PlaceWithDriveTime, ProbeUploadOutcome, ProjectionUniform, ProjectionVariant, ProtocolHost, ReachabilityMode, RouteEffectName, RouteProfile, SearchBoxBias, SearchBoxSelectListener, Slot, SunTimes, ThemeSource, TransitDrawnVehicle, TransitFeedStatus, TransitSlot, UseTheme, Vec3
AdrTunnelCategory
Type
type AdrTunnelCategory = "A" | "B" | "C" | "D" | "E";
The ADR category of a tunnel, "A" (least restrictive) … "E".
AttributionPosition
Type
type AttributionPosition = "bottom-left" | "bottom-right" | "top-left" | "top-right";
Map corners an enforced control (attribution, mark) can be mounted in.
BusVehicleType
Type
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
type ClipType = keyof typeof CLIP_TARGETS;
CreditTier
Type
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
type DataLayerMode = "auto" | "point" | "line" | "polygon" | "cluster" | "heatmap" | "extrusion";
How a DataLayer draws its features.
DiagnosticIssue
Type
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
type DirectionIconName = keyof typeof directionIcons;
A valid icon name (turn_left, roundabout_right, ferry, ...).
FeaturesetId
Type
type FeaturesetId = "poi" | "place-labels" | "buildings";
A named group of basemap layers, as in Mapbox Standard featuresets.
FixSet
Type
type FixSet = ReadonlyMap<string, TransitFix>;
One poll's fixes, by vehicle id.
GeocodeKind
Type
type GeocodeKind = "address" | "street" | "locality" | "poi" | "postcode";
The document kinds the reverse endpoint can filter by.
GuidanceSeverity
Type
type GuidanceSeverity = "info" | "warning" | "critical";
Instruction urgency, as emitted by the core (InstructionSeverity).
InteractionType
Type
type InteractionType = "click" | "mouseenter" | "mouseleave" | "mousemove";
The mouse events an interaction can listen for.
LngLatLike
Type
type LngLatLike = [number, number] | {
lng: number;
lat: number;
} | {
lon: number;
lat: number;
};
A longitude/latitude pair, accepted in the three common shapes.
LogoPosition
Type
type LogoPosition = "bottom-right" | "bottom-left" | "top-right" | "top-left";
Corner positions accepted by the logo control.
LutBytes
Type
type LutBytes = Uint8Array;
A colour-theme LUT: exactly LUT_BYTES bytes, layout as above.
MapBrandTheme
Type
type MapBrandTheme = "day" | "night";
Day (dark ink on light maps) or Night (white on dark maps).
MapDisplayMode
Type
type MapDisplayMode = "browse" | "navigating";
Whether the map is being browsed or actively navigated.
MapMapBaseGroup
Type
type MapMapBaseGroup = "base" | "designed";
Which half of the catalogue an entry belongs to.
MapMapBaseId
Type
type MapMapBaseId = "light" | "dark" | "streets" | "midnight" | "navigator-day" | "navigator-night" | "fleet" | "outdoor" | "dataviz" | "backdrop" | "print";
Every first-party base id.
MapMapTheme
Type
type MapMapTheme = "light" | "dark";
Which built-in palette a theme starts from.
MarkerGlyphId
Type
type MarkerGlyphId = (typeof MARKER_GLYPH_IDS)[number];
One marker glyph id ("pin", "star", "fuel", ...).
MarkerSize
Type
type MarkerSize = "s" | "m" | "l";
One marker size preset.
NavAlertAudioMode
Type
type NavAlertAudioMode = (typeof NAV_ALERT_AUDIO_MODES)[number];
One audio mode.
NavAlertChipPosition
Type
type NavAlertChipPosition = (typeof NAV_ALERT_CHIP_POSITIONS)[number];
One chip position slot.
NavAlertIconSet
Type
type NavAlertIconSet = (typeof NAV_ALERT_ICON_SETS)[number];
One curated icon set. "brand" means "use my iconUrlOverrides".
NavAlertLeadDistance
Type
type NavAlertLeadDistance = (typeof NAV_ALERT_LEAD_DISTANCES)[number];
One lead-distance preset.
NavAlertMode
Type
type NavAlertMode = (typeof NAV_ALERT_MODES)[number];
One alert mode.
NavAlertSound
Type
type NavAlertSound = (typeof NAV_ALERT_SOUNDS)[number];
One built-in earcon.
NavCameraKind
Type
type NavCameraKind = (typeof NAV_CAMERA_KINDS)[number];
One safety-camera kind.
NavigationCameraMode
Type
type NavigationCameraMode = "follow" | "overview" | "free";
The camera's current behaviour.
PlacesInput
Type
type PlacesInput = Place[] | PlacesFeatureCollection;
Either input shape for PlacesLayer / setPlaces.
PlaceWithDistance
Type
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
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
type ProbeUploadOutcome = "accepted" | "refused" | "notEnabled" | "rejected" | "gaveUp";
The outcome of an upload attempt.
ProjectionUniform
Type
type ProjectionUniform = (typeof PROJECTION_UNIFORMS)[number];
ProjectionVariant
Type
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,
with one console warning.
ProtocolHost
Type
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
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
type RouteEffectName = "flow";
The names the effects registry knows. Grows as effects land.
RouteProfile
Type
type RouteProfile = "driving" | "walking" | "truck" | (string & {});
OSRM routing profile. MapMap ships driving, walking and truck.
SearchBoxBias
Type
type SearchBoxBias = "map-centre" | {
lat: number;
lon: number;
} | false;
Where a suggest request should bias its results.
SearchBoxSelectListener
Type
type SearchBoxSelectListener = (event: SearchBoxSelectEvent) => void;
The listener signature for the "select" event.
Slot
Type
type Slot = "bottom" | "middle" | "top";
A named insertion point in the MapMap layer stack.
SunTimes
Type
type SunTimes = {
sunrise: Date;
sunset: Date;
} | "polarDay" | "polarNight";
Sun-times result: either both events, or a polar day/night marker.
ThemeSource
Type
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
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 | 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
type TransitFeedStatus = "ok" | "stale" | "unknown";
The gateway's freshness states for the feed.
TransitSlot
Type
type TransitSlot = "bottom" | "middle" | "top";
Where in the style the layer sits (the SDK slot names).
UseTheme
Type
type UseTheme = "default" | "none";
LayerOverride.use_theme (Mapbox *-use-theme). Absent means "default".
Vec3
Type
type Vec3 = [number, number, number];
Constants (@mapmap/maps)
ALERT_CONTRAST_MIN, BAKED_MARKERS_GLYPH_ID, BASE_ID_ALIASES, BODS_SHORT_CREDIT, BUS_DEFAULT_VEHICLE_TYPE, BUS_IMAGES, BUS_INK, BUS_RING_IMAGES, BUS_SPRITE_IMAGES, BUS_STALE_AFTER_S, BUS_VEHICLE_TYPES, CLIP_DEFAULT_MINZOOM, CLIP_MAX_VERTICES, CLIP_TARGETS, COLOUR_THEME_LAYER_ID, COLOUR_THEME_METADATA_KEY, DEFAULT_DEBOUNCE_MS, DEFAULT_GATEWAY_URL, DEFAULT_GLYPHS_URL, DEFAULT_MAX_MERCATOR_SEGMENT, DEFAULT_TERRAIN_ATTRIBUTION, DEFAULT_TERRAIN_URL, DEFAULT_TERRITORY_TILES_URL, directionIcons, EFFECTS_METADATA_KEY, FEATURESETS, FLOW_DEFAULTS, FULL_ATTRIBUTION, GLOBE_BLEND_GLSL, GLOBE_RADIUS_M, IDENTITY_LUT, KEYED_TILES_TERRITORY, LOGO_SVG, LUT_BYTES, LUT_SIZE, MAPMAP_BASES, MARKER_DEFAULT_COLOUR, MARKER_GLYPH_IDS, MARKER_GLYPH_VIEWBOX, MARKER_LABEL_TEXT_SIZES, MARKER_PIN_BODY_PATH, MARKER_SIZES, MARKER_SIZES_PX, markerGlyphPaths, MARKERS_ID, MAX_MARKER_ID_LENGTH, MAX_MARKER_IMAGE_BYTES, MAX_MARKER_ITEMS, MAX_MARKER_LABEL_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_PUCK_IMAGE_BYTES, MAX_THEME_GEOJSON_SOURCES, MAX_THEME_SOURCES, NAPTAN_SHORT_CREDIT, NAV_ALERT_AUDIO_MODES, NAV_ALERT_CHIP_POSITIONS, NAV_ALERT_ICON_SETS, NAV_ALERT_LEAD_DISTANCE_M, NAV_ALERT_LEAD_DISTANCES, NAV_ALERT_MODES, NAV_ALERT_SOUND_BASE_URL, NAV_ALERT_SOUND_FILES, NAV_ALERT_SOUNDS, NAV_CAMERA_DEFAULTS, NAV_CAMERA_KINDS, NO_ROADS_NEAR_ORIGIN, OPENMAPTILES_ATTRIBUTION, OSM_ATTRIBUTION, PALETTE_SLOTS, POI_CATEGORY_COLORS, POI_CATEGORY_IDS, POI_CLASS_CATEGORIES, PROJECTION_UNIFORMS, RIBBON_FLOATS_PER_VERTEX, ROUTE_EFFECTS, SIGNAL_BLUE, SLOT_ANCHORS, SLOTS, SOURCE_LAYERS, TRANSIT_LINE_FALLBACK_COLOUR, TRANSIT_TILES_INDEX_URL
ALERT_CONTRAST_MIN
Constant
const ALERT_CONTRAST_MIN = 4.5;
Minimum acceptable contrast ratio for alert-chip text on its background.
BAKED_MARKERS_GLYPH_ID
Constant
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
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
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
keeps resolving them. Mirrors sn_style::BASE_ID_ALIASES.
BODS_SHORT_CREDIT
Constant
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
const BUS_DEFAULT_VEHICLE_TYPE: BusVehicleType;
What a vehicle with no sourced body type is drawn as, unless defaultVehicleType says otherwise.
BUS_IMAGES
Constant
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
const BUS_INK = "#0f1115";
The bus marker's neutral ink.
BUS_RING_IMAGES
Constant
const BUS_RING_IMAGES: Record<BusVehicleType, string>;
The followed ring's under-image id for each body type.
BUS_SPRITE_IMAGES
Constant
const BUS_SPRITE_IMAGES: Record<BusVehicleType, string>;
The top-down sprite's image id for each body type.
BUS_STALE_AFTER_S
Constant
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
const BUS_VEHICLE_TYPES: readonly BusVehicleType[];
Every BusVehicleType.
CLIP_DEFAULT_MINZOOM
Constant
const CLIP_DEFAULT_MINZOOM = 13;
Default ClipOptions.minzoom: the street zooms where buildings draw.
CLIP_MAX_VERTICES
Constant
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
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
const COLOUR_THEME_LAYER_ID = "mapmap-colour-theme";
The default layer id map.setColorTheme uses.
COLOUR_THEME_METADATA_KEY
Constant
const COLOUR_THEME_METADATA_KEY = "mapmap:color-theme";
The compiled style's metadata key for the theme summary.
DEFAULT_DEBOUNCE_MS
Constant
const DEFAULT_DEBOUNCE_MS = 250;
Default debounce between the last keystroke and the suggest request.
DEFAULT_GATEWAY_URL
Constant
const DEFAULT_GATEWAY_URL = "https://api.mapmap.ai";
The MapMap gateway, the default for MapMapOptions.baseUrl.
DEFAULT_GLYPHS_URL
Constant
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
const DEFAULT_MAX_MERCATOR_SEGMENT: number;
Default subdivideMercator limit: 1/512 mercator units, about 78 km at the equator.
DEFAULT_TERRAIN_ATTRIBUTION
Constant
const DEFAULT_TERRAIN_ATTRIBUTION: string;
DEFAULT_TERRAIN_URL
Constant
const DEFAULT_TERRAIN_URL = "https://mapmap.ai/terrain/{z}/{x}/{y}.png";
DEFAULT_TERRITORY_TILES_URL
Constant
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 and territoryTilesSourceUrl.
directionIcons
Constant · also exported from @mapmap/maps/direction-icons
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
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
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
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
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
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
const GLOBE_RADIUS_M = 6371008.8;
The prelude's GLOBE_RADIUS: MapLibre's mean Earth radius, metres.
IDENTITY_LUT
Constant
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
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,
so feature ids, zoom range and the OpenMapTiles schema are identical.
LOGO_SVG
Constant · deprecated
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
const LUT_BYTES: number;
Bytes in a LUT: 16 * 16 * 16 RGB triplets.
LUT_SIZE
Constant
const LUT_SIZE = 16;
Lattice points per axis. Fixed at 16 (Mapbox's size too).
MAPMAP_BASES
Constant
const MAPMAP_BASES: readonly MapMapBase[];
The catalogue, in display order.
MARKER_DEFAULT_COLOUR
Constant
const MARKER_DEFAULT_COLOUR = "#1a6bff";
Default marker colour (MapMap marker blue).
MARKER_GLYPH_IDS
Constant
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
const MARKER_GLYPH_VIEWBOX = 24;
The glyph and pin-body coordinate space (a 24x24 viewBox).
MARKER_LABEL_TEXT_SIZES
Constant
const MARKER_LABEL_TEXT_SIZES: Record<MarkerSize, number>;
Label text size in px for each marker size preset.
MARKER_PIN_BODY_PATH
Constant
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
const MARKER_SIZES: readonly ["s", "m", "l"];
The marker size presets, smallest first.
MARKER_SIZES_PX
Constant
const MARKER_SIZES_PX: Record<MarkerSize, number>;
CSS pixel size of each marker preset (the rasterised square canvas).
markerGlyphPaths
Constant
const markerGlyphPaths: Record<MarkerGlyphId, string>;
24x24 viewBox SVG path data for every marker glyph, keyed by id.
MARKERS_ID
Constant
const MARKERS_ID = "mm-user-markers";
The shared GeoJSON source id AND symbol layer id (contract).
MAX_MARKER_ID_LENGTH
Constant
const MAX_MARKER_ID_LENGTH = 64;
Maximum marker id length (contract).
MAX_MARKER_IMAGE_BYTES
Constant
const MAX_MARKER_IMAGE_BYTES: number;
Maximum accepted data: marker-image payload (matches the puck's cap).
MAX_MARKER_ITEMS
Constant
const MAX_MARKER_ITEMS = 200;
Maximum markers per theme (extra.markers v1 contract).
MAX_MARKER_LABEL_LENGTH
Constant
const MAX_MARKER_LABEL_LENGTH = 120;
Maximum marker label length (contract).
MAX_MARKER_TEXT_LENGTH
Constant
const MAX_MARKER_TEXT_LENGTH = 3;
Maximum characters drawn INSIDE a pin head by the short-text mode of
renderMarkerImage (numbered waypoints 1, 2, 3; lettered
stops A, B). Anything longer belongs in a label below the pin.
MAX_PUCK_IMAGE_BYTES
Constant
const MAX_PUCK_IMAGE_BYTES: number;
Maximum accepted data: puck-image payload (matches Studio's upload cap).
MAX_THEME_GEOJSON_SOURCES
Constant
const MAX_THEME_GEOJSON_SOURCES = 8;
At most this many GeoJSON sources per theme.
MAX_THEME_SOURCES
Constant
const MAX_THEME_SOURCES = 15;
At most this many sources per theme. Mirrors sn_style::MAX_THEME_SOURCES.
NAPTAN_SHORT_CREDIT
Constant
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
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
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
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
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
const NAV_ALERT_LEAD_DISTANCES: readonly ["short", "standard", "long"];
Lead-distance presets. Not a free number: see NAV_ALERT_LEAD_DISTANCE_M.
NAV_ALERT_MODES
Constant
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
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 to self-host them - the SDK bundles
no audio, exactly like VoiceGuidance's earcon URLs.
NAV_ALERT_SOUND_FILES
Constant
const NAV_ALERT_SOUND_FILES: Record<NavAlertSound, string>;
File name of each built-in earcon, relative to
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
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
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
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 before indexing a design.
NO_ROADS_NEAR_ORIGIN
Constant
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
const OPENMAPTILES_ATTRIBUTION = "\u00A9 OpenMapTiles";
The OpenMapTiles schema attribution (CC-BY 4.0).
OSM_ATTRIBUTION
Constant
const OSM_ATTRIBUTION = "\u00A9 OpenStreetMap contributors";
The OSM attribution string. Legally required (ODbL) - never removed.
PALETTE_SLOTS
Constant
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
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
const POI_CATEGORY_IDS: readonly string[];
The valid category ids, in presentation order.
POI_CLASS_CATEGORIES
Constant
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
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
const RIBBON_FLOATS_PER_VERTEX = 5;
Floats per ribbon vertex: x, y (mercator), nx, ny (unit normal), progress.
ROUTE_EFFECTS
Constant
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.
SIGNAL_BLUE
Constant
const SIGNAL_BLUE = "#3a86ff";
MapMap brand signal blue (see website --signal).
SLOT_ANCHORS
Constant
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
const SLOTS: readonly Slot[];
Every slot, bottom of the stack first.
SOURCE_LAYERS
Constant
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
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
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
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, AdrCheck, AdrCheckOptions, AdrCheckRequest, AdrCheckResult, AdrDimensions, AdrTunnelCategory, ALERT_CONTRAST_MIN, alertChipContent, AlertChipContent, AlertContrastIssue, alertContrastIssues, alertIconSvg, alertIconUrl, alertPresentation, AlertPresentation, alertSoundUrl, alertSpeedReadoutColor, applyColourTheme, applyGeoJSONDiff, applyPoiDesign, applyTerrain, AttributionPosition, BAKED_MARKERS_GLYPH_ID, BannerComponent, BannerContent, BannerInstruction, bannerLanes, BASE_ID_ALIASES, baseTheme, bearingBetween, bindFlythroughToScroll, BODS_SHORT_CREDIT, buildAdrCheckBody, buildGeocodeUrl, buildLut, buildProbeUrl, buildRetrieveUrl, buildReverseGeocodeUrl, buildRouteQuery, buildRouteUrl, buildStyle, BuildStyleOptions, buildSuggestUrl, buildVertexSource, builtInPoiColor, BUS_DEFAULT_VEHICLE_TYPE, BUS_IMAGES, BUS_INK, BUS_RING_IMAGES, BUS_SPRITE_IMAGES, BUS_STALE_AFTER_S, BUS_VEHICLE_TYPES, busMarkerLayers, busPuckImage, busPuckImageOptions, busSilhouetteSdf, busSpriteBody, busTickImage, busVehicleType, BusVehicleType, CameraAlert, CameraAlertChip, CameraFix, cameraKindLabel, cameraKindsShownOnMap, cameraShownOnMap, cameraSpriteName, cameraSymbolLayer, capTransitBbox, checkSourceUrl, CLIP_DEFAULT_MINZOOM, CLIP_MAX_VERTICES, CLIP_TARGETS, clipFilter, ClipMatrices, ClipOptions, ClipType, ColorThemeSpec, COLOUR_THEME_LAYER_ID, COLOUR_THEME_METADATA_KEY, ColourTheme, ColourThemeLayer, ColourThemeLayerOptions, colourThemeMetadata, contrastRatio, createMap, createRouteEffect, CreditControl, CreditInnerControl, CreditOptions, creditTier, CreditTier, DataLayer, DataLayerCategories, DataLayerClusterOptions, dataLayerColor, DataLayerEvent, DataLayerExtrusionOptions, DataLayerGraduated, DataLayerMode, DataLayerOptions, DataLayerSimpleStyle, DataLayerStyle, DEFAULT_DEBOUNCE_MS, DEFAULT_GATEWAY_URL, DEFAULT_GLYPHS_URL, DEFAULT_MAX_MERCATOR_SEGMENT, DEFAULT_TERRAIN_ATTRIBUTION, DEFAULT_TERRAIN_URL, DEFAULT_TERRITORY_TILES_URL, defaultNavAlertsDesign, defaultNavDesign, defaultPoiDesign, DiagnosticIssue, directionArrow, EFFECTS_METADATA_KEY, effectsFromStyleMetadata, EndpointMarker, EndpointsDesign, extractGuidance, FeaturesetId, featuresetLayerIds, FEATURESETS, FixSet, fixSetFromCollection, FLOW_DEFAULTS, FlowRouteEffectLayer, flythrough, FlythroughController, FlythroughOptions, flythroughPose, FlythroughPose, formatCoord, formatCoords, FULL_ATTRIBUTION, GeocodeHit, GeocodeKind, GeocodeOptions, Geocoder, GeocoderOptions, GLOBE_BLEND_GLSL, GLOBE_RADIUS_M, globeModelMatrix, gradeColourString, gradeStyleColours, GradeStyleOptions, GroundReadableMap, GuidanceBanner, GuidanceSeverity, GuidanceSpokenPrompt, hasBakedMarkers, haversineDistanceM, holdLayersOnTop, IDENTITY_LUT, Interaction, InteractionEvent, InteractionRegistry, InteractionTarget, InteractionType, interpolateFixes, isMarkerGlyphId, isNameTextField, IsochroneError, IsochroneFeatureCollection, IsochroneLayer, IsochroneLayerOptions, isSlot, KEYED_TILES_TERRITORY, keyedStyleUrl, keyedTilesUrl, laneArrowDirection, LaneIndication, languageTextField, LayerOverride, layersOnTopMoves, liveTargetLayerIds, LngLatLike, lngLatToMercator, locateProjectionUniforms, LOGO_SVG, LogoControl, LogoOptions, LogoPosition, LUT_BYTES, LUT_SIZE, lutApply, LutBytes, lutDigest, lutFromBase64, lutFromImageData, lutFromPngData, LutImageData, lutToBase64, lutToImageData, mapBrandSvg, MapBrandTheme, MapDiagnosticsInput, MapDisplayMode, mapGroundTheme, MAPMAP_BASES, MapMapBase, MapMapBaseGroup, MapMapBaseId, MapMapMap, MapMapOptions, MapMapTheme, MARKER_DEFAULT_COLOUR, MARKER_GLYPH_IDS, MARKER_GLYPH_VIEWBOX, MARKER_LABEL_TEXT_SIZES, MARKER_PIN_BODY_PATH, MARKER_SIZES, MARKER_SIZES_PX, MarkerGlyphId, markerGlyphPaths, markerImage, markerImageDataKey, markerImageKey, MarkerItem, MARKERS_ID, MarkersBlock, markersFromTheme, markersFromThemeUrl, MarkerSize, MarkersLayer, MarkersLayerIds, MarkersLayerOptions, markerText, markerTextImageKey, MAX_MARKER_ID_LENGTH, MAX_MARKER_IMAGE_BYTES, MAX_MARKER_ITEMS, MAX_MARKER_LABEL_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_PUCK_IMAGE_BYTES, MAX_THEME_GEOJSON_SOURCES, MAX_THEME_SOURCES, mercatorModelMatrix, modelClipMatrices, multiplyMat4, namedBase, NAPTAN_SHORT_CREDIT, NAV_ALERT_AUDIO_MODES, NAV_ALERT_CHIP_POSITIONS, NAV_ALERT_ICON_SETS, NAV_ALERT_LEAD_DISTANCE_M, NAV_ALERT_LEAD_DISTANCES, NAV_ALERT_MODES, NAV_ALERT_SOUND_BASE_URL, NAV_ALERT_SOUND_FILES, NAV_ALERT_SOUNDS, NAV_CAMERA_DEFAULTS, NAV_CAMERA_KINDS, NavAlertAudioMode, NavAlertChipPosition, NavAlertIconSet, NavAlertKindDesign, NavAlertLeadDistance, navAlertLeadDistanceM, NavAlertMode, NavAlertsDesign, NavAlertSound, navAlertSoundUrl, NavBannerDesign, NavCameraDesign, navCameraKind, NavCameraKind, NavDesign, navDesignFromTheme, navDesignFromThemeUrl, NavigationCamera, NavigationCameraMode, NavigationCameraOptions, NavPuckDesign, NavRouteDesign, NearestByDriveTimeOptions, NO_ROADS_NEAR_ORIGIN, OPENMAPTILES_ATTRIBUTION, OSM_ATTRIBUTION, osmAndMoreCredit, PALETTE_SLOTS, parseCssColour, ParsedRoute, parseGeocodeResponse, parseHexColor, parseMarkers, parseNavAlertsDesign, parseNavDesign, parseOsrmRoute, parsePoiDesign, parseSuggestResponse, PersistentIds, PersistentLayerOptions, Place, PlacePointFeature, PlacesFeatureCollection, placesFromGeoJSON, PlacesIcon, PlacesInput, PlacesLabelOptions, PlacesLayer, PlacesLayerIds, PlacesLayerOptions, PlacesSelectOptions, PlaceWithDistance, PlaceWithDriveTime, POI_CATEGORY_COLORS, POI_CATEGORY_IDS, POI_CLASS_CATEGORIES, PoiCategoryDesign, PoiDesign, poiDesignFromTheme, poiDesignFromThemeUrl, poiDesignIsDefault, poiTextColorExpression, PositionPuck, PositionPuckOptions, prefersReducedMotion, ProbeUploadOutcome, ProgramCache, PROJECTION_UNIFORMS, ProjectionArgs, ProjectionUniform, projectionVariant, ProjectionVariant, ProtocolHost, ReachabilityMode, readProjection, registerPmtilesProtocol, relativeLuminance, removeTerrain, renderClockMs, Rendered, RenderedMarkerImage, renderMarkerImage, RenderMarkerImageOptions, resetDiagnostics, resolveBaseId, resolveSlot, resolveTerrain, resolveTheme, ReverseGeocodeOptions, RIBBON_FLOATS_PER_VERTEX, RibbonMesh, ROUTE_EFFECTS, RouteEffectLayer, RouteEffectName, RouteFlowOptions, RouteGeometry, RouteLayer, RouteLayerIds, RouteLayerOptions, RouteLineStyle, RouteOptions, RouteProfile, runMapDiagnostics, SearchBox, SearchBoxBias, SearchBoxMap, SearchBoxOptions, SearchBoxSelectEvent, SearchBoxSelectListener, SearchSuggestion, setMapLanguage, setProjectionUniforms, SetRouteEffectOptions, severityProsody, SeverityProsody, shortCredit, shortestArcDeg, shortestArcDelta, ShowReachabilityOptions, SIGNAL_BLUE, Slot, SLOT_ANCHORS, SLOTS, SnappedOrigin, SOURCE_LAYERS, speak, SpeakOptions, sphereFrame, sphereSurface, ssmlToText, StepGuidance, styleFromBase, styleGroundTheme, subdivideMercator, SuggestUrlOptions, sunTimes, SunTimes, supportsTerrainDraping, TerrainOptions, territoryTilesSourceUrl, tessellateRouteRibbon, Theme, ThemeEffects, ThemeScheduler, ThemeSchedulerOptions, ThemeSource, tidyPlaceName, toLngLat, toPmtilesUrl, TRANSIT_LINE_FALLBACK_COLOUR, TRANSIT_TILES_INDEX_URL, transitArchiveUrl, TransitDrawnVehicle, TransitFeedAnswer, TransitFeedReport, TransitFeedStatus, TransitFix, transitFreshnessLabel, TransitSlot, TransitStopFeature, transitStopFromProperties, TransitStopsLayer, TransitStopsLayerOptions, transitTileJsonUrl, transitUrlsFromIndex, TransitVehicle, TransitVehiclesLayer, TransitVehiclesLayerOptions, TruckParams, truncateChars, uploadProbeBatch, UploadProbeOptions, UseTheme, validateColourTheme, validateUseTheme, Vec3, VoiceGuidance, VoiceGuidanceOptions, VoiceGuidanceUpdate, VoiceInstruction.
@mapmap/maps/avatars
import { /* … */ } from "@mapmap/maps/avatars";
Classes (@mapmap/maps/avatars)
AvatarPuck
Class
class AvatarPuck { … }
The user's vehicle drawn as a MapMap car. A drop-in for PositionPuck:
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 | The current car. | |
readonly paint | AvatarPaint | The current paint. | |
readonly fallback | AvatarFallbackEvent | undefined | 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 (checkonFallback). Never rejects.setLocation(location: { lat: number; lon: number; }, headingDeg?: number): void: Moves the vehicle (adding it to the map on the first call).headingDegis the course, clockwise from north; omit it to keep the previous one. Same glide asPositionPuck.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).setLocationadds it again.
Example
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, avatarCellRect, avatarFeatureProperties, avatarIconOffset, avatarIconSize, avatarImageId, avatarModelUrl, avatarPixelRatio, avatarSizePx, avatarSpriteUrl, avatarSymbolLayer, headingBucket, pitchBucket
avatarAlignment
Function
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 | required |
Options and types: AvatarPitch.
avatarCellRect
Function
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
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; a fleet layer can hold many such
features in one GeoJSON source (all in the same pitch bucket).
| Parameter | Type | Default | Description |
|---|---|---|---|
car | AvatarCar | required | |
paint | AvatarPaint | required | |
pitch | AvatarPitch | required | |
courseDeg | number | required | |
bearingDeg | number | required | |
size? | AvatarSize | number | not stated |
Options and types: AvatarCar, AvatarPaint, AvatarPitch, AvatarSize.
avatarIconOffset
Function
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 | required | |
pitch | AvatarPitch | required |
Options and types: AvatarCar, AvatarPitch.
avatarIconSize
Function
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 | required | |
pitch | AvatarPitch | required | |
size? | AvatarSize | number | not stated |
Options and types: AvatarCar, AvatarPitch, AvatarSize.
avatarImageId
Function
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 | required | |
paint | AvatarPaint | required | |
pitch | AvatarPitch | required | |
headingIndex? | number | not stated |
Options and types: AvatarCar, AvatarPaint, AvatarPitch.
avatarModelUrl
Function
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 | required | |
options? | { lod?: 0 | 1; baseUrl?: string; } | not stated |
Options and types: AvatarCar.
avatarPixelRatio
Function
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 | required | |
size | AvatarSize | number | required | |
devicePixelRatio | number | required |
Options and types: AvatarCar, AvatarSize.
avatarSizePx
Function
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.
avatarSpriteUrl
Function
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 | required | |
paint | AvatarPaint | required | |
pitch | AvatarPitch | required | |
options? | { baseUrl?: string; pixelRatio?: 1 | 2; ext?: "webp" | "json"; } | not stated |
Options and types: AvatarCar, AvatarPaint, AvatarPitch.
avatarSymbolLayer
Function
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); the alignment is the
bucket's (avatarAlignment). The vehicle never yields to labels.
| Parameter | Type | Default | Description |
|---|---|---|---|
source | string | required | |
id | string | required | |
pitch? | AvatarPitch | not stated |
Options and types: AvatarPitch.
headingBucket
Function
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
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, AvatarManifest, AvatarManifestPitch, AvatarPuckOptions
AvatarFallbackEvent
Interface
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
interface AvatarManifest
The shape of 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
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
interface AvatarPuckOptions
Options for AvatarPuck.
| Property | Type | Default | Description |
|---|---|---|---|
car? | AvatarCar | "city" | The vehicle (default "city"). |
paint? | 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 | Where the artwork is served from (default 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, AvatarPaint, AvatarPitch, AvatarSize
AvatarCar
Type
type AvatarCar = (typeof AVATAR_MANIFEST.cars)[number]["id"];
A released vehicle: "city" | "tourer" | "suv" | "van".
AvatarPaint
Type
type AvatarPaint = (typeof AVATAR_MANIFEST.paints)[number]["id"];
A paint: "glacier-white" | "signal-blue" | "graphite" | ....
AvatarPitch
Type
type AvatarPitch = 0 | 30 | 45 | 58;
A pitch bucket, degrees from straight down.
AvatarSize
Type
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_CARS, AVATAR_MANIFEST, AVATAR_PAINT_HEX, AVATAR_PAINTS, AVATAR_PITCHES, AVATAR_SIZES
AVATAR_ASSET_BASE_URL
Constant
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
const AVATAR_CARS: readonly AvatarCar[];
Every released car, in catalogue order.
AVATAR_MANIFEST
Constant
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
const AVATAR_PAINT_HEX: Readonly<Record<AvatarPaint, string>>;
Each paint's sRGB hex, for pickers and swatches.
AVATAR_PAINTS
Constant
const AVATAR_PAINTS: readonly AvatarPaint[];
Every paint, in catalogue order.
AVATAR_PITCHES
Constant
const AVATAR_PITCHES: readonly AvatarPitch[];
The pitch buckets, ascending.
AVATAR_SIZES
Constant
const AVATAR_SIZES: Readonly<Record<AvatarSize, number>>;
The disc diameter in CSS pixels for each named size.
@mapmap/maps/road-signs
import { /* … */ } from "@mapmap/maps/road-signs";
Functions (@mapmap/maps/road-signs)
resolveRoadRef, resolveSpeedSign, roadRefLabel, roadRefSvg, shieldSpriteName, speedSignLabel, speedSignSvg
resolveRoadRef
Function
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 | required |
Options and types: RoadRefInput.
resolveSpeedSign
Function
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 | required |
Options and types: SpeedSignInput.
roadRefLabel
Function
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.
roadRefSvg
Function
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 | required | |
height | number | required | |
opts? | ComposeOptions | not stated |
Options and types: RoadRef, ComposeOptions.
shieldSpriteName
Function
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
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.
speedSignSvg
Function
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 | required | |
size | number | required | |
opts? | ComposeOptions | not stated |
Options and types: SpeedSign, ComposeOptions.
Interfaces (@mapmap/maps/road-signs)
ComposeOptions, CountrySignProfile, FamilySpacing, GenericCountryEntry, GlyphData, Plausible, ResolvedProfile, RoadRef, RoadRefInput, ShieldTier, SignProfileTable, SignTables, SpeedSign, SpeedSignInput
ComposeOptions
Interface · also exported from @mapmap/maps/asset-markers
interface ComposeOptions
| Property | Type | Default | Description |
|---|---|---|---|
surface? | Surface | not stated | |
fitMode? | FitMode | not stated | Numeral width rule; default "official". |
CountrySignProfile
Interface · also exported from @mapmap/maps/asset-markers
interface CountrySignProfile
| Property | Type | Default | Description |
|---|---|---|---|
unit | Unit | required | |
speedSignForm | SpeedSignForm | required | |
nationalLimitSign | "gb_nsl" | "none" | required | |
noLimitSign | "de_end" | "none" | required | |
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 | required | Sanity bounds in the display unit for engine-only values; outside or off-step -> unknown. |
shields | ShieldTier[] | required | |
speedSignTemplate? | 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 | 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
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
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
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
interface Plausible
| Property | Type | Default | Description |
|---|---|---|---|
min | number | required | |
max | number | required | |
step | number | required |
ResolvedProfile
Interface · also exported from @mapmap/maps/asset-markers
interface ResolvedProfile extends GenericSignProfile
A country profile after alias resolution and subdivision merge.
Extends 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
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
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
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
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 | 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
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
interface SpeedSign
| Property | Type | Default | Description |
|---|---|---|---|
kind | SpeedSignKind | required | |
value? | number | not stated | Display value in unit (kept for national and zone for the label). |
unit | Unit | required | |
form | SpeedSignForm | required | |
a11yLabel | string | required | |
template? | SpeedTemplateId | not stated | Template to draw; absent when nothing renders (unknown, walk, none outside DE, unknown country). |
family? | 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
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, GenericSignProfile, GlyphFamily, ImplicitKey, ImplicitValue, NumeralFamily, ShieldTemplateId, SpeedSignForm, SpeedSignKind, SpeedSignTemplateId, SpeedTemplateId, Surface, Unit, ZoneForm
FitMode
Type · also exported from @mapmap/maps/asset-markers
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
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
type GlyphFamily = Record<string, GlyphData>;
ImplicitKey
Type · also exported from @mapmap/maps/asset-markers
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
type ImplicitValue = number | "none" | "walk";
NumeralFamily
Type · also exported from @mapmap/maps/asset-markers
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
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
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
type SpeedSignKind = "numeric" | "national" | "none" | "walk" | "zone" | "unknown";
SpeedSignTemplateId
Type
type SpeedSignTemplateId = SpeedTemplateId;
Speed-sign template ids (templates/speed/).
SpeedTemplateId
Type · also exported from @mapmap/maps/asset-markers
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
type Surface = "light" | "dark";
Unit
Type · also exported from @mapmap/maps/asset-markers
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
type ZoneForm = "gb_zone" | "vienna_zone" | "mutcd_zone" | "none";
Constants (@mapmap/maps/road-signs)
shieldTemplates, signAlphabets, signAttributions, signNumerals, signProfiles, signSpacing, speedSignTemplates
shieldTemplates
Constant
const shieldTemplates: Record<ShieldTemplateId, string>;
Shield templates with stretch metadata (100 units high at their two-digit width).
signAlphabets
Constant
const signAlphabets: Record<string, GlyphFamily>;
MapMap shield alphabets (alphabet/).
signAttributions
Constant
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
const signNumerals: Record<string, GlyphFamily>;
MapMap numeral families (numerals/): single-path glyphs on a 100-unit cap grid.
signProfiles
Constant
const signProfiles: SignProfileTable;
The canonical country table (map-assets/signs/profiles.json, SPEC 4.3).
signSpacing
Constant
const signSpacing: Record<string, FamilySpacing>;
Word space and pair kerning per family (numerals|alphabet/<family>/_spacing.json).
speedSignTemplates
Constant
const speedSignTemplates: Record<SpeedTemplateId, string>;
Speed-sign templates with their named slots (100 units high).
@mapmap/maps/asset-markers
import { /* … */ } from "@mapmap/maps/asset-markers";
Also re-exports, documented above: ComposeOptions, CountrySignProfile, FamilySpacing, FitMode, GenericCountryEntry, GenericSignProfile, GlyphData, GlyphFamily, ImplicitKey, ImplicitValue, NumeralFamily, Plausible, ResolvedProfile, RoadRef, RoadRefInput, ShieldTier, SignProfileTable, SignTables, SpeedSign, SpeedSignForm, SpeedSignInput, SpeedSignKind, SpeedTemplateId, Surface, Unit, ZoneForm.
Functions (@mapmap/maps/asset-markers)
assetGroupOf, assetIconFor, beamImageId, borderPx, faceShape, faceSvg, facingSvg, layoutFace, markerImageId, puckBox, puckGroup, puckImageId, puckSvg, resolveFace, roadsideMarkerFor, signMarker
assetGroupOf
Function
function assetGroupOf(cat: string): AssetGroupId;
The legend group of a category.
| Parameter | Type | Default | Description |
|---|---|---|---|
cat | string | required |
assetIconFor
Function
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
function beamImageId(surface: MarkerSurface): string;
Sprite and image-registry id of the facing beam.
| Parameter | Type | Default | Description |
|---|---|---|---|
surface | MarkerSurface | required |
Options and types: MarkerSurface.
borderPx
Function
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 | required | |
surface | MarkerSurface | required |
Options and types: MarkerTier, MarkerSurface.
faceShape
Function
function faceShape(id: string): FaceShape | undefined;
The shape of a face id.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | string | required |
faceSvg
Function
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 | required | |
opts? | { size?: number; surface?: MarkerSurface; tier?: MarkerTier; label?: string; } | not stated |
Options and types: FaceRef, MarkerSurface, MarkerTier.
facingSvg
Function
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 | not stated |
Options and types: MarkerSurface.
layoutFace
Function
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 | required | |
size | number | required | |
surface? | MarkerSurface | not stated |
Options and types: FaceRef, MarkerSurface.
markerImageId
Function
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[] | required | |
tier | MarkerTier | required | |
surface | MarkerSurface | required |
Options and types: FaceRef, MarkerTier, MarkerSurface.
puckBox
Function
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 | required |
Options and types: PuckTier.
puckGroup
Function
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 | required | |
group? | string | not stated |
Options and types: AssetGlyphId.
puckImageId
Function
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 | required | |
group? | string | not stated |
Options and types: MarkerSurface.
puckSvg
Function
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 | required | |
surface? | MarkerSurface | not stated | |
group? | AssetGroupId | not stated |
Options and types: AssetGlyphId, MarkerSurface, AssetGroupId.
resolveFace
Function
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
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
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
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[] | required | |
opts? | { tier?: MarkerTier; surface?: MarkerSurface; lift?: number; } | not stated |
Options and types: FaceRef, MarkerTier, MarkerSurface.
Example
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, FaceRef, FaceRule, FaceTables, ResolvedFace, SignMarker
FaceLayout
Interface
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
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
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
interface FaceTables
| Property | Type | Default | Description |
|---|---|---|---|
templates | Record<string, string> | required | templates/face/*.svg keyed by id. |
numerals | GlyphFamily | required | numerals/transport (digits, k, m, h, /). |
letters | 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
interface ResolvedFace extends FaceRef
Extends 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
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, AssetGroupId, FaceShape, FaceTemplateId, MarkerSurface, MarkerTier, PlateShape, PuckSurface, PuckTier, RoadsideMarker
AssetGlyphId
Type
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
type AssetGroupId = "carriageway" | "furniture" | "green" | "waste" | "commercial" | "works" | "heritage";
A Roadside legend group id.
FaceShape
Type
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
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
type MarkerSurface = "light" | "dark";
The ground a marker is drawn on: "light" (Day map) or "dark" (Night map).
MarkerTier
Type
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
type PlateShape = "roundel" | "triangle" | "rect";
Shapes an unread or rear plate can take.
PuckSurface
Type
type PuckSurface = "light" | "dark";
PuckTier
Type
type PuckTier = "safety" | "furniture" | "ground";
RoadsideMarker
Type
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, assetGlyphGroups, assetGlyphInfo, assetGlyphs, assetGroupColours, assetGroups, assetMap, assetTiers, BEAM_PX, faceRules, faceTemplates, MARKER_COLOURS, MIN_CAP_PX, PLATE_MAX_WIDTH, PUCK_COLOURS, PUCK_STYLES, SHAPE_SCALE, signAttribution, signShortCredit, STEM, TIER_PX
assetFallbackGroup
Constant
const assetFallbackGroup: AssetGroupId;
The group a category outside every group falls into.
assetGlyphGroups
Constant
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
const assetGlyphInfo: Readonly<Record<AssetGlyphId, {
name: string;
tier: PuckTier;
}>>;
Each glyph's name (legend, card) and tier.
assetGlyphs
Constant
const assetGlyphs: Readonly<Record<AssetGlyphId, string>>;
Asset glyphs: 24-unit SVG, currentColor, secondary class="mm2" at 0.35 opacity.
assetGroupColours
Constant
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
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
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
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
const BEAM_PX = 44;
Size of the facing beam image, px (square; the beam's apex is its centre).
faceRules
Constant
const faceRules: readonly FaceRule[];
The face map rules (map-assets/signs/sources/roadside-face-map.json): first match wins.
faceTemplates
Constant
const faceTemplates: Readonly<Record<FaceTemplateId, string>>;
Face templates (SVG, 100 units high, with slots).
MARKER_COLOURS
Constant
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
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
const PLATE_MAX_WIDTH = 4;
A worded plate (a street name) never grows wider than this many nominal sizes.
PUCK_COLOURS
Constant
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
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
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
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
const signShortCredit = "Sign images Crown copyright, OGL v3";
The one-line map credit form of the attribution.
STEM
Constant
const STEM: {
readonly length: 9;
readonly width: 1.5;
readonly gap: 2;
};
Stem geometry of the full tier, px.
TIER_PX
Constant
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
import { /* … */ } from "@mapmap/maps/models";
Classes (@mapmap/maps/models)
ModelLayer, TransitVehicles3dLayer
ModelLayer
Class
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): voidonRemove(): voidrender(_gl: WebGLRenderingContext | WebGL2RenderingContext, args: unknown): voidadd(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; aclip: truefootprint 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
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
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): voidonRemove(): voidrender(_gl: WebGLRenderingContext | WebGL2RenderingContext, args: unknown): voiddispose(): void: Remove the layer, hand the sprite back to the 2D layer and free the GPU buffers.
Example
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, bus3dFade, busDrawType, busGeometry, busInstances, busScaleFloor, decodeBusModel, defaultLights, doubleDeckerGeometry, followedRingGeometry, footprintPolygon, lngLatFromLocal, loadGlb, localOffset, metresPerPixel, placeModel, roundedRectOutline
bearingToRotationY
Function
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
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
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 | not stated |
Options and types: TransitDrawnVehicle, BusVehicleType.
busGeometry
Function
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): 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 | not stated | |
livery? | "day" | "night" | not stated |
Options and types: BusVehicleType.
busInstances
Function
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). 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[] | required | |
anchor | [number, number] | required | |
scale | number | required | |
max | number | required | |
defaultType? | BusVehicleType | not stated |
Options and types: TransitDrawnVehicle, BusVehicleType.
busScaleFloor
Function
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
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 | not stated |
Options and types: BusVehicleType.
defaultLights
Function
function defaultLights(): THREE.Light[];
The London demo's daylight: a sky/ground hemisphere and an afternoon sun from the south-west.
doubleDeckerGeometry
Function
function doubleDeckerGeometry(livery?: "day" | "night"): THREE.BufferGeometry;
The modelled UK double-decker, 10.9 x 2.55 x 4.3 m (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
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 | not stated |
Options and types: BusVehicleType.
footprintPolygon
Function
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
function lngLatFromLocal(anchor: [number, number], x: number, z: number): [number, number];
The inverse of 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
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
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
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
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 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 | required | |
anchor? | [number, number] | not stated |
Options and types: Placement.
roundedRectOutline
Function
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, BusInstances, DecodedBusModel, ModelLayerOptions, ModelSpec, Placement, TransitVehicles3dOptions
BusFleet
Interface
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
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
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
interface ModelLayerOptions
| Property | Type | Default | Description |
|---|---|---|---|
id? | string | "mapmap-models" | Default "mapmap-models". |
models? | ModelSpec[] | not stated | Models to add straight away (errors are logged; use add to await them). |
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
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
interface Placement
A model's placement, in the shape placeModel reads.
| Property | Type | Default | Description |
|---|---|---|---|
lngLat | [number, number] | required | |
altitudeM? | number | not stated | |
bearingDeg? | number | not stated | |
scale? | number | not stated |
TransitVehicles3dOptions
Interface
interface TransitVehicles3dOptions
Options for 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 | 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_DIMENSIONS, DOUBLE_DECKER, TRANSIT_3D_DEFAULTS, TRANSIT_3D_PITCH_RAMP
BUS_3D_LIVERY
Constant
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
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
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
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.
TRANSIT_3D_PITCH_RAMP
Constant
const TRANSIT_3D_PITCH_RAMP = 5;
The pitch band, degrees, below minPitch over which the buses fade in.
@mapmap/maps/tiles
import { /* … */ } from "@mapmap/maps/tiles";
Classes (@mapmap/maps/tiles)
TilesLayer
Class
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): voidonRemove(): voidrender(_gl: WebGLRenderingContext | WebGL2RenderingContext, args: unknown): voiddispose(): void: Remove the layer, its clip and every loaded tile.
Example
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
Interface
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.
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 | 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
import { /* … */ } from "@mapmap/maps/live";
Classes (@mapmap/maps/live)
AuroraLayer, EarthEventsLayer, WeatherLayer, WindLayer
AuroraLayer
Class
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 everyrefreshMs. 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
import { map } from "./map.ts";
import { AuroraLayer } from "@mapmap/maps/live";
const aurora = new AuroraLayer(map);
aurora.start();
EarthEventsLayer
Class
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 openonSelect.pulsing(): string[]: Quake ids pulsing now or waiting to (for tests).
Example
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
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 frameindex(clamped). The old frame stays up until it is drawable.setTime(atMs: number | null): void: Show the frame whose valid time is nearestatMs(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
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
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 nearestatMs(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; seewindDimUnderField).setVisible(on: boolean): voidsetFollowing(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(): WindReportsample(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
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, buildFieldMesh, decodeValue, detailFromFeature, equirectUv, eventIds, fieldStops, hexRgb01, liveFetch, mercatorY, nearestFrameIndex, parseAuroraFile, pickWindFrame, preferHalf, quakeRadius, solarElevationDeg, subsolarPoint, timeAgo, windDimUnderField, windHeadingDeg, windSpeed
auroraLabel
Function
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
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
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.
detailFromFeature
Function
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.
equirectUv
Function
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
function eventIds(id: string): EventIds;
The source, layer and image ids for a prefix.
| Parameter | Type | Default | Description |
|---|---|---|---|
id | string | required |
fieldStops
Function
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
function hexRgb01(hex: string): [number, number, number];
| Parameter | Type | Default | Description |
|---|---|---|---|
hex | string | required |
liveFetch
Function
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 | not stated |
Options and types: LiveFetchOptions.
mercatorY
Function
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
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
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
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
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
function quakeRadius(mag: number): number;
| Parameter | Type | Default | Description |
|---|---|---|---|
mag | number | required |
solarElevationDeg
Function
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
function subsolarPoint(atMs: number): [number, number];
Subsolar point [lng, lat] for a moment (epoch milliseconds).
| Parameter | Type | Default | Description |
|---|---|---|---|
atMs | number | required |
timeAgo
Function
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
function windDimUnderField(zoom: number): number;
| Parameter | Type | Default | Description |
|---|---|---|---|
zoom | number | required |
windHeadingDeg
Function
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
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, AuroraLayerOptions, AuroraReport, AuroraSource, EarthEventsFeedReport, EarthEventsLayerOptions, EarthEventsReport, EventIds, FieldEncoding, FieldMesh, FieldStop, LiveFetchOptions, WeatherFrame, WeatherLayerOptions, WeatherManifest, WeatherReport, WeatherSource, WindFrameRef, WindLayerOptions, WindMeta, WindReport, WindSource
AuroraFile
Interface
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
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 | 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
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
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
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
interface EarthEventsLayerOptions
| Property | Type | Default | Description |
|---|---|---|---|
types? | 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 | 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 | 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
interface EarthEventsReport
| Property | Type | Default | Description |
|---|---|---|---|
quake | EarthEventsFeedReport | required | |
volcano | EarthEventsFeedReport | required | |
fire | 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
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
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
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
interface FieldStop
| Property | Type | Default | Description |
|---|---|---|---|
value | number | required | |
colour | string | required | |
alpha | number | required |
LiveFetchOptions
Interface
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
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
interface WeatherLayerOptions
| Property | Type | Default | Description |
|---|---|---|---|
field? | WeatherField | null | 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 | 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
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
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 | required | |
isobars | boolean | required | |
frames | 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
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
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
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 | 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
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
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
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, EventDetail, EventsBase, EventType, WeatherField
EarthEventsSource
Type
type EarthEventsSource = {
url: string;
} | {
files: Record<EventType, string>;
status: string;
};
Where the layer reads from, other than the map's gateway.
EventDetail
Type
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
type EventsBase = "light" | "dark";
The map ink the quake ring is drawn in.
EventType
Type
type EventType = "quake" | "volcano" | "fire";
The three kinds of event.
WeatherField
Type
type WeatherField = WeatherFieldId;
The four colour fields.
Constants (@mapmap/maps/live)
AURORA_SHORT_CREDIT, AURORA_STALE_MS, DEFAULT_LIVE_LABEL, EVENT_TYPES, EVENTS_SHORT_CREDITS, FIELD_PNG_HEIGHT, FIELD_PNG_WIDTH, FIRES_HONEST, VOLCANOES_HONEST, WEATHER_FIELDS, WEATHER_SHORT_CREDIT, WIND_PARTICLES_DESKTOP, WIND_PARTICLES_TOUCH
AURORA_SHORT_CREDIT
Constant
const AURORA_SHORT_CREDIT = "Aurora: NOAA SWPC forecast";
AURORA_STALE_MS
Constant
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
const DEFAULT_LIVE_LABEL = "Feed status unknown";
The label a live layer reports before anything vouches for its feed.
EVENT_TYPES
Constant
const EVENT_TYPES: readonly EventType[];
All three event types, in draw order (bottom to top: fires, quakes, volcanoes).
EVENTS_SHORT_CREDITS
Constant
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
const FIELD_PNG_HEIGHT = 721;
FIELD_PNG_WIDTH
Constant
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 ascolor-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
projectTileputs 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
const FIRES_HONEST = "Satellite-detected heat, not confirmed fires";
The honest labels (contract section 8).
VOLCANOES_HONEST
Constant
const VOLCANOES_HONEST = "US-monitored volcanoes only";
WEATHER_FIELDS
Constant
const WEATHER_FIELDS: readonly WeatherField[];
The fields, in the order a control would list them.
WEATHER_SHORT_CREDIT
Constant
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
const WIND_PARTICLES_DESKTOP = 65536;
Particle counts by device class (contract section 11).
WIND_PARTICLES_TOUCH
Constant
const WIND_PARTICLES_TOUCH = 16384;