@mapmap/maps 0.5.2 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import maplibregl, { CustomLayerInterface, Map, StyleSpecification, MapOptions, ExpressionSpecification } from 'maplibre-gl';
2
- export { DirectionIconName, DirectionIconStep, directionIconSvg, directionIcons, iconNameForManeuver, iconNameForStep } from './direction-icons.js';
2
+ export { DirectionIconName, DirectionIconStep, directionIconSvg, directionIcons, iconNameForManeuver, iconNameForStep, iconNamesForSteps } from './direction-icons.js';
3
3
 
4
4
  /**
5
5
  * Shared public types for @mapmap/maps.
@@ -518,6 +518,26 @@ interface NavRouteDesign {
518
518
  opacity: number;
519
519
  /** Casing (outline) colour drawn under the line. */
520
520
  casingColor: string;
521
+ /**
522
+ * Casing width in px. Defaults to `width + 4`.
523
+ *
524
+ * Optional because the casing was previously always `width + 4`, which
525
+ * cannot express a design whose casing is not exactly four wider than
526
+ * its line (the playground's is 9 over a 4.5 line).
527
+ */
528
+ casingWidth?: number;
529
+ /**
530
+ * Casing opacity, 0-1. Defaults to {@link NavRouteDesign.opacity}.
531
+ *
532
+ * Optional because casing and line previously shared one opacity, which
533
+ * cannot express a translucent casing under a solid line.
534
+ */
535
+ casingOpacity?: number;
536
+ /**
537
+ * Line dash pattern, in line-widths, as MapLibre's `line-dasharray`.
538
+ * Omitted or `null` draws a solid line.
539
+ */
540
+ dash?: number[] | null;
521
541
  }
522
542
  /** Current-position puck tokens. */
523
543
  interface NavPuckDesign {
@@ -902,7 +922,8 @@ declare function navDesignFromThemeUrl(url: string, fetchImpl?: typeof fetch): P
902
922
  * and expression building. The class->category mapping mirrors
903
923
  * `website/lib/poi-icons.ts` (`POI_CLASS_CATEGORIES`). Keep them in sync so
904
924
  * a theme previews in Studio exactly as the SDK applies it. Not imported
905
- * from the website because the packages do not depend on each other.
925
+ * from the website because nothing here may depend on the website: the
926
+ * dependency runs the other way (the website installs `@mapmap/maps`).
906
927
  *
907
928
  * PUBLISH NOTE: the block travels with the THEME DOCUMENT, never with the
908
929
  * compiled `style.json`. The canonical `sn_style` crate stores `extra`
@@ -1089,7 +1110,24 @@ declare class MapMapMap {
1089
1110
  private effectGeometry;
1090
1111
  private effectExplicit;
1091
1112
  private effectLayer;
1113
+ /** True while an effect re-apply is already queued behind the style. */
1114
+ private effectWaiting;
1115
+ private destroyed;
1116
+ private readonly pendingWaits;
1092
1117
  constructor(options: MapMapOptions);
1118
+ /**
1119
+ * Run `action` as soon as the style can take layer edits, or now if it
1120
+ * already can.
1121
+ *
1122
+ * Never wait on `load` alone: MapLibre fires it exactly once per map, so
1123
+ * anything deferred after the first load is dropped for good - and
1124
+ * `isStyleLoaded()` reports false for the whole of every `style.load`
1125
+ * (the new style's sources are still loading) and whenever a source is
1126
+ * loading afterwards. Waiting for whichever of `style.load` / `idle` /
1127
+ * `load` arrives first covers the first load and every later style swap,
1128
+ * and the listeners detach as soon as one fires (or on `destroy()`).
1129
+ */
1130
+ private whenStyleReady;
1093
1131
  private readonly handleStyleLoadForEffects;
1094
1132
  /** The current style's `metadata`, if it can be read yet. */
1095
1133
  private styleMetadata;
@@ -1147,24 +1185,450 @@ declare class MapMapMap {
1147
1185
  * (maplibre-native#4107 — fill-extrusion memory at street zooms).
1148
1186
  */
1149
1187
  setBuildings3d(enabled: boolean): void;
1150
- /** Resolves once the style and first tiles are loaded. */
1188
+ /**
1189
+ * Resolves once the style and first tiles are loaded.
1190
+ *
1191
+ * Safe to call at any point in the map's life, not just before its
1192
+ * first load: `map.loaded()` goes false again whenever a source is
1193
+ * loading (a theme swap, a pan into new tiles) while `load` only ever
1194
+ * fires once, so this waits on `idle` too rather than hanging.
1195
+ */
1151
1196
  whenReady(): Promise<void>;
1152
- /** Remove the map and release its WebGL context. */
1197
+ /**
1198
+ * Remove the map and release its WebGL context. Anything still queued
1199
+ * behind a style load is dropped first, so nothing touches the map
1200
+ * after this returns.
1201
+ */
1153
1202
  destroy(): void;
1154
1203
  }
1155
1204
  /** Functional alias for {@link MapMapMap}, mirroring the Mapbox `new Map` feel. */
1156
1205
  declare function createMap(options: MapMapOptions): MapMapMap;
1157
1206
 
1207
+ /**
1208
+ * GENERATED FILE — do not edit by hand.
1209
+ *
1210
+ * LOCKSTEP: identical copies of this module are emitted to
1211
+ * web-sdk/src/marker-icons.ts
1212
+ * website/lib/marker-icons.ts
1213
+ * Source of truth: map-assets/icons/markers/*.svg
1214
+ * Regenerate with: node map-assets/icons/markers/generate.mjs
1215
+ *
1216
+ * Every glyph is 24x24 viewBox path data, drawn as a solid shape (nonzero
1217
+ * fill rule; holes are reverse-wound subpaths) that reads clearly in white
1218
+ * at 24 px. The pin body is the teardrop the coloured marker is drawn
1219
+ * with: head centre (12, 9.4), radius 9, tip exactly at (12, 24) so an
1220
+ * `icon-anchor: "bottom"` symbol anchors at the tip.
1221
+ *
1222
+ * The glyph-id list is the LOCKED `theme.extra.markers` v1 contract shared
1223
+ * with MapMap Studio and the gateway's Rust validator — order and spelling
1224
+ * must never drift.
1225
+ */
1226
+ /** The 21 marker glyph ids of the `extra.markers` v1 contract, in order. */
1227
+ declare 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"];
1228
+ /** One marker glyph id (`"pin"`, `"star"`, `"fuel"`, ...). */
1229
+ type MarkerGlyphId = (typeof MARKER_GLYPH_IDS)[number];
1230
+ /** Whether `value` is one of the 21 marker glyph ids. */
1231
+ declare function isMarkerGlyphId(value: unknown): value is MarkerGlyphId;
1232
+ /** 24x24 viewBox SVG path data for every marker glyph, keyed by id. */
1233
+ declare const markerGlyphPaths: Record<MarkerGlyphId, string>;
1234
+ /**
1235
+ * The coloured pin-body teardrop, 24x24 viewBox: head centre (12, 9.4),
1236
+ * radius 9, tip at (12, 24). Every glyph except `dot` is drawn in white
1237
+ * on top of this body, centred on the head.
1238
+ */
1239
+ declare 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";
1240
+ /** The glyph and pin-body coordinate space (a 24x24 viewBox). */
1241
+ declare const MARKER_GLYPH_VIEWBOX = 24;
1242
+
1243
+ /**
1244
+ * MarkersLayer - custom markers & labels from a Studio theme's
1245
+ * `extra.markers` block (schema v1).
1246
+ *
1247
+ * A theme can carry up to 200 designed markers - coloured glyph pins, a
1248
+ * plain dot, or a small custom image - each with an optional text label.
1249
+ * This module renders that block on a MapLibre map: glyph markers are
1250
+ * rasterised on an offscreen canvas at 2x (the `poi-icons` pattern) and
1251
+ * registered via `map.addImage`; custom images are `data:` URIs capped at
1252
+ * 64 KB (the `puck.ts` precedent). Everything draws from ONE GeoJSON
1253
+ * source and ONE symbol layer, both id `mm-user-markers`, with per-feature
1254
+ * `icon-image`, `icon-size`, `icon-anchor`, label and text sizing.
1255
+ *
1256
+ * LOCKSTEP: the schema, glyph set, image caps and cache-key format are a
1257
+ * locked contract shared with MapMap Studio's markers panel and the
1258
+ * gateway's Rust validator. Do not change ids, defaults or key formats
1259
+ * here without coordinating all three.
1260
+ *
1261
+ * Like PlacesLayer, the layer survives `setStyle` (theme swaps) — but NOT
1262
+ * because a diffed `setStyle` leaves it alone. `Style.serialize()` includes
1263
+ * runtime-added sources and layers, so MapLibre's diff sees
1264
+ * `mm-user-markers` in the old style and not in the new one and emits
1265
+ * removeLayer/removeSource for it: EVERY theme swap tears this layer down.
1266
+ * It comes back because `Style.setState` fires `style.load` after a
1267
+ * successful diff as well as after a full reload, and this layer
1268
+ * re-installs on that event. Do not "optimise" the re-install away.
1269
+ * (Runtime IMAGES do survive a successful diff — only a full reload rebuilds
1270
+ * the image registry — so `registerImages` is written to be idempotent.)
1271
+ */
1272
+
1273
+ /** One marker size preset. */
1274
+ type MarkerSize = "s" | "m" | "l";
1275
+ /** The marker size presets, smallest first. */
1276
+ declare const MARKER_SIZES: readonly ["s", "m", "l"];
1277
+ /** CSS pixel size of each marker preset (the rasterised square canvas). */
1278
+ declare const MARKER_SIZES_PX: Record<MarkerSize, number>;
1279
+ /** Label text size in px for each marker size preset. */
1280
+ declare const MARKER_LABEL_TEXT_SIZES: Record<MarkerSize, number>;
1281
+ /** Default marker colour (MapMap marker blue). */
1282
+ declare const MARKER_DEFAULT_COLOUR = "#1a6bff";
1283
+ /** Maximum accepted `data:` marker-image payload (matches the puck's cap). */
1284
+ declare const MAX_MARKER_IMAGE_BYTES: number;
1285
+ /** Maximum markers per theme (`extra.markers` v1 contract). */
1286
+ declare const MAX_MARKER_ITEMS = 200;
1287
+ /** Maximum marker `id` length (contract). */
1288
+ declare const MAX_MARKER_ID_LENGTH = 64;
1289
+ /** Maximum marker `label` length (contract). */
1290
+ declare const MAX_MARKER_LABEL_LENGTH = 120;
1291
+ /**
1292
+ * Maximum characters drawn INSIDE a pin head by the short-text mode of
1293
+ * {@link renderMarkerImage} (numbered waypoints `1`, `2`, `3`; lettered
1294
+ * stops `A`, `B`). Anything longer belongs in a `label` below the pin.
1295
+ */
1296
+ declare const MAX_MARKER_TEXT_LENGTH = 3;
1297
+ /** The shared GeoJSON source id AND symbol layer id (contract). */
1298
+ declare const MARKERS_ID = "mm-user-markers";
1299
+ /** One parsed marker of an `extra.markers` v1 block. */
1300
+ interface MarkerItem {
1301
+ /** Stable unique id, non-empty, at most 64 characters. */
1302
+ id: string;
1303
+ /** Longitude, degrees, [-180, 180]. */
1304
+ lng: number;
1305
+ /** Latitude, degrees, [-90, 90]. */
1306
+ lat: number;
1307
+ /** Glyph drawn on the pin (or the standalone `dot`). Default `"pin"`. */
1308
+ icon: MarkerGlyphId;
1309
+ /**
1310
+ * Optional custom image (`data:` URI, png/jpeg/webp/svg+xml, at most
1311
+ * 64 KB decoded). Overrides `icon`; an unusable image falls back to the
1312
+ * default pin, never to nothing.
1313
+ */
1314
+ image?: string;
1315
+ /** Pin colour, `#rrggbb` (normalised lowercase). Default `#1a6bff`. */
1316
+ colour: string;
1317
+ /** Size preset. Default `"m"` (32 CSS px). */
1318
+ size: MarkerSize;
1319
+ /** Optional label drawn below the marker, at most 120 characters. */
1320
+ label?: string;
1321
+ }
1322
+ /** A parsed `extra.markers` block. */
1323
+ interface MarkersBlock {
1324
+ /** Block schema version; currently always 1. */
1325
+ version: 1;
1326
+ /** The valid markers, in input order, capped at 200. */
1327
+ items: MarkerItem[];
1328
+ }
1329
+ /**
1330
+ * Cut a string to `max` UNICODE SCALARS (not UTF-16 code units). The Rust
1331
+ * validator counts `chars()`, so a plain `slice` both rejected valid emoji
1332
+ * strings and could cut a surrogate pair in half — and a lone surrogate
1333
+ * fails `serde_json` with a JSON parse error instead of the friendly
1334
+ * marker message. LOCKSTEP: the same helper exists in Studio's mirror.
1335
+ */
1336
+ declare function truncateChars(text: string, max: number): string;
1337
+ /**
1338
+ * A usable custom marker image, or `undefined`: a `data:` URI of an
1339
+ * accepted image type (png/jpeg/webp/svg+xml) whose decoded payload is at
1340
+ * most 64 KB. Oversized or foreign URIs are rejected - the marker falls
1341
+ * back to its glyph pin (the `puck.ts` cap precedent).
1342
+ */
1343
+ declare function markerImage(v: unknown): string | undefined;
1344
+ /**
1345
+ * Lenient parse of an `extra.markers` value into a v1 block. A block whose
1346
+ * `version` is not 1 (including a missing one) parses to NOTHING: the Rust
1347
+ * validator requires exactly 1, so a version-less block that rendered here
1348
+ * could never be published, and a future v2 block must not be silently
1349
+ * drawn as v1 by an already-deployed SDK.
1350
+ *
1351
+ * Within a v1 block, invalid ITEMS are skipped (bad/missing id, duplicate
1352
+ * id, out-of-range coordinates); invalid FIELDS on a valid item fall back
1353
+ * to their defaults (unknown glyph -> `pin`, bad colour -> the default
1354
+ * blue, bad size -> `m`, over-long labels truncated, unusable images
1355
+ * dropped). Never throws on bad data - a hand-edited theme cannot break
1356
+ * the map. At most 200 items survive.
1357
+ */
1358
+ declare function parseMarkers(value: unknown): MarkersBlock;
1359
+ /**
1360
+ * Reads and parses the `extra.markers` block from a Studio theme document.
1361
+ * Returns `undefined` when the theme carries no markers block at all (so
1362
+ * callers can tell "no markers" apart from "an empty designed set"); a
1363
+ * present but malformed block parses leniently via {@link parseMarkers}.
1364
+ */
1365
+ declare function markersFromTheme(theme: Theme | undefined | null): MarkersBlock | undefined;
1366
+ /**
1367
+ * Fetches a hosted theme document and reads its `extra.markers` block -
1368
+ * the same endpoint contract as `navDesignFromThemeUrl`:
1369
+ *
1370
+ * ```ts
1371
+ * const markers = await markersFromThemeUrl(
1372
+ * "https://api.mapmap.ai/styles/midnight-fleet-a1b2c3/theme",
1373
+ * );
1374
+ * if (markers) new MarkersLayer(map, markers);
1375
+ * ```
1376
+ *
1377
+ * Returns `undefined` when the theme carries no markers block; throws on
1378
+ * network failure, a non-2xx response or a non-JSON body. Bad DATA never
1379
+ * throws - invalid items are skipped by the lenient parser.
1380
+ *
1381
+ * @param fetchImpl Optional `fetch` replacement (tests, Node polyfills).
1382
+ */
1383
+ declare function markersFromThemeUrl(url: string, fetchImpl?: typeof fetch): Promise<MarkersBlock | undefined>;
1384
+ /**
1385
+ * `map.addImage` cache key for a rasterised glyph marker:
1386
+ * `mm-marker-<icon>-<colour>-<size>` (contract format; the colour is the
1387
+ * normalised lowercase `#rrggbb`). Identical markers share one image.
1388
+ */
1389
+ declare function markerImageKey(icon: MarkerGlyphId, colour: string, size: MarkerSize): string;
1390
+ /**
1391
+ * Normalise a short pin text: trimmed and cut to
1392
+ * {@link MAX_MARKER_TEXT_LENGTH} characters, or `undefined` when nothing
1393
+ * printable remains. Callers use `undefined` to mean "draw the glyph".
1394
+ */
1395
+ declare function markerText(value: unknown): string | undefined;
1396
+ /**
1397
+ * `map.addImage` cache key for a rasterised SHORT-TEXT marker:
1398
+ * `mm-marker-text-<text>-<colour>-<size>`. Deliberately distinct from
1399
+ * {@link markerImageKey} so glyph pins keep their existing keys unchanged.
1400
+ */
1401
+ declare function markerTextImageKey(text: string, colour: string, size: MarkerSize): string;
1402
+ /**
1403
+ * `map.addImage` cache key for a custom-image marker: the data URI is
1404
+ * hashed (FNV-1a), so identical images share one registered copy no
1405
+ * matter how many markers use them.
1406
+ */
1407
+ declare function markerImageDataKey(dataUri: string): string;
1408
+ /** What {@link renderMarkerImage} returns, ready for `map.addImage`. */
1409
+ interface RenderedMarkerImage {
1410
+ /** The rasterised marker, drawn at 2x. */
1411
+ image: ImageData;
1412
+ /** Pass to `addImage` so the marker displays at its CSS pixel size. */
1413
+ pixelRatio: 2;
1414
+ }
1415
+ /** Options for {@link renderMarkerImage}. */
1416
+ interface RenderMarkerImageOptions {
1417
+ /** Glyph to draw. Default `"pin"`. */
1418
+ icon?: MarkerGlyphId;
1419
+ /** Pin (or dot) colour. Default the marker blue. */
1420
+ colour?: string;
1421
+ /** Size preset. Default `"m"` (32 CSS px). */
1422
+ size?: MarkerSize;
1423
+ /**
1424
+ * Short text (1-3 characters) drawn in white in the pin head INSTEAD of
1425
+ * the glyph - numbered waypoints (`"1"`, `"2"`) and lettered stops
1426
+ * (`"A"`, `"B"`). Longer strings are cut to
1427
+ * {@link MAX_MARKER_TEXT_LENGTH}; blank strings fall back to the glyph.
1428
+ * Text always draws the pin body, so `icon: "dot"` is ignored when text
1429
+ * is given (a dot has no head to write in).
1430
+ */
1431
+ text?: string;
1432
+ }
1433
+ /**
1434
+ * Rasterise one marker to `ImageData` for `map.addImage`, per the
1435
+ * rendering contract: every glyph except `dot` is the coloured pin-body
1436
+ * teardrop with the glyph drawn in white centred on the head (anchor the
1437
+ * symbol `bottom` - the tip sits exactly on the canvas's bottom edge);
1438
+ * `dot` is a coloured circle with a white outline (anchor `center`). The
1439
+ * canvas is a square of twice the preset's CSS pixel size, returned with
1440
+ * `pixelRatio: 2`.
1441
+ *
1442
+ * Passing `text` switches on the short-text mode: the same coloured pin
1443
+ * body, with 1-3 white bold characters centred in the head (auto-shrunk to
1444
+ * fit) in place of the glyph. That is what numbered waypoints and lettered
1445
+ * stops are drawn with.
1446
+ *
1447
+ * Returns `null` where canvas 2D or `Path2D` is unavailable (never
1448
+ * expected in a browser; keeps non-DOM environments safe, the
1449
+ * `poi-icons.ts` pattern).
1450
+ */
1451
+ declare function renderMarkerImage(options?: RenderMarkerImageOptions): RenderedMarkerImage | null;
1452
+ /** The generated MapLibre ids (see {@link MarkersLayer.ids}). */
1453
+ interface MarkersLayerIds {
1454
+ /** The GeoJSON source id (`mm-user-markers`). */
1455
+ source: string;
1456
+ /** The symbol layer id (`mm-user-markers`). */
1457
+ layer: string;
1458
+ }
1459
+ /** Options for {@link MarkersLayer}. */
1460
+ interface MarkersLayerOptions {
1461
+ /**
1462
+ * Called when a marker's custom image passes the schema (right media
1463
+ * type, within the 64 KB cap) but MapLibre cannot rasterise it — most
1464
+ * often an SVG with no intrinsic `width`/`height`, which `<img>` renders
1465
+ * happily and `createImageBitmap` rejects. The marker keeps its glyph
1466
+ * pin either way; this is the only way to TELL the user that the logo
1467
+ * they can see in their editor will not be on the map.
1468
+ */
1469
+ onImageError?: (image: string, error: unknown) => void;
1470
+ }
1471
+ /**
1472
+ * Draws a theme's custom markers & labels on a MapLibre map.
1473
+ *
1474
+ * ```ts
1475
+ * const markers = await markersFromThemeUrl(themeUrl);
1476
+ * if (markers) new MarkersLayer(map, markers);
1477
+ * ```
1478
+ *
1479
+ * Registers every needed marker image (rasterised glyph pins are cached by
1480
+ * `mm-marker-<icon>-<colour>-<size>`; custom images by data-URI hash),
1481
+ * builds one GeoJSON feature per marker carrying its image key, label,
1482
+ * precomputed `icon-size` ratio and anchor, and installs the single
1483
+ * `mm-user-markers` source + symbol layer. Survives `setStyle` like
1484
+ * PlacesLayer (re-installed on every `style.load`).
1485
+ */
1486
+ declare class MarkersLayer {
1487
+ private readonly map;
1488
+ private readonly options;
1489
+ private items;
1490
+ private data;
1491
+ private destroyed;
1492
+ /**
1493
+ * Bumped by {@link clear} (and {@link destroy}). An image load started
1494
+ * before the bump resolves into a cleared layer, so its handler compares
1495
+ * the generation it was started in and drops out — without this a
1496
+ * pending `loadImage` would rebuild and RE-INSTALL the layer on a map the
1497
+ * caller had already cleaned up.
1498
+ */
1499
+ private generation;
1500
+ /** Custom images by cache key, once loaded (kept across style swaps). */
1501
+ private readonly loadedImages;
1502
+ /** Custom-image cache keys that failed to load (pin fallback, once). */
1503
+ private readonly failedImages;
1504
+ /** Custom-image cache keys with a load in flight. */
1505
+ private readonly pendingImages;
1506
+ /**
1507
+ * Every `map.addImage` key this layer currently owns, so a rebuild can
1508
+ * drop the ones nothing references any more and teardown removes exactly
1509
+ * what it added (the RouteLayer endpoints pattern). Without it every
1510
+ * icon/colour/size change leaked a 2x RGBA image — 25 KB per click at
1511
+ * the 40px preset — and re-invalidated the sprite atlas.
1512
+ */
1513
+ private readonly registeredImageKeys;
1514
+ constructor(map: MapMapMap | Map, markers?: MarkersBlock, options?: MarkersLayerOptions);
1515
+ private readonly handleStyleLoad;
1516
+ /**
1517
+ * Replace the layer's markers with a parsed block (`undefined` or an
1518
+ * empty block clears the map of markers but keeps the layer alive).
1519
+ * Custom images load asynchronously: their markers render as default
1520
+ * pins until the image arrives, then swap in place; a failed or
1521
+ * oversized image keeps the pin fallback.
1522
+ */
1523
+ setMarkers(markers: MarkersBlock | undefined): void;
1524
+ /** The layer's current markers. */
1525
+ get current(): MarkerItem[];
1526
+ /**
1527
+ * The MapLibre source/layer ids - public API for escape-hatch styling.
1528
+ * Both are the contract id `mm-user-markers`.
1529
+ */
1530
+ get ids(): MarkersLayerIds;
1531
+ /**
1532
+ * Rebuild the source data from the items and (re)install. Mirrors
1533
+ * PlacesLayer's never-drop-an-update rule: once the source exists the
1534
+ * data is applied immediately (even while `isStyleLoaded()` is
1535
+ * transiently false mid-render); before first installation the latest
1536
+ * data is stashed for the next `style.load`.
1537
+ */
1538
+ private rebuild;
1539
+ /** Build one feature, kicking off the custom-image load if needed. */
1540
+ private featureFor;
1541
+ /** Load a custom `data:` image once; rebuild the features when done. */
1542
+ private loadCustomImage;
1543
+ /**
1544
+ * Register every image the current features reference and drop the ones
1545
+ * they no longer do (idempotent). Runtime images survive a diffed
1546
+ * `setStyle` and are rebuilt after a full reload, so this runs on every
1547
+ * install as well as every data rebuild.
1548
+ */
1549
+ private registerImages;
1550
+ /** Add-or-update the images, source and layer on the current style. */
1551
+ private install;
1552
+ /**
1553
+ * Remove the markers' layer, source and registered images from the map.
1554
+ * Images are removed by the keys this layer REGISTERED, not by whatever
1555
+ * `items` happens to hold now — deriving them from the items meant
1556
+ * `setMarkers(undefined)` followed by `clear()`/`destroy()` removed
1557
+ * nothing at all.
1558
+ */
1559
+ clear(): void;
1560
+ /**
1561
+ * Remove the markers and detach the style.load listener. Call when
1562
+ * disposing of the layer (the map is left untouched). Any image load
1563
+ * still in flight resolves into a no-op.
1564
+ */
1565
+ destroy(): void;
1566
+ }
1567
+
1158
1568
  /**
1159
1569
  * RouteLayer - fetch an OSRM route from the MapMap gateway and draw it.
1160
1570
  *
1161
1571
  * The route request/parse logic lives in the pure `osrm` module; this class
1162
1572
  * owns the network call and the MapLibre source/layers (a signal-blue line
1163
- * with a darker casing, matching the brand).
1573
+ * with a darker casing, matching the brand), plus the optional overlays
1574
+ * that hang off a drawn route: alternatives, ferry dashes, the manoeuvre
1575
+ * arrow, average-speed corridors, and — with the `endpoints` option — the
1576
+ * branded start/destination/waypoint markers, drawn as a symbol layer over
1577
+ * their own source so they land in canvas exports and match the native
1578
+ * SDKs.
1164
1579
  */
1165
1580
 
1166
1581
  /** MapMap brand signal blue (see website `--signal`). */
1167
1582
  declare const SIGNAL_BLUE = "#3a86ff";
1583
+ /**
1584
+ * Runtime overrides for the drawn line, applied over the design or the
1585
+ * built-in look. Every field is optional; omitted ones are left alone.
1586
+ */
1587
+ interface RouteLineStyle {
1588
+ /** Line colour. */
1589
+ color?: string;
1590
+ /** Dash pattern in line-widths, or `null` for solid. */
1591
+ dash?: number[] | null;
1592
+ /** Casing opacity, 0-1. Set 0 to hide the casing entirely. */
1593
+ casingOpacity?: number;
1594
+ }
1595
+ /**
1596
+ * One designed endpoint marker: a coloured pin (or dot) drawn from the
1597
+ * shared marker rasteriser, optionally with a text label underneath.
1598
+ */
1599
+ interface EndpointMarker {
1600
+ /** Glyph drawn on the pin, or `"dot"` for a plain circle. */
1601
+ icon?: MarkerGlyphId;
1602
+ /**
1603
+ * Short text (1-3 characters) drawn in the pin head INSTEAD of the
1604
+ * glyph - `"A"`, `"B"`, `"1"`. Overrides `icon`.
1605
+ */
1606
+ text?: string;
1607
+ /** Marker colour, any CSS colour the canvas accepts. */
1608
+ colour?: string;
1609
+ /** Size preset: `"s"` 24 px, `"m"` 32 px, `"l"` 40 px. */
1610
+ size?: MarkerSize;
1611
+ /** Optional caption drawn below the marker, with a white halo. */
1612
+ label?: string;
1613
+ }
1614
+ /**
1615
+ * How a route's start, end and intermediate waypoints are drawn. Every
1616
+ * field is optional; `false` switches an endpoint off entirely.
1617
+ */
1618
+ interface EndpointsDesign {
1619
+ /** The first route coordinate. Default a green `dot`, size `m`. */
1620
+ start?: EndpointMarker | false;
1621
+ /** The last route coordinate. Default a signal-blue `pin`, size `m`. */
1622
+ end?: EndpointMarker | false;
1623
+ /** Every intermediate stop. Default a signal-blue `pin`, size `s`. */
1624
+ waypoint?: EndpointMarker | false;
1625
+ /**
1626
+ * Draw each waypoint's 1-based index in its pin head (`1`, `2`, `3`)
1627
+ * instead of the glyph. Default `true`. An explicit `waypoint.text`
1628
+ * wins over the number.
1629
+ */
1630
+ numberWaypoints?: boolean;
1631
+ }
1168
1632
  interface RouteLayerOptions {
1169
1633
  /** Gateway base URL. Defaults to the map's `baseUrl` when given a map. */
1170
1634
  baseUrl?: string;
@@ -1191,6 +1655,51 @@ interface RouteLayerOptions {
1191
1655
  * route line"). Defaults to a dimmed grey.
1192
1656
  */
1193
1657
  progressColor?: string;
1658
+ /** Casing colour for unselected alternative routes. */
1659
+ alternativeCasingColor?: string;
1660
+ /** Line colour for unselected alternative routes. */
1661
+ alternativeColor?: string;
1662
+ /** Colour of the ferry dashes drawn over the selected route. */
1663
+ ferryColor?: string;
1664
+ /**
1665
+ * Branded start/end/waypoint markers on the drawn route. Off by default:
1666
+ * a layer built without this option behaves exactly as it always has.
1667
+ *
1668
+ * `true` uses the brand defaults (green `dot` at the start, blue `pin`
1669
+ * at the end, smaller numbered blue pins at each intermediate stop); an
1670
+ * {@link EndpointsDesign} overrides any of them, and `false` on one
1671
+ * endpoint drops just that one.
1672
+ *
1673
+ * The coordinates come from the route itself: first coordinate = start,
1674
+ * last = end. Intermediate stops are remembered from
1675
+ * {@link RouteLayer.routePath}'s via points, or set explicitly with
1676
+ * {@link RouteLayer.setWaypoints} when drawing a route parsed elsewhere.
1677
+ *
1678
+ * These are symbol-layer markers, not DOM `maplibregl.Marker`s, so they
1679
+ * appear in canvas exports and match the native SDKs.
1680
+ */
1681
+ endpoints?: boolean | EndpointsDesign;
1682
+ }
1683
+ /** The MapLibre ids a {@link RouteLayer} owns (see {@link RouteLayer.ids}). */
1684
+ interface RouteLayerIds {
1685
+ /** GeoJSON source holding the route line. */
1686
+ source: string;
1687
+ /** Line layer drawn under the route (the darker casing). */
1688
+ casing: string;
1689
+ /** Line layer for the route itself. */
1690
+ line: string;
1691
+ /** GeoJSON source for the manoeuvre arrow. */
1692
+ maneuverSource: string;
1693
+ /** Symbol layer for the manoeuvre arrow. */
1694
+ maneuver: string;
1695
+ /** GeoJSON source for the average-speed corridors. */
1696
+ corridorSource: string;
1697
+ /** Line layer tinting the average-speed corridors. */
1698
+ corridor: string;
1699
+ /** GeoJSON source for the start/end/waypoint markers. */
1700
+ endpointsSource: string;
1701
+ /** Symbol layer drawing the start/end/waypoint markers. */
1702
+ endpoints: string;
1194
1703
  }
1195
1704
  /** Draws MapMap routes on a MapLibre map. */
1196
1705
  declare class RouteLayer {
@@ -1208,22 +1717,69 @@ declare class RouteLayer {
1208
1717
  private readonly arrowImageId;
1209
1718
  private readonly corridorSourceId;
1210
1719
  private readonly corridorLayerId;
1720
+ private readonly endpointsSourceId;
1721
+ private readonly endpointsLayerId;
1722
+ /** The resolved endpoint design, or `undefined` when switched off. */
1723
+ private readonly endpoints;
1724
+ /** Intermediate stops for the endpoint markers, `[lng, lat]` each. */
1725
+ private waypoints;
1726
+ /** Marker images this layer registered, so teardown can remove them. */
1727
+ private readonly endpointImageKeys;
1211
1728
  private readonly design;
1212
1729
  private alertsDesign;
1213
1730
  private readonly progressColor;
1731
+ private readonly altSourceId;
1732
+ private readonly altCasingLayerId;
1733
+ private readonly altLineLayerId;
1734
+ private readonly ferrySourceId;
1735
+ private readonly ferryLayerId;
1736
+ private readonly altCasingColor;
1737
+ private readonly altColor;
1738
+ private readonly ferryColor;
1214
1739
  private lastRoute;
1740
+ /** Unselected alternatives, drawn beneath the selected route. */
1741
+ private alternatives;
1742
+ /** Ferry legs of the selected route, drawn as dashes over its line. */
1743
+ private ferrySegments;
1744
+ private selectHandler;
1745
+ /** Runtime paint override, re-applied after every style reload. */
1746
+ private lineStyle;
1215
1747
  private progress;
1216
1748
  private maneuver;
1217
1749
  private corridors;
1750
+ /**
1751
+ * Monotonic id of the newest route request. A response only draws when it
1752
+ * still owns this value - see {@link RouteLayer.routePath}.
1753
+ */
1754
+ private requestSeq;
1755
+ /** Whether the alternative-line click listener is currently attached. */
1756
+ private altClickBound;
1218
1757
  constructor(map: MapMapMap | Map, options?: RouteLayerOptions);
1219
1758
  private readonly handleStyleLoad;
1759
+ /**
1760
+ * The MapLibre source and layer ids this layer owns - public API for
1761
+ * escape-hatch styling and layer ordering (`map.moveLayer`). All are
1762
+ * derived from the `id` option (default `mapmap-route`).
1763
+ */
1764
+ get ids(): RouteLayerIds;
1220
1765
  /**
1221
1766
  * Route from `from` to `to` (with optional intermediate `via` points passed
1222
1767
  * through `options` is not supported here - pass a coordinate list to
1223
1768
  * {@link routePath} for that), draw the line, and return the parsed route.
1224
1769
  */
1225
1770
  route(from: LngLatLike, to: LngLatLike, options?: RouteOptions): Promise<ParsedRoute>;
1226
- /** Route through an ordered list of coordinates and draw the result. */
1771
+ /**
1772
+ * Route through an ordered list of coordinates and draw the result.
1773
+ *
1774
+ * Concurrent calls are ordered by ISSUE, not by arrival: only the newest
1775
+ * request draws. A destination retyped while the first request is still in
1776
+ * flight used to leave the map showing whichever response happened to land
1777
+ * last, so a driver could be looking at the line to the address they had
1778
+ * already replaced. Each caller still receives its own parsed route, so a
1779
+ * caller that wants the data of a superseded request keeps getting it -
1780
+ * only the drawing is suppressed. {@link RouteLayer.clear} likewise
1781
+ * supersedes anything in flight.
1782
+ */
1227
1783
  routePath(points: LngLatLike[], options?: RouteOptions): Promise<ParsedRoute>;
1228
1784
  /** The most recently drawn route, if any. */
1229
1785
  get current(): ParsedRoute | undefined;
@@ -1235,6 +1791,98 @@ declare class RouteLayer {
1235
1791
  draw(route: ParsedRoute): void;
1236
1792
  /** Add-or-update the source and layers for a route on the current style. */
1237
1793
  private install;
1794
+ /**
1795
+ * Set the intermediate stops drawn by the endpoint markers, in order.
1796
+ * {@link routePath} fills these in from its own via points, so this is
1797
+ * only needed when drawing a route parsed elsewhere (e.g. a route fetched
1798
+ * by the app and handed to {@link draw}). An empty array clears them.
1799
+ */
1800
+ setWaypoints(waypoints: readonly LngLatLike[]): void;
1801
+ /** The intermediate stops currently drawn, `[lng, lat]` each. */
1802
+ get currentWaypoints(): [number, number][];
1803
+ /**
1804
+ * Build one Point feature per endpoint marker: start (first route
1805
+ * coordinate), each waypoint in order, then end (last coordinate). The
1806
+ * end is added last so it wins MapLibre's symbol-collision ordering when
1807
+ * markers overlap on a short route.
1808
+ */
1809
+ private endpointFeatures;
1810
+ /**
1811
+ * Register every endpoint marker image the current features need
1812
+ * (idempotent; `setStyle` wipes runtime images, so this runs on each
1813
+ * install). Keys are remembered so teardown removes exactly what this
1814
+ * layer added.
1815
+ */
1816
+ private registerEndpointImages;
1817
+ /** Add-or-update the endpoint markers' source and symbol layer. */
1818
+ private installEndpoints;
1819
+ /** Remove the endpoint markers, their source and their images. */
1820
+ private clearEndpoints;
1821
+ /**
1822
+ * Draw a bare line from coordinates, without a parsed OSRM route.
1823
+ *
1824
+ * {@link RouteLayer.draw} expects a {@link ParsedRoute} because it is
1825
+ * normally fed by {@link RouteLayer.route}. Callers that already have
1826
+ * geometry and nothing else — an agent tool result, a stored polyline,
1827
+ * a hand-built preview — had to invent a `ParsedRoute` with zeroed
1828
+ * distance and duration that `draw` never reads. This is that path,
1829
+ * named honestly.
1830
+ */
1831
+ drawGeometry(coordinates: [number, number][]): void;
1832
+ /**
1833
+ * Draw a set of routes: one selected, the rest as dimmer alternatives
1834
+ * beneath it. Offering alternatives is table stakes for a navigation UI,
1835
+ * and the selected route keeps every feature of {@link RouteLayer.draw}
1836
+ * (progress, manoeuvre arrow, corridors, route effects).
1837
+ *
1838
+ * Pass the index of the route to select; out-of-range values clamp to the
1839
+ * first route. Calling with a single route is equivalent to `draw`, and
1840
+ * calling with an empty array clears everything.
1841
+ */
1842
+ drawAlternatives(routes: ParsedRoute[], selectedIndex?: number): void;
1843
+ /** Which alternatives are currently drawn, in the order given. */
1844
+ get alternativeRoutes(): readonly ParsedRoute[];
1845
+ /**
1846
+ * Register a click handler for the alternative lines. The index is the
1847
+ * position within the array last passed to
1848
+ * {@link RouteLayer.drawAlternatives}, so a caller can re-issue that call
1849
+ * with the new selection. Passing `undefined` removes the handler.
1850
+ */
1851
+ onSelectAlternative(handler: ((index: number) => void) | undefined): void;
1852
+ /**
1853
+ * Draw ferry legs of the selected route as dashes over its line, so water
1854
+ * crossings do not read as driving.
1855
+ *
1856
+ * Supplied as explicit geometries rather than derived from the route,
1857
+ * because `line-dasharray` cannot be data-driven: a ferry leg needs its
1858
+ * own layer, and only the caller knows which parts of their route are
1859
+ * ferries.
1860
+ */
1861
+ setFerrySegments(segments: [number, number][][]): void;
1862
+ /** Remove the ferry dashes. */
1863
+ clearFerrySegments(): void;
1864
+ /** Add-or-update the alternative-route source and its two layers. */
1865
+ private installAlternatives;
1866
+ private readonly handleAlternativeClick;
1867
+ /** Original array position of each drawn alternative, by its own index. */
1868
+ private altOriginalIndices;
1869
+ private removeAlternativeLayers;
1870
+ /** Add-or-update the ferry-dash overlay above the selected route. */
1871
+ private installFerry;
1872
+ /**
1873
+ * Override the drawn line's paint at runtime, over whatever the design or
1874
+ * the built-in look set.
1875
+ *
1876
+ * Route styling is not always static: a route can be provisional, or a
1877
+ * straight-line approximation that must not be mistaken for a surveyed
1878
+ * one. That is a paint change on a route already drawn, so it belongs
1879
+ * here rather than in the constructor's design.
1880
+ *
1881
+ * Overrides survive style reloads. Pass `{}` to clear them.
1882
+ */
1883
+ setLineStyle(style: RouteLineStyle): void;
1884
+ /** Apply any runtime override; a no-op when the layers are absent. */
1885
+ private applyLineStyle;
1238
1886
  /**
1239
1887
  * Sets how much of the route has been travelled, as a fraction in `[0, 1]`
1240
1888
  * of the line's length. The travelled part dims to `progressColor` (the
@@ -1275,7 +1923,11 @@ declare class RouteLayer {
1275
1923
  clearAlertCorridors(): void;
1276
1924
  /** Add-or-update the corridor source/layer for the current style. */
1277
1925
  private installCorridors;
1278
- /** Remove the route's layers and source from the map. */
1926
+ /**
1927
+ * Remove the route's layers and source from the map. Any route request
1928
+ * still in flight is superseded, so a late response cannot redraw a route
1929
+ * the caller has already cleared.
1930
+ */
1279
1931
  clear(): void;
1280
1932
  /**
1281
1933
  * Remove the route and detach the layer's `style.load` listener. Call
@@ -1567,7 +2219,11 @@ interface UploadProbeOptions {
1567
2219
  backoffMs?: (attempt: number) => number;
1568
2220
  /** Injectable delay, for deterministic tests (default `setTimeout`). */
1569
2221
  sleep?: (ms: number) => Promise<void>;
1570
- /** Abort signal forwarded to fetch. */
2222
+ /**
2223
+ * Abort signal forwarded to fetch. Once it has aborted the retry loop
2224
+ * stops immediately (an abort is a caller decision, not a transient
2225
+ * failure) and the upload resolves `"gaveUp"`.
2226
+ */
1571
2227
  signal?: AbortSignal;
1572
2228
  }
1573
2229
  /** The `/v1/probe` endpoint for a gateway origin (trailing slashes trimmed). */
@@ -1575,8 +2231,9 @@ declare function buildProbeUrl(baseUrl: string): string;
1575
2231
  /**
1576
2232
  * POST one probe body to `{baseUrl}/v1/probe` with a bearer key. Transient
1577
2233
  * failures (network or `5xx`) are retried with bounded backoff; permanent ones
1578
- * (`400`/`403`/`501`) are not. Resolves with the terminal outcome and never
1579
- * rejects, so a fire-and-forget `void uploadProbeBatch(...)` is safe.
2234
+ * (`400`/`403`/`501`) are not, and neither is an aborted `signal`. Resolves
2235
+ * with the terminal outcome and never rejects, so a fire-and-forget
2236
+ * `void uploadProbeBatch(...)` is safe.
1580
2237
  *
1581
2238
  * @param body the string returned by `GuidanceSession.finishProbeJson()`.
1582
2239
  */
@@ -1645,6 +2302,33 @@ interface PlacesIcon {
1645
2302
  /** MapLibre `icon-size` scale factor. Defaults to `1`. */
1646
2303
  size?: number;
1647
2304
  }
2305
+ /**
2306
+ * Options for the per-place name labels (see `PlacesLayerOptions.label`).
2307
+ *
2308
+ * The colour keys accept BOTH spellings: `colour`/`haloColour` (British,
2309
+ * as first shipped) and `color`/`haloColor` (matching the sibling
2310
+ * `color`/`clusterColor` options on {@link PlacesLayerOptions}). A
2311
+ * dynamically built options object should not lose its labels' colours to
2312
+ * a spelling mismatch. British wins if somehow both are given.
2313
+ */
2314
+ interface PlacesLabelOptions {
2315
+ /**
2316
+ * Feature property holding the label text (see {@link Place.properties}
2317
+ * for what is addressable). Defaults to `"name"` - every place carries
2318
+ * its `name` there.
2319
+ */
2320
+ property?: string;
2321
+ /** Text size in px. Defaults to `12`. */
2322
+ size?: number;
2323
+ /** Text colour. Defaults to a dark ink (`#333333`). Alias: `color`. */
2324
+ colour?: string;
2325
+ /** Text colour, American spelling of {@link PlacesLabelOptions.colour}. */
2326
+ color?: string;
2327
+ /** Halo colour behind the text. Defaults to white. Alias: `haloColor`. */
2328
+ haloColour?: string;
2329
+ /** Halo colour, American spelling of {@link PlacesLabelOptions.haloColour}. */
2330
+ haloColor?: string;
2331
+ }
1648
2332
  /** Options for {@link PlacesLayer.nearestByDriveTime}. */
1649
2333
  interface NearestByDriveTimeOptions {
1650
2334
  /** How many places to return. Defaults to `1`. */
@@ -1688,8 +2372,29 @@ interface PlacesLayerOptions {
1688
2372
  /**
1689
2373
  * Custom pin image for unclustered places (a symbol layer instead of the
1690
2374
  * default circle). If the image fails to load, the circle look is used.
2375
+ *
2376
+ * Also accepts a record of images keyed by a feature-property value for
2377
+ * per-place icons - e.g. `{ cafe: { url: … }, fuel: { url: … } }` picks
2378
+ * an image per place from the property named by `iconProperty` (default
2379
+ * `"kind"`). Places whose property value has no entry - or whose entry's
2380
+ * image fails to load - keep the default circle look.
1691
2381
  */
1692
- icon?: PlacesIcon;
2382
+ icon?: PlacesIcon | Record<string, PlacesIcon>;
2383
+ /**
2384
+ * Feature property that selects a per-place icon when `icon` is a
2385
+ * record (ignored for a single icon). Defaults to `"kind"`. Set place
2386
+ * `properties` values to the record's keys.
2387
+ */
2388
+ iconProperty?: string;
2389
+ /**
2390
+ * Per-place name labels beside the points. `true` labels every place
2391
+ * with its `name`; an options object customises the property, size and
2392
+ * colours (see {@link PlacesLabelOptions}). Labels are drawn in the map
2393
+ * font with a halo, hide before they collide (`text-optional`), and
2394
+ * only ever appear on unclustered points. Omit for no labels (today's
2395
+ * behaviour).
2396
+ */
2397
+ label?: boolean | PlacesLabelOptions;
1693
2398
  /**
1694
2399
  * Fit the map to the places the first time they are set. Defaults to
1695
2400
  * `false` (the map view is left alone).
@@ -1723,6 +2428,13 @@ interface PlacesLayerIds {
1723
2428
  clusters: string;
1724
2429
  /** The cluster count badge layer id (only installed with `cluster: true`). */
1725
2430
  clusterCounts: string;
2431
+ /** The labels symbol layer id (only installed with a `label` option). */
2432
+ labels: string;
2433
+ /**
2434
+ * The circle layer drawn under places without a loaded per-place icon
2435
+ * (only installed when `icon` is a record of images).
2436
+ */
2437
+ pointsFallback: string;
1726
2438
  }
1727
2439
  /** Options for {@link PlacesLayer.select}. */
1728
2440
  interface PlacesSelectOptions {
@@ -1756,13 +2468,20 @@ declare class PlacesLayer {
1756
2468
  private readonly clustersLayerId;
1757
2469
  private readonly clusterCountLayerId;
1758
2470
  private readonly pointsLayerId;
2471
+ private readonly pointsFallbackLayerId;
2472
+ private readonly labelsLayerId;
1759
2473
  private readonly iconImageId;
1760
2474
  private readonly cluster;
1761
2475
  private readonly clusterRadius;
1762
2476
  private readonly clusterMaxZoom;
1763
2477
  private readonly color;
1764
2478
  private readonly clusterColor;
1765
- private readonly icon;
2479
+ private readonly singleIcon;
2480
+ private readonly iconRecord;
2481
+ private readonly iconProperty;
2482
+ private readonly label;
2483
+ /** Per-place icon keys whose image failed to load (circle fallback). */
2484
+ private readonly failedIconKeys;
1766
2485
  private readonly wantFitBounds;
1767
2486
  private readonly onPlaceClick;
1768
2487
  private readonly popupFn;
@@ -1826,6 +2545,11 @@ declare class PlacesLayer {
1826
2545
  * attached; unreachable places (`null` duration) are dropped. Needs the
1827
2546
  * gateway `baseUrl`/`apiKey` - picked up from a `MapMapMap` automatically,
1828
2547
  * or pass them as layer options.
2548
+ *
2549
+ * Answers always describe the places that were asked about: the list is
2550
+ * snapshotted at call time, so a `setPlaces` landing mid-flight (a
2551
+ * search-as-you-type filter, say) cannot mis-pair the matrix rows with a
2552
+ * different set of places.
1829
2553
  */
1830
2554
  nearestByDriveTime(origin: {
1831
2555
  lat: number;
@@ -1833,6 +2557,13 @@ declare class PlacesLayer {
1833
2557
  }, options?: NearestByDriveTimeOptions): Promise<PlaceWithDriveTime[]>;
1834
2558
  /** Add-or-update the source and layers on the current style. */
1835
2559
  private install;
2560
+ /**
2561
+ * The per-place name labels layer (only with a `label` option): the map
2562
+ * font with a halo, anchored just below each unclustered point, hidden
2563
+ * before colliding (`text-optional`). Re-installed with the rest of the
2564
+ * layers on every `style.load`.
2565
+ */
2566
+ private installLabelsLayer;
1836
2567
  /** Cluster circles (sized by count) and the count badge on top. */
1837
2568
  private installClusterLayers;
1838
2569
  /**
@@ -1841,6 +2572,17 @@ declare class PlacesLayer {
1841
2572
  * circle look if it fails, like PositionPuck's image fallback).
1842
2573
  */
1843
2574
  private installPointsLayer;
2575
+ /** The `map.addImage` id for one key of the per-place icon record. */
2576
+ private iconImageIdFor;
2577
+ /**
2578
+ * Per-place icon mode: load every record image (each key independently -
2579
+ * one bad URL never sinks the rest), then install a symbol layer whose
2580
+ * `icon-image` is a match expression on `iconProperty`, plus a circle
2581
+ * fallback layer for places whose property value has no loaded icon.
2582
+ */
2583
+ private installPerPlaceIconLayers;
2584
+ /** Install the per-place icon symbol layer + circle fallback layer. */
2585
+ private addPerPlaceLayers;
1844
2586
  private addCirclePointsLayer;
1845
2587
  private addIconPointsLayer;
1846
2588
  /** Open the built-in popup for a place, replacing any previous one. */
@@ -2177,7 +2919,15 @@ declare class IsochroneLayer {
2177
2919
  showReachability(options: ShowReachabilityOptions): Promise<IsochroneFeatureCollection>;
2178
2920
  /** The most recently drawn rings, if any. */
2179
2921
  get current(): IsochroneFeatureCollection | undefined;
2180
- /** Draw (or update) a ring collection. Safe before the style has loaded. */
2922
+ /**
2923
+ * Draw (or update) a ring collection. Safe before the style has loaded.
2924
+ * Never silently drops an update: once the source exists the data is
2925
+ * applied immediately, even while `isStyleLoaded()` is transiently
2926
+ * `false` mid-render (PlacesLayer has the same rule) - otherwise the map
2927
+ * would keep showing the previous rings until the next style swap.
2928
+ * Calls made before the source has first been installed are stashed in
2929
+ * `lastData` - the latest wins - and installed on the next `style.load`.
2930
+ */
2181
2931
  private draw;
2182
2932
  /** Add-or-update the source and ring layers on the current style. */
2183
2933
  private install;
@@ -2342,13 +3092,31 @@ interface LaneIndication {
2342
3092
  valid?: boolean;
2343
3093
  /** Whether guidance recommends this lane. */
2344
3094
  active?: boolean;
3095
+ /**
3096
+ * Which one of {@link directions} guidance actually takes from this lane,
3097
+ * when the server resolves it (the gateway's `active_direction`, set only
3098
+ * on active lanes whose mask decodes to a single indication).
3099
+ *
3100
+ * A lane may permit several movements — "straight or right" is the common
3101
+ * one — and only this field says which of them the route uses. Draw the
3102
+ * lane arrow from it, falling back to the first permitted direction: see
3103
+ * {@link laneArrowDirection}.
3104
+ */
3105
+ activeDirection?: string;
2345
3106
  }
2346
- /** A typed fragment of banner content. */
3107
+ /**
3108
+ * A typed fragment of banner content.
3109
+ *
3110
+ * Field names are the wire names, because these objects come straight off
3111
+ * `route.raw` without a mapping pass (see {@link extractGuidance}).
3112
+ */
2347
3113
  interface BannerComponent {
2348
3114
  type: string;
2349
3115
  text: string;
2350
3116
  directions?: string[];
2351
3117
  active?: boolean;
3118
+ /** Which of `directions` guidance recommends (gateway `active_direction`). */
3119
+ active_direction?: string;
2352
3120
  }
2353
3121
  /** One banner line. */
2354
3122
  interface BannerContent {
@@ -2421,6 +3189,18 @@ declare function ssmlToText(ssml: string): string;
2421
3189
  * `valid` set.
2422
3190
  */
2423
3191
  declare function bannerLanes(banner: BannerInstruction): LaneIndication[];
3192
+ /**
3193
+ * The single direction a lane's arrow should show.
3194
+ *
3195
+ * A lane that permits several movements ("straight or right") carries them
3196
+ * all in `directions`, in the gateway's bitmask order, which is NOT the
3197
+ * order the route cares about. Drawing `directions[0]` therefore shows a
3198
+ * straight-ahead arrow on the lane the driver is being told to turn right
3199
+ * from. `activeDirection` is the one the route actually takes, so it wins;
3200
+ * the first permitted direction is only the fallback for lanes where the
3201
+ * server could not resolve one.
3202
+ */
3203
+ declare function laneArrowDirection(lane: LaneIndication): string;
2424
3204
  /**
2425
3205
  * Unicode arrow for a lane/maneuver direction (OSRM indication strings).
2426
3206
  * A text-only fallback; sprite-based lane icons ship with the map assets.
@@ -2558,9 +3338,10 @@ declare function cameraKindsShownOnMap(design: NavAlertsDesign, mode?: MapDispla
2558
3338
  * floor and per-mode visibility.
2559
3339
  *
2560
3340
  * The features must carry a `kind` property holding one of
2561
- * {@link NAV_CAMERA_KINDS} (or the gateway's `redlight` spelling); an
2562
- * unrecognised value falls back to the `unknown` icon, never to a guess at
2563
- * a specific camera type.
3341
+ * {@link NAV_CAMERA_KINDS} or any wire spelling {@link navCameraKind}
3342
+ * accepts (the gateway's `redlight`, OSM's `traffic_signals`, …); an
3343
+ * unrecognised or absent value falls back to the `unknown` icon, never to a
3344
+ * guess at a specific camera type.
2564
3345
  *
2565
3346
  * @param source GeoJSON source id holding the camera features.
2566
3347
  * @param variant Sprite variant to draw: `"light"` icons read on a dark
@@ -2783,4 +3564,4 @@ declare class VoiceGuidance {
2783
3564
  private playEarcon;
2784
3565
  }
2785
3566
 
2786
- export { ALERT_CONTRAST_MIN, AdrCheck, type AdrCheckOptions, type AdrCheckRequest, type AdrCheckResult, type AdrDimensions, type AdrTunnelCategory, type AlertChipContent, type AlertContrastIssue, type AlertPresentation, type BannerComponent, type BannerContent, type BannerInstruction, type BuildStyleOptions, type CameraAlert, CameraAlertChip, type CameraFix, DEFAULT_GLYPHS_URL, DEFAULT_TERRITORY_TILES_URL, type DiagnosticIssue, EFFECTS_METADATA_KEY, FLOW_DEFAULTS, FULL_ATTRIBUTION, FlowRouteEffectLayer, type FlythroughController, type FlythroughOptions, type FlythroughPose, GuidanceBanner, type GuidanceSeverity, type GuidanceSpokenPrompt, type IsochroneFeatureCollection, IsochroneLayer, type IsochroneLayerOptions, LOGO_SVG, type LaneIndication, type LayerOverride, type LngLatLike, LogoControl, type LogoOptions, type LogoPosition, MAX_PUCK_IMAGE_BYTES, type MapDiagnosticsInput, type MapDisplayMode, MapMapMap, type MapMapOptions, type MapMapTheme, NAV_ALERT_AUDIO_MODES, NAV_ALERT_CHIP_POSITIONS, NAV_ALERT_ICON_SETS, NAV_ALERT_LEAD_DISTANCES, NAV_ALERT_LEAD_DISTANCE_M, NAV_ALERT_MODES, NAV_ALERT_SOUNDS, NAV_ALERT_SOUND_BASE_URL, NAV_ALERT_SOUND_FILES, NAV_CAMERA_DEFAULTS, NAV_CAMERA_KINDS, type NavAlertAudioMode, type NavAlertChipPosition, type NavAlertIconSet, type NavAlertKindDesign, type NavAlertLeadDistance, type NavAlertMode, type NavAlertSound, type NavAlertsDesign, type NavBannerDesign, type NavCameraDesign, type NavCameraKind, type NavDesign, type NavPuckDesign, type NavRouteDesign, NavigationCamera, type NavigationCameraMode, type NavigationCameraOptions, type NearestByDriveTimeOptions, OPENMAPTILES_ATTRIBUTION, OSM_ATTRIBUTION, PALETTE_SLOTS, POI_CATEGORY_COLORS, POI_CATEGORY_IDS, POI_CLASS_CATEGORIES, type ParsedRoute, type Place, type PlacePointFeature, type PlaceWithDistance, type PlaceWithDriveTime, type PlacesFeatureCollection, type PlacesIcon, type PlacesInput, PlacesLayer, type PlacesLayerIds, type PlacesLayerOptions, type PlacesSelectOptions, type PoiCategoryDesign, type PoiDesign, PositionPuck, type PositionPuckOptions, type ProbeUploadOutcome, RIBBON_FLOATS_PER_VERTEX, ROUTE_EFFECTS, type ReachabilityMode, type RibbonMesh, type RouteEffectLayer, type RouteEffectName, type RouteFlowOptions, type RouteGeometry, RouteLayer, type RouteLayerOptions, type RouteOptions, type RouteProfile, SIGNAL_BLUE, SOURCE_LAYERS, type SetRouteEffectOptions, type SeverityProsody, type ShowReachabilityOptions, type SpeakOptions, type StepGuidance, type SunTimes, type Theme, type ThemeEffects, ThemeScheduler, type ThemeSchedulerOptions, type TruckParams, type UploadProbeOptions, VoiceGuidance, type VoiceGuidanceOptions, type VoiceGuidanceUpdate, type VoiceInstruction, alertChipContent, alertContrastIssues, alertIconSvg, alertIconUrl, alertPresentation, alertSoundUrl, alertSpeedReadoutColor, applyPoiDesign, bannerLanes, bearingBetween, bindFlythroughToScroll, buildAdrCheckBody, buildProbeUrl, buildRouteQuery, buildRouteUrl, buildStyle, builtInPoiColor, cameraKindLabel, cameraKindsShownOnMap, cameraShownOnMap, cameraSpriteName, cameraSymbolLayer, contrastRatio, createMap, createRouteEffect, defaultNavAlertsDesign, defaultNavDesign, defaultPoiDesign, directionArrow, effectsFromStyleMetadata, extractGuidance, flythrough, flythroughPose, formatCoord, formatCoords, haversineDistanceM, isNameTextField, languageTextField, lngLatToMercator, navAlertLeadDistanceM, navAlertSoundUrl, navCameraKind, navDesignFromTheme, navDesignFromThemeUrl, parseCssColour, parseHexColor, parseNavAlertsDesign, parseNavDesign, parseOsrmRoute, parsePoiDesign, placesFromGeoJSON, poiDesignFromTheme, poiDesignFromThemeUrl, poiDesignIsDefault, poiTextColorExpression, prefersReducedMotion, registerPmtilesProtocol, relativeLuminance, resetDiagnostics, resolveTheme, runMapDiagnostics, setMapLanguage, severityProsody, shortestArcDeg, shortestArcDelta, speak, ssmlToText, sunTimes, tessellateRouteRibbon, toLngLat, toPmtilesUrl, uploadProbeBatch };
3567
+ export { ALERT_CONTRAST_MIN, AdrCheck, type AdrCheckOptions, type AdrCheckRequest, type AdrCheckResult, type AdrDimensions, type AdrTunnelCategory, type AlertChipContent, type AlertContrastIssue, type AlertPresentation, type BannerComponent, type BannerContent, type BannerInstruction, type BuildStyleOptions, type CameraAlert, CameraAlertChip, type CameraFix, DEFAULT_GLYPHS_URL, DEFAULT_TERRITORY_TILES_URL, type DiagnosticIssue, EFFECTS_METADATA_KEY, type EndpointMarker, type EndpointsDesign, FLOW_DEFAULTS, FULL_ATTRIBUTION, FlowRouteEffectLayer, type FlythroughController, type FlythroughOptions, type FlythroughPose, GuidanceBanner, type GuidanceSeverity, type GuidanceSpokenPrompt, type IsochroneFeatureCollection, IsochroneLayer, type IsochroneLayerOptions, LOGO_SVG, type LaneIndication, type LayerOverride, type LngLatLike, LogoControl, type LogoOptions, type LogoPosition, MARKERS_ID, MARKER_DEFAULT_COLOUR, MARKER_GLYPH_IDS, MARKER_GLYPH_VIEWBOX, MARKER_LABEL_TEXT_SIZES, MARKER_PIN_BODY_PATH, MARKER_SIZES, MARKER_SIZES_PX, MAX_MARKER_ID_LENGTH, MAX_MARKER_IMAGE_BYTES, MAX_MARKER_ITEMS, MAX_MARKER_LABEL_LENGTH, MAX_MARKER_TEXT_LENGTH, MAX_PUCK_IMAGE_BYTES, type MapDiagnosticsInput, type MapDisplayMode, MapMapMap, type MapMapOptions, type MapMapTheme, type MarkerGlyphId, type MarkerItem, type MarkerSize, type MarkersBlock, MarkersLayer, type MarkersLayerIds, type MarkersLayerOptions, NAV_ALERT_AUDIO_MODES, NAV_ALERT_CHIP_POSITIONS, NAV_ALERT_ICON_SETS, NAV_ALERT_LEAD_DISTANCES, NAV_ALERT_LEAD_DISTANCE_M, NAV_ALERT_MODES, NAV_ALERT_SOUNDS, NAV_ALERT_SOUND_BASE_URL, NAV_ALERT_SOUND_FILES, NAV_CAMERA_DEFAULTS, NAV_CAMERA_KINDS, type NavAlertAudioMode, type NavAlertChipPosition, type NavAlertIconSet, type NavAlertKindDesign, type NavAlertLeadDistance, type NavAlertMode, type NavAlertSound, type NavAlertsDesign, type NavBannerDesign, type NavCameraDesign, type NavCameraKind, type NavDesign, type NavPuckDesign, type NavRouteDesign, NavigationCamera, type NavigationCameraMode, type NavigationCameraOptions, type NearestByDriveTimeOptions, OPENMAPTILES_ATTRIBUTION, OSM_ATTRIBUTION, PALETTE_SLOTS, POI_CATEGORY_COLORS, POI_CATEGORY_IDS, POI_CLASS_CATEGORIES, type ParsedRoute, type Place, type PlacePointFeature, type PlaceWithDistance, type PlaceWithDriveTime, type PlacesFeatureCollection, type PlacesIcon, type PlacesInput, type PlacesLabelOptions, PlacesLayer, type PlacesLayerIds, type PlacesLayerOptions, type PlacesSelectOptions, type PoiCategoryDesign, type PoiDesign, PositionPuck, type PositionPuckOptions, type ProbeUploadOutcome, RIBBON_FLOATS_PER_VERTEX, ROUTE_EFFECTS, type ReachabilityMode, type RenderMarkerImageOptions, type RenderedMarkerImage, type RibbonMesh, type RouteEffectLayer, type RouteEffectName, type RouteFlowOptions, type RouteGeometry, RouteLayer, type RouteLayerIds, type RouteLayerOptions, type RouteLineStyle, type RouteOptions, type RouteProfile, SIGNAL_BLUE, SOURCE_LAYERS, type SetRouteEffectOptions, type SeverityProsody, type ShowReachabilityOptions, type SpeakOptions, type StepGuidance, type SunTimes, type Theme, type ThemeEffects, ThemeScheduler, type ThemeSchedulerOptions, type TruckParams, type UploadProbeOptions, VoiceGuidance, type VoiceGuidanceOptions, type VoiceGuidanceUpdate, type VoiceInstruction, alertChipContent, alertContrastIssues, alertIconSvg, alertIconUrl, alertPresentation, alertSoundUrl, alertSpeedReadoutColor, applyPoiDesign, bannerLanes, bearingBetween, bindFlythroughToScroll, buildAdrCheckBody, buildProbeUrl, buildRouteQuery, buildRouteUrl, buildStyle, builtInPoiColor, cameraKindLabel, cameraKindsShownOnMap, cameraShownOnMap, cameraSpriteName, cameraSymbolLayer, contrastRatio, createMap, createRouteEffect, defaultNavAlertsDesign, defaultNavDesign, defaultPoiDesign, directionArrow, effectsFromStyleMetadata, extractGuidance, flythrough, flythroughPose, formatCoord, formatCoords, haversineDistanceM, isMarkerGlyphId, isNameTextField, laneArrowDirection, languageTextField, lngLatToMercator, markerGlyphPaths, markerImage, markerImageDataKey, markerImageKey, markerText, markerTextImageKey, markersFromTheme, markersFromThemeUrl, navAlertLeadDistanceM, navAlertSoundUrl, navCameraKind, navDesignFromTheme, navDesignFromThemeUrl, parseCssColour, parseHexColor, parseMarkers, parseNavAlertsDesign, parseNavDesign, parseOsrmRoute, parsePoiDesign, placesFromGeoJSON, poiDesignFromTheme, poiDesignFromThemeUrl, poiDesignIsDefault, poiTextColorExpression, prefersReducedMotion, registerPmtilesProtocol, relativeLuminance, renderMarkerImage, resetDiagnostics, resolveTheme, runMapDiagnostics, setMapLanguage, severityProsody, shortestArcDeg, shortestArcDelta, speak, ssmlToText, sunTimes, tessellateRouteRibbon, toLngLat, toPmtilesUrl, truncateChars, uploadProbeBatch };