@superrouteca/smap 1.0.5 → 1.0.6

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/CHANGELOG.md CHANGED
@@ -1,5 +1,10 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.6 — 2026-10-27
4
+
5
+ - Close up (from zoom 13, `bakeZoom`) each pin's number is drawn into the pin, so where pins overlap the pin on top covers the one below completely.
6
+ - Marker shape `vehicle`: the route map's live position, a white circle with a truck in each feature's own colour, in one layer for any number of drivers. Point labels can sit to the right (`labelPosition: 'right'`).
7
+
3
8
  ## 1.0.5 — 2026-10-27
4
9
 
5
10
  - Auto Zoom has a switch: the map option `autoZoom` and `map.setAutoZoom(on)`. In 3D, W / S tilt and A / D turn the view (`keyboard3D`).
package/README.md CHANGED
@@ -10,7 +10,7 @@ areas drawn on the GPU (hundreds of thousands of points, clustered or not), 3D b
10
10
  renew themselves. Also the JS client (no map view) for servers and workers: tokens, the nearest node, styles, tiles
11
11
  and raw reads.
12
12
 
13
- - Version 1.0.5, MIT licence (see `LICENSE` at the repository root)
13
+ - Version 1.0.6, MIT licence (see `LICENSE` at the repository root)
14
14
  - Works with bundlers (Vite, webpack, Next.js and others): ES module and CommonJS entry points with types
15
15
  - Nothing else to install: the map renderer is bundled in the package
16
16
  - Importing is safe on a server; a map is created in the browser only
@@ -159,7 +159,7 @@ with `default` for features without it) or an engine expression (an array such a
159
159
 
160
160
  | Option | Meaning |
161
161
  |---|---|
162
- | `shape` | `'pin'` (default; the tip is at the position) or `'circle'` |
162
+ | `shape` | `'pin'` (default; the tip is at the position), `'circle'`, or `'vehicle'` (a white circle with a truck in the `border` colour, the route map's live position) |
163
163
  | `fill` | inside colour (default white) |
164
164
  | `border` | border colour (default the layer's `color`) |
165
165
  | `borderWidth` | pixels (default 3) |
@@ -169,6 +169,7 @@ with `default` for features without it) or an engine expression (an array such a
169
169
  | `badge` | a red count at the upper right, shown when more than 1 |
170
170
  | `opacity` | of the pin, its number and its badge together (e.g. settled stops paler) |
171
171
  | `overlap` | Auto Zoom for this layer (map option `autoZoom`, `map.setAutoZoom(on)`, on by default): pins piled up under a click: 4 or more within 12 px zoom the map in by 2; close up (zoom 17) or at one spot a chooser lists them, `label(properties)` names each row; `false` turns it off |
172
+ | `bakeZoom` | from this zoom on (default 13) each pin's number is drawn into the pin, so an upper pin covers a lower one completely; `false` keeps numbers a layer of their own |
172
173
  | `hoverRaise` | the pin under the pointer comes to the front, so overlapping pins can be read and clicked (default `true`) |
173
174
  | `scale` | size factor of the pin, its number and its badge together; a number or an expression of the zoom, e.g. `['interpolate', ['linear'], ['zoom'], 9, 0.5, 14, 1]` |
174
175
  | `textMinzoom`, `badgeMinzoom` | show numbers / badges only from this zoom on, so an overview of many stops stays readable |
package/dist/SOURCE.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
- "sdkVersion": "1.0.5",
2
+ "sdkVersion": "1.0.6",
3
3
  "engine": "maplibre-gl 6.16.0",
4
4
  "files": {
5
- "smap.js": "3b8d6eaa6a2ddd6fb14b1465a9c6ef46a09d550093bb567b613fda3b26fa1fe5",
5
+ "smap.js": "ebb6560db4ed5564693ebbf19b030bba0d5bbcc3fee2f7e2b5bf959584ffba18",
6
6
  "smap.css": "5e3bdcb00b8fc8805f545b76cdeea5e783c89984f48ec2fee66c4319af4ada0b",
7
- "smap-client.js": "1ae5f49b4be515efa69570a110b862ac8f325a6b45a9438a5c8011fa9278f598",
7
+ "smap-client.js": "87d4eaad1204383570d6d3655b52a45f19e6698b8fdbaa42c82a03471cba2e3d",
8
8
  "smap-client.d.ts": "ad701612630a290b2dc0e49250ae25cea965ecb3a145c016bb462cd30f108cf7",
9
- "smap.mjs": "7ea12152fa21a7d3e2c235646998cd8e36fded1db808945289123dc19c11424e",
10
- "smap.cjs": "668b85f95674a22b7072566ec91f499de3623cddb10afb28aa32c528a39313da",
11
- "smap-client.cjs": "6c54aa4a7676c26e7c4569203afb19f6c20233dc96f3e1400087b035f4e7d4f0",
12
- "smap-client.mjs": "207bcb78118603359d73aecbea3f422d101b729f5e8462c1bb05225ad886d1f5",
9
+ "smap.mjs": "6639dc281f3af69b45164c842aa83d2096e002a0c55f41ab12aa4ad9878a34e0",
10
+ "smap.cjs": "37865209689abe5a06ee251dc21dcf977c16ddadcb02dddab37cb0b470d7822d",
11
+ "smap-client.cjs": "ab2b816c894027712082f23b944349cba6d7d33f1f262d1d2c495cddf07008bd",
12
+ "smap-client.mjs": "91587c5d58c1341ffdcd133c74aa8d393b23e5fe0d20fe5beac25e5f5bb6647b",
13
13
  "smap-client.d.cts": "80b96f5a907caf6295ca073816dc526e8d285f22670c809adaab5242a018e686",
14
14
  "smap-client.d.mts": "a66834cab868a6a0eaf943c1b185a236916cb00ecf32a871f972ab3213eddeba",
15
15
  "vendor-licenses.txt": "ee5fc05a0677eaf69601d2c7db0d9ecd6cc27c3abc1d0733bc9ed34707cf8ef2"
@@ -1,5 +1,5 @@
1
1
  /* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
2
- /*! sMap JS client 1.0.5 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
2
+ /*! sMap JS client 1.0.6 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
3
3
  /*
4
4
  * Runs wherever `fetch` exists: browsers, Node.js 18+, workers; elsewhere (mini programs, older runtimes) pass your
5
5
  * own `fetch`. UMD: `window.SMapClient`, `require('smap-client')`, or an AMD module.
@@ -19,7 +19,7 @@
19
19
  }(typeof self !== 'undefined' ? self : this, function () {
20
20
  'use strict';
21
21
 
22
- var VERSION = '1.0.5';
22
+ var VERSION = '1.0.6';
23
23
  var NODE_TTL = 3600 * 1000; // the measured order
24
24
  var KNOWN_TTL = 30 * 86400 * 1000; // the last node list received, used when none can be read
25
25
 
@@ -1,4 +1,4 @@
1
- /*! sMap JS client 1.0.5 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
1
+ /*! sMap JS client 1.0.6 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
2
2
  /*
3
3
  * Runs wherever `fetch` exists: browsers, Node.js 18+, workers; elsewhere (mini programs, older runtimes) pass your
4
4
  * own `fetch`. UMD: `window.SMapClient`, `require('smap-client')`, or an AMD module.
@@ -18,7 +18,7 @@
18
18
  }(typeof self !== 'undefined' ? self : this, function () {
19
19
  'use strict';
20
20
 
21
- var VERSION = '1.0.5';
21
+ var VERSION = '1.0.6';
22
22
  var NODE_TTL = 3600 * 1000; // the measured order
23
23
  var KNOWN_TTL = 30 * 86400 * 1000; // the last node list received, used when none can be read
24
24
 
@@ -1,7 +1,7 @@
1
1
  /* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
2
2
  var smapClientModule = { exports: {} };
3
3
  (function (module, exports, define) {
4
- /*! sMap JS client 1.0.5 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
4
+ /*! sMap JS client 1.0.6 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
5
5
  /*
6
6
  * Runs wherever `fetch` exists: browsers, Node.js 18+, workers; elsewhere (mini programs, older runtimes) pass your
7
7
  * own `fetch`. UMD: `window.SMapClient`, `require('smap-client')`, or an AMD module.
@@ -21,7 +21,7 @@ var smapClientModule = { exports: {} };
21
21
  }(typeof self !== 'undefined' ? self : this, function () {
22
22
  'use strict';
23
23
 
24
- var VERSION = '1.0.5';
24
+ var VERSION = '1.0.6';
25
25
  var NODE_TTL = 3600 * 1000; // the measured order
26
26
  var KNOWN_TTL = 30 * 86400 * 1000; // the last node list received, used when none can be read
27
27
 
package/dist/smap.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  /* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
2
2
  'use strict';
3
- /*! sMap Web SDK 1.0.5. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
3
+ /*! sMap Web SDK 1.0.6. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
4
4
 
5
5
  var smapCore = null;
6
6
  function smapCreateEngine() {
@@ -26873,7 +26873,7 @@ function smapCreateSdk(engine) {
26873
26873
  })(typeof self !== 'undefined' ? self : this, function (engine) {
26874
26874
  'use strict';
26875
26875
 
26876
- var VERSION = '1.0.5';
26876
+ var VERSION = '1.0.6';
26877
26877
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
26878
26878
  var BASEMAP_SOURCE = 'basemap';
26879
26879
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -27028,7 +27028,7 @@ function smapCreateSdk(engine) {
27028
27028
  // from the position (right of the round part, as the page's badge sits on the turned pin's corner).
27029
27029
  function markerGeometry(shape, size) {
27030
27030
  var k = size / 32;
27031
- if (shape === 'circle') {
27031
+ if (shape === 'circle' || shape === 'vehicle') {
27032
27032
  return { k: k, tipToHead: 0, badge: [size * 0.36, -size * 0.36] };
27033
27033
  }
27034
27034
  var half = size * SQRT1_2; // the centre of the round part, above the tip
@@ -27054,16 +27054,18 @@ function smapCreateSdk(engine) {
27054
27054
 
27055
27055
  // Each draw function gets a 2D context already scaled to the pixel ratio and returns nothing; sizes are in pixels.
27056
27056
  var IMAGE_KINDS = {
27057
- // pin|shape|fill|border|borderWidth|size
27057
+ // pin|shape|fill|border|borderWidth|size (shape pin, circle, or vehicle: the route map's live position, a white
27058
+ // circle with a truck in the border colour)
27058
27059
  pin: function (args) {
27059
27060
  var shape = args[0], fill = args[1], border = args[2], bw = Number(args[3]), size = Number(args[4]);
27060
27061
  var g = markerGeometry(shape, size);
27061
- var reach = shape === 'circle' ? size / 2 : g.tipToHead; // farthest the shape goes from its centre
27062
+ var reach = shape === 'pin' ? g.tipToHead : size / 2; // farthest the shape goes from its centre
27062
27063
  var w = Math.ceil(2 * reach + 2 * PIN_PAD), h = w;
27063
27064
  return {
27064
27065
  width: w, height: h,
27065
27066
  draw: function (ctx, ratio) {
27066
27067
  var c = w / 2;
27068
+ if (shape === 'vehicle') { drawVehicle(ctx, ratio, c, size, border); return; }
27067
27069
  ctx.save();
27068
27070
  ctx.translate(c, c);
27069
27071
  var s = size / 2;
@@ -27094,6 +27096,31 @@ function smapCreateSdk(engine) {
27094
27096
  },
27095
27097
  };
27096
27098
  },
27099
+ // pinnum|shape|fill|border|borderWidth|size|textColor|text: a pin with its number drawn in, so a pin above covers a
27100
+ // pin below completely (used close up, where few pins are in view; further out the number is its own layer).
27101
+ pinnum: function (args, measure) {
27102
+ var pin = IMAGE_KINDS.pin(args.slice(0, 5));
27103
+ var size = Number(args[4]), color = args[5], text = args.slice(6).join('|');
27104
+ var px = 14 * size / 32, font = 'bold ' + px + 'px monospace';
27105
+ return {
27106
+ width: pin.width, height: pin.height,
27107
+ draw: function (ctx, ratio) {
27108
+ pin.draw(ctx, ratio);
27109
+ if (text === '') { return; }
27110
+ ctx.save();
27111
+ ctx.font = font;
27112
+ ctx.textAlign = 'center';
27113
+ ctx.textBaseline = 'middle';
27114
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
27115
+ ctx.shadowOffsetX = ratio;
27116
+ ctx.shadowOffsetY = ratio;
27117
+ ctx.shadowBlur = 2 * ratio;
27118
+ ctx.fillStyle = color;
27119
+ ctx.fillText(text, pin.width / 2, pin.height / 2 + 0.5);
27120
+ ctx.restore();
27121
+ },
27122
+ };
27123
+ },
27097
27124
  // num|color|size|text (bold, with the route map's text shadow)
27098
27125
  num: function (args, measure) {
27099
27126
  var color = args[0], size = Number(args[1]), text = args.slice(2).join('|');
@@ -27373,6 +27400,27 @@ function smapCreateSdk(engine) {
27373
27400
  };
27374
27401
 
27375
27402
  // Draws an image the SDK named (IMAGE_PREFIX + kind|args) and adds it to the map; other names are left alone.
27403
+ // The live-position marker of the route map: a white circle (85%) with a truck in the driver colour.
27404
+ function drawVehicle(ctx, ratio, c, size, color) {
27405
+ var r = size / 2, k = size / 24;
27406
+ ctx.save();
27407
+ ctx.translate(c, c);
27408
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
27409
+ ctx.shadowBlur = 2 * ratio;
27410
+ ctx.beginPath(); ctx.arc(0, 0, r, 0, Math.PI * 2);
27411
+ ctx.fillStyle = 'rgba(255,255,255,0.85)'; ctx.fill();
27412
+ ctx.shadowColor = 'transparent';
27413
+ ctx.scale(k, k);
27414
+ ctx.fillStyle = color;
27415
+ // cargo box, cab and two wheels, about 14 x 10 px on a 24 px marker
27416
+ ctx.fillRect(-7, -5, 8.5, 7);
27417
+ roundedPath(ctx, 2.2, -2.6, 4.8, 4.6, [[1.4, 1.4], [0.6, 0.6], [0, 0], [0, 0]]); ctx.fill();
27418
+ ctx.beginPath(); ctx.arc(-4.2, 3.4, 1.9, 0, Math.PI * 2); ctx.arc(4.1, 3.4, 1.9, 0, Math.PI * 2); ctx.fill();
27419
+ ctx.fillStyle = 'rgba(255,255,255,0.85)';
27420
+ ctx.beginPath(); ctx.arc(-4.2, 3.4, 0.7, 0, Math.PI * 2); ctx.arc(4.1, 3.4, 0.7, 0, Math.PI * 2); ctx.fill();
27421
+ ctx.restore();
27422
+ }
27423
+
27376
27424
  SMap.prototype._drawImage = function (id) {
27377
27425
  if (typeof id !== 'string' || id.indexOf(IMAGE_PREFIX) !== 0) { return; }
27378
27426
  var m = this.engine;
@@ -27553,7 +27601,7 @@ function smapCreateSdk(engine) {
27553
27601
  if (hit.o.marker && popupOptions.offset === undefined && this._popupDefaults.offset === undefined) {
27554
27602
  // Just above the pin as it is drawn now (its scale may follow the zoom).
27555
27603
  var mk = hit.o.marker, size = mk.size || 32, k = scaleAt(mk.scale, m.getZoom ? m.getZoom() : 0);
27556
- popupOptions.offset = mk.shape === 'circle' ? Math.round((size / 2) * k + 4) : Math.round((markerGeometry('pin', size).tipToHead + size / 2) * k + 2);
27604
+ popupOptions.offset = mk.shape === 'circle' || mk.shape === 'vehicle' ? Math.round((size / 2) * k + 4) : Math.round((markerGeometry('pin', size).tipToHead + size / 2) * k + 2);
27557
27605
  }
27558
27606
  // Above the pin, always (as on Superroute's route map); the map pans to make room instead of flipping it below.
27559
27607
  if (hit.o.marker && popupOptions.anchor === undefined && this._popupDefaults.anchor === undefined) { popupOptions.anchor = 'bottom'; }
@@ -27820,6 +27868,7 @@ function smapCreateSdk(engine) {
27820
27868
  // The engine refuses a filter key whose value is undefined (unclustered points have none).
27821
27869
  if (layer.filter === undefined) { delete layer.filter; }
27822
27870
  if (layer.minzoom === undefined) { delete layer.minzoom; }
27871
+ if (layer.maxzoom === undefined) { delete layer.maxzoom; }
27823
27872
  if (layer.paint === undefined) { delete layer.paint; }
27824
27873
  // A layer's own minzoom (a marker's textMinzoom / badgeMinzoom) never shows it before the layer's own range.
27825
27874
  for (var k in range) { layer[k] = k === 'minzoom' && layer.minzoom !== undefined ? Math.max(layer.minzoom, range.minzoom) : range[k]; }
@@ -27857,7 +27906,7 @@ function smapCreateSdk(engine) {
27857
27906
  // Pins like Superroute's route map, drawn on the GPU: one image per colour pair (and per number, per badge),
27858
27907
  // made the first time a feature shows it. Numbers and badges are their own layers above the pins.
27859
27908
  var mk = o.marker;
27860
- var shape = mk.shape === 'circle' ? 'circle' : 'pin';
27909
+ var shape = mk.shape === 'circle' || mk.shape === 'vehicle' ? mk.shape : 'pin';
27861
27910
  var size = mk.size || 32;
27862
27911
  var bw = mk.borderWidth !== undefined ? mk.borderWidth : 3;
27863
27912
  var g = markerGeometry(shape, size);
@@ -27868,22 +27917,29 @@ function smapCreateSdk(engine) {
27868
27917
  // Opacity of the pin, its number and its badge together (the route map draws settled stops at .72).
27869
27918
  var markerPaint = mk.opacity !== undefined ? { 'icon-opacity': valueExpr(mk.opacity, 1) } : undefined;
27870
27919
  main = src + '-points';
27920
+ // From bakeZoom on (default 13) a pin's number is drawn into its own image, so an upper pin covers a lower
27921
+ // one completely. Further out, where thousands of pins can be in view, the numbers stay one layer above the
27922
+ // pins (an image per colour pair instead of per pin and number). bakeZoom: false keeps them separate.
27923
+ var plainPin = drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]);
27924
+ var bakeZoom = mk.text !== undefined && mk.bakeZoom !== false ? (mk.bakeZoom !== undefined ? mk.bakeZoom : 13) : null;
27925
+ var pinImage = bakeZoom === null ? plainPin : ['step', ['zoom'], plainPin, bakeZoom,
27926
+ drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'), str(mk.text, '')])];
27871
27927
  var markerSpecs = [];
27872
27928
  var addMarker = function (layer) { markerSpecs.push(JSON.parse(JSON.stringify(layer))); add(layer); };
27873
27929
  addMarker({
27874
27930
  id: main, type: 'symbol', filter: only, paint: markerPaint,
27875
27931
  layout: {
27876
- 'icon-image': drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]),
27877
- 'icon-anchor': shape === 'circle' ? 'center' : 'bottom',
27932
+ 'icon-image': pinImage,
27933
+ 'icon-anchor': shape === 'pin' ? 'bottom' : 'center',
27878
27934
  // The image has room for the shadow below the tip: the tip, not the image's edge, is at the position.
27879
- 'icon-offset': [0, shape === 'circle' ? 0 : PIN_PAD], 'icon-size': scale,
27935
+ 'icon-offset': [0, shape === 'pin' ? PIN_PAD : 0], 'icon-size': scale,
27880
27936
  'icon-allow-overlap': true, 'icon-ignore-placement': true,
27881
27937
  },
27882
27938
  });
27883
27939
  if (mk.text !== undefined) {
27884
27940
  var text = str(mk.text, '');
27885
27941
  addMarker({
27886
- id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, paint: markerPaint,
27942
+ id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
27887
27943
  layout: {
27888
27944
  'icon-image': drawnImageId('num', [str(mk.textColor, '#ffffff'), String(size), text]),
27889
27945
  'icon-offset': [0, -g.tipToHead], 'icon-size': scale, 'icon-allow-overlap': true, 'icon-ignore-placement': true,
@@ -27930,7 +27986,9 @@ function smapCreateSdk(engine) {
27930
27986
  if (o.label) {
27931
27987
  add({
27932
27988
  id: src + '-labels', type: 'symbol', filter: only,
27933
- layout: { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.2], 'text-anchor': 'top', 'text-optional': true },
27989
+ layout: o.labelPosition === 'right'
27990
+ ? { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 11, 'text-offset': [1.3, 0], 'text-anchor': 'left', 'text-optional': true, 'text-max-width': 12 }
27991
+ : { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.2], 'text-anchor': 'top', 'text-optional': true },
27934
27992
  paint: { 'text-color': '#1f2937', 'text-halo-color': '#ffffff', 'text-halo-width': 1.4 },
27935
27993
  });
27936
27994
  }
@@ -28012,6 +28070,7 @@ function smapCreateSdk(engine) {
28012
28070
  if (layer.filter === undefined) { delete layer.filter; }
28013
28071
  if (layer.paint === undefined) { delete layer.paint; }
28014
28072
  if (layer.minzoom === undefined) { delete layer.minzoom; }
28073
+ if (layer.maxzoom === undefined) { delete layer.maxzoom; }
28015
28074
  m.addLayer(layer);
28016
28075
  def.engineLayers.push(layer.id);
28017
28076
  if (!top) { top = layer.id; }
@@ -28071,7 +28130,7 @@ function SMapMap(options) {
28071
28130
  SMapMap.prototype = core.Map.prototype;
28072
28131
  return new core.Map(options);
28073
28132
  }
28074
- var smapVersion = "1.0.5";
28133
+ var smapVersion = "1.0.6";
28075
28134
  var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
28076
28135
  /** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
28077
28136
  function smapGetEngine() { return smapLoad().engine; }
package/dist/smap.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! sMap Web SDK 1.0.5. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
1
+ /*! sMap Web SDK 1.0.6. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
2
2
  (function () {
3
3
  function smapCreateEngine() {
4
4
  var SMAP_BASE_URL = (typeof document !== 'undefined' && document.baseURI) || (typeof location !== 'undefined' && location.href) || 'about:blank';
@@ -26870,7 +26870,7 @@ function smapCreateSdk(engine) {
26870
26870
  })(typeof self !== 'undefined' ? self : this, function (engine) {
26871
26871
  'use strict';
26872
26872
 
26873
- var VERSION = '1.0.5';
26873
+ var VERSION = '1.0.6';
26874
26874
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
26875
26875
  var BASEMAP_SOURCE = 'basemap';
26876
26876
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -27025,7 +27025,7 @@ function smapCreateSdk(engine) {
27025
27025
  // from the position (right of the round part, as the page's badge sits on the turned pin's corner).
27026
27026
  function markerGeometry(shape, size) {
27027
27027
  var k = size / 32;
27028
- if (shape === 'circle') {
27028
+ if (shape === 'circle' || shape === 'vehicle') {
27029
27029
  return { k: k, tipToHead: 0, badge: [size * 0.36, -size * 0.36] };
27030
27030
  }
27031
27031
  var half = size * SQRT1_2; // the centre of the round part, above the tip
@@ -27051,16 +27051,18 @@ function smapCreateSdk(engine) {
27051
27051
 
27052
27052
  // Each draw function gets a 2D context already scaled to the pixel ratio and returns nothing; sizes are in pixels.
27053
27053
  var IMAGE_KINDS = {
27054
- // pin|shape|fill|border|borderWidth|size
27054
+ // pin|shape|fill|border|borderWidth|size (shape pin, circle, or vehicle: the route map's live position, a white
27055
+ // circle with a truck in the border colour)
27055
27056
  pin: function (args) {
27056
27057
  var shape = args[0], fill = args[1], border = args[2], bw = Number(args[3]), size = Number(args[4]);
27057
27058
  var g = markerGeometry(shape, size);
27058
- var reach = shape === 'circle' ? size / 2 : g.tipToHead; // farthest the shape goes from its centre
27059
+ var reach = shape === 'pin' ? g.tipToHead : size / 2; // farthest the shape goes from its centre
27059
27060
  var w = Math.ceil(2 * reach + 2 * PIN_PAD), h = w;
27060
27061
  return {
27061
27062
  width: w, height: h,
27062
27063
  draw: function (ctx, ratio) {
27063
27064
  var c = w / 2;
27065
+ if (shape === 'vehicle') { drawVehicle(ctx, ratio, c, size, border); return; }
27064
27066
  ctx.save();
27065
27067
  ctx.translate(c, c);
27066
27068
  var s = size / 2;
@@ -27091,6 +27093,31 @@ function smapCreateSdk(engine) {
27091
27093
  },
27092
27094
  };
27093
27095
  },
27096
+ // pinnum|shape|fill|border|borderWidth|size|textColor|text: a pin with its number drawn in, so a pin above covers a
27097
+ // pin below completely (used close up, where few pins are in view; further out the number is its own layer).
27098
+ pinnum: function (args, measure) {
27099
+ var pin = IMAGE_KINDS.pin(args.slice(0, 5));
27100
+ var size = Number(args[4]), color = args[5], text = args.slice(6).join('|');
27101
+ var px = 14 * size / 32, font = 'bold ' + px + 'px monospace';
27102
+ return {
27103
+ width: pin.width, height: pin.height,
27104
+ draw: function (ctx, ratio) {
27105
+ pin.draw(ctx, ratio);
27106
+ if (text === '') { return; }
27107
+ ctx.save();
27108
+ ctx.font = font;
27109
+ ctx.textAlign = 'center';
27110
+ ctx.textBaseline = 'middle';
27111
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
27112
+ ctx.shadowOffsetX = ratio;
27113
+ ctx.shadowOffsetY = ratio;
27114
+ ctx.shadowBlur = 2 * ratio;
27115
+ ctx.fillStyle = color;
27116
+ ctx.fillText(text, pin.width / 2, pin.height / 2 + 0.5);
27117
+ ctx.restore();
27118
+ },
27119
+ };
27120
+ },
27094
27121
  // num|color|size|text (bold, with the route map's text shadow)
27095
27122
  num: function (args, measure) {
27096
27123
  var color = args[0], size = Number(args[1]), text = args.slice(2).join('|');
@@ -27370,6 +27397,27 @@ function smapCreateSdk(engine) {
27370
27397
  };
27371
27398
 
27372
27399
  // Draws an image the SDK named (IMAGE_PREFIX + kind|args) and adds it to the map; other names are left alone.
27400
+ // The live-position marker of the route map: a white circle (85%) with a truck in the driver colour.
27401
+ function drawVehicle(ctx, ratio, c, size, color) {
27402
+ var r = size / 2, k = size / 24;
27403
+ ctx.save();
27404
+ ctx.translate(c, c);
27405
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
27406
+ ctx.shadowBlur = 2 * ratio;
27407
+ ctx.beginPath(); ctx.arc(0, 0, r, 0, Math.PI * 2);
27408
+ ctx.fillStyle = 'rgba(255,255,255,0.85)'; ctx.fill();
27409
+ ctx.shadowColor = 'transparent';
27410
+ ctx.scale(k, k);
27411
+ ctx.fillStyle = color;
27412
+ // cargo box, cab and two wheels, about 14 x 10 px on a 24 px marker
27413
+ ctx.fillRect(-7, -5, 8.5, 7);
27414
+ roundedPath(ctx, 2.2, -2.6, 4.8, 4.6, [[1.4, 1.4], [0.6, 0.6], [0, 0], [0, 0]]); ctx.fill();
27415
+ ctx.beginPath(); ctx.arc(-4.2, 3.4, 1.9, 0, Math.PI * 2); ctx.arc(4.1, 3.4, 1.9, 0, Math.PI * 2); ctx.fill();
27416
+ ctx.fillStyle = 'rgba(255,255,255,0.85)';
27417
+ ctx.beginPath(); ctx.arc(-4.2, 3.4, 0.7, 0, Math.PI * 2); ctx.arc(4.1, 3.4, 0.7, 0, Math.PI * 2); ctx.fill();
27418
+ ctx.restore();
27419
+ }
27420
+
27373
27421
  SMap.prototype._drawImage = function (id) {
27374
27422
  if (typeof id !== 'string' || id.indexOf(IMAGE_PREFIX) !== 0) { return; }
27375
27423
  var m = this.engine;
@@ -27550,7 +27598,7 @@ function smapCreateSdk(engine) {
27550
27598
  if (hit.o.marker && popupOptions.offset === undefined && this._popupDefaults.offset === undefined) {
27551
27599
  // Just above the pin as it is drawn now (its scale may follow the zoom).
27552
27600
  var mk = hit.o.marker, size = mk.size || 32, k = scaleAt(mk.scale, m.getZoom ? m.getZoom() : 0);
27553
- popupOptions.offset = mk.shape === 'circle' ? Math.round((size / 2) * k + 4) : Math.round((markerGeometry('pin', size).tipToHead + size / 2) * k + 2);
27601
+ popupOptions.offset = mk.shape === 'circle' || mk.shape === 'vehicle' ? Math.round((size / 2) * k + 4) : Math.round((markerGeometry('pin', size).tipToHead + size / 2) * k + 2);
27554
27602
  }
27555
27603
  // Above the pin, always (as on Superroute's route map); the map pans to make room instead of flipping it below.
27556
27604
  if (hit.o.marker && popupOptions.anchor === undefined && this._popupDefaults.anchor === undefined) { popupOptions.anchor = 'bottom'; }
@@ -27817,6 +27865,7 @@ function smapCreateSdk(engine) {
27817
27865
  // The engine refuses a filter key whose value is undefined (unclustered points have none).
27818
27866
  if (layer.filter === undefined) { delete layer.filter; }
27819
27867
  if (layer.minzoom === undefined) { delete layer.minzoom; }
27868
+ if (layer.maxzoom === undefined) { delete layer.maxzoom; }
27820
27869
  if (layer.paint === undefined) { delete layer.paint; }
27821
27870
  // A layer's own minzoom (a marker's textMinzoom / badgeMinzoom) never shows it before the layer's own range.
27822
27871
  for (var k in range) { layer[k] = k === 'minzoom' && layer.minzoom !== undefined ? Math.max(layer.minzoom, range.minzoom) : range[k]; }
@@ -27854,7 +27903,7 @@ function smapCreateSdk(engine) {
27854
27903
  // Pins like Superroute's route map, drawn on the GPU: one image per colour pair (and per number, per badge),
27855
27904
  // made the first time a feature shows it. Numbers and badges are their own layers above the pins.
27856
27905
  var mk = o.marker;
27857
- var shape = mk.shape === 'circle' ? 'circle' : 'pin';
27906
+ var shape = mk.shape === 'circle' || mk.shape === 'vehicle' ? mk.shape : 'pin';
27858
27907
  var size = mk.size || 32;
27859
27908
  var bw = mk.borderWidth !== undefined ? mk.borderWidth : 3;
27860
27909
  var g = markerGeometry(shape, size);
@@ -27865,22 +27914,29 @@ function smapCreateSdk(engine) {
27865
27914
  // Opacity of the pin, its number and its badge together (the route map draws settled stops at .72).
27866
27915
  var markerPaint = mk.opacity !== undefined ? { 'icon-opacity': valueExpr(mk.opacity, 1) } : undefined;
27867
27916
  main = src + '-points';
27917
+ // From bakeZoom on (default 13) a pin's number is drawn into its own image, so an upper pin covers a lower
27918
+ // one completely. Further out, where thousands of pins can be in view, the numbers stay one layer above the
27919
+ // pins (an image per colour pair instead of per pin and number). bakeZoom: false keeps them separate.
27920
+ var plainPin = drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]);
27921
+ var bakeZoom = mk.text !== undefined && mk.bakeZoom !== false ? (mk.bakeZoom !== undefined ? mk.bakeZoom : 13) : null;
27922
+ var pinImage = bakeZoom === null ? plainPin : ['step', ['zoom'], plainPin, bakeZoom,
27923
+ drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'), str(mk.text, '')])];
27868
27924
  var markerSpecs = [];
27869
27925
  var addMarker = function (layer) { markerSpecs.push(JSON.parse(JSON.stringify(layer))); add(layer); };
27870
27926
  addMarker({
27871
27927
  id: main, type: 'symbol', filter: only, paint: markerPaint,
27872
27928
  layout: {
27873
- 'icon-image': drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]),
27874
- 'icon-anchor': shape === 'circle' ? 'center' : 'bottom',
27929
+ 'icon-image': pinImage,
27930
+ 'icon-anchor': shape === 'pin' ? 'bottom' : 'center',
27875
27931
  // The image has room for the shadow below the tip: the tip, not the image's edge, is at the position.
27876
- 'icon-offset': [0, shape === 'circle' ? 0 : PIN_PAD], 'icon-size': scale,
27932
+ 'icon-offset': [0, shape === 'pin' ? PIN_PAD : 0], 'icon-size': scale,
27877
27933
  'icon-allow-overlap': true, 'icon-ignore-placement': true,
27878
27934
  },
27879
27935
  });
27880
27936
  if (mk.text !== undefined) {
27881
27937
  var text = str(mk.text, '');
27882
27938
  addMarker({
27883
- id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, paint: markerPaint,
27939
+ id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
27884
27940
  layout: {
27885
27941
  'icon-image': drawnImageId('num', [str(mk.textColor, '#ffffff'), String(size), text]),
27886
27942
  'icon-offset': [0, -g.tipToHead], 'icon-size': scale, 'icon-allow-overlap': true, 'icon-ignore-placement': true,
@@ -27927,7 +27983,9 @@ function smapCreateSdk(engine) {
27927
27983
  if (o.label) {
27928
27984
  add({
27929
27985
  id: src + '-labels', type: 'symbol', filter: only,
27930
- layout: { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.2], 'text-anchor': 'top', 'text-optional': true },
27986
+ layout: o.labelPosition === 'right'
27987
+ ? { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 11, 'text-offset': [1.3, 0], 'text-anchor': 'left', 'text-optional': true, 'text-max-width': 12 }
27988
+ : { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.2], 'text-anchor': 'top', 'text-optional': true },
27931
27989
  paint: { 'text-color': '#1f2937', 'text-halo-color': '#ffffff', 'text-halo-width': 1.4 },
27932
27990
  });
27933
27991
  }
@@ -28009,6 +28067,7 @@ function smapCreateSdk(engine) {
28009
28067
  if (layer.filter === undefined) { delete layer.filter; }
28010
28068
  if (layer.paint === undefined) { delete layer.paint; }
28011
28069
  if (layer.minzoom === undefined) { delete layer.minzoom; }
28070
+ if (layer.maxzoom === undefined) { delete layer.maxzoom; }
28012
28071
  m.addLayer(layer);
28013
28072
  def.engineLayers.push(layer.id);
28014
28073
  if (!top) { top = layer.id; }
package/dist/smap.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  /* Generated by js/build.mjs from js/src (see SOURCE.json). Do not edit. */
2
- /*! sMap Web SDK 1.0.5. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
2
+ /*! sMap Web SDK 1.0.6. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
3
3
 
4
4
  var smapCore = null;
5
5
  function smapCreateEngine() {
@@ -26872,7 +26872,7 @@ function smapCreateSdk(engine) {
26872
26872
  })(typeof self !== 'undefined' ? self : this, function (engine) {
26873
26873
  'use strict';
26874
26874
 
26875
- var VERSION = '1.0.5';
26875
+ var VERSION = '1.0.6';
26876
26876
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
26877
26877
  var BASEMAP_SOURCE = 'basemap';
26878
26878
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -27027,7 +27027,7 @@ function smapCreateSdk(engine) {
27027
27027
  // from the position (right of the round part, as the page's badge sits on the turned pin's corner).
27028
27028
  function markerGeometry(shape, size) {
27029
27029
  var k = size / 32;
27030
- if (shape === 'circle') {
27030
+ if (shape === 'circle' || shape === 'vehicle') {
27031
27031
  return { k: k, tipToHead: 0, badge: [size * 0.36, -size * 0.36] };
27032
27032
  }
27033
27033
  var half = size * SQRT1_2; // the centre of the round part, above the tip
@@ -27053,16 +27053,18 @@ function smapCreateSdk(engine) {
27053
27053
 
27054
27054
  // Each draw function gets a 2D context already scaled to the pixel ratio and returns nothing; sizes are in pixels.
27055
27055
  var IMAGE_KINDS = {
27056
- // pin|shape|fill|border|borderWidth|size
27056
+ // pin|shape|fill|border|borderWidth|size (shape pin, circle, or vehicle: the route map's live position, a white
27057
+ // circle with a truck in the border colour)
27057
27058
  pin: function (args) {
27058
27059
  var shape = args[0], fill = args[1], border = args[2], bw = Number(args[3]), size = Number(args[4]);
27059
27060
  var g = markerGeometry(shape, size);
27060
- var reach = shape === 'circle' ? size / 2 : g.tipToHead; // farthest the shape goes from its centre
27061
+ var reach = shape === 'pin' ? g.tipToHead : size / 2; // farthest the shape goes from its centre
27061
27062
  var w = Math.ceil(2 * reach + 2 * PIN_PAD), h = w;
27062
27063
  return {
27063
27064
  width: w, height: h,
27064
27065
  draw: function (ctx, ratio) {
27065
27066
  var c = w / 2;
27067
+ if (shape === 'vehicle') { drawVehicle(ctx, ratio, c, size, border); return; }
27066
27068
  ctx.save();
27067
27069
  ctx.translate(c, c);
27068
27070
  var s = size / 2;
@@ -27093,6 +27095,31 @@ function smapCreateSdk(engine) {
27093
27095
  },
27094
27096
  };
27095
27097
  },
27098
+ // pinnum|shape|fill|border|borderWidth|size|textColor|text: a pin with its number drawn in, so a pin above covers a
27099
+ // pin below completely (used close up, where few pins are in view; further out the number is its own layer).
27100
+ pinnum: function (args, measure) {
27101
+ var pin = IMAGE_KINDS.pin(args.slice(0, 5));
27102
+ var size = Number(args[4]), color = args[5], text = args.slice(6).join('|');
27103
+ var px = 14 * size / 32, font = 'bold ' + px + 'px monospace';
27104
+ return {
27105
+ width: pin.width, height: pin.height,
27106
+ draw: function (ctx, ratio) {
27107
+ pin.draw(ctx, ratio);
27108
+ if (text === '') { return; }
27109
+ ctx.save();
27110
+ ctx.font = font;
27111
+ ctx.textAlign = 'center';
27112
+ ctx.textBaseline = 'middle';
27113
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
27114
+ ctx.shadowOffsetX = ratio;
27115
+ ctx.shadowOffsetY = ratio;
27116
+ ctx.shadowBlur = 2 * ratio;
27117
+ ctx.fillStyle = color;
27118
+ ctx.fillText(text, pin.width / 2, pin.height / 2 + 0.5);
27119
+ ctx.restore();
27120
+ },
27121
+ };
27122
+ },
27096
27123
  // num|color|size|text (bold, with the route map's text shadow)
27097
27124
  num: function (args, measure) {
27098
27125
  var color = args[0], size = Number(args[1]), text = args.slice(2).join('|');
@@ -27372,6 +27399,27 @@ function smapCreateSdk(engine) {
27372
27399
  };
27373
27400
 
27374
27401
  // Draws an image the SDK named (IMAGE_PREFIX + kind|args) and adds it to the map; other names are left alone.
27402
+ // The live-position marker of the route map: a white circle (85%) with a truck in the driver colour.
27403
+ function drawVehicle(ctx, ratio, c, size, color) {
27404
+ var r = size / 2, k = size / 24;
27405
+ ctx.save();
27406
+ ctx.translate(c, c);
27407
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
27408
+ ctx.shadowBlur = 2 * ratio;
27409
+ ctx.beginPath(); ctx.arc(0, 0, r, 0, Math.PI * 2);
27410
+ ctx.fillStyle = 'rgba(255,255,255,0.85)'; ctx.fill();
27411
+ ctx.shadowColor = 'transparent';
27412
+ ctx.scale(k, k);
27413
+ ctx.fillStyle = color;
27414
+ // cargo box, cab and two wheels, about 14 x 10 px on a 24 px marker
27415
+ ctx.fillRect(-7, -5, 8.5, 7);
27416
+ roundedPath(ctx, 2.2, -2.6, 4.8, 4.6, [[1.4, 1.4], [0.6, 0.6], [0, 0], [0, 0]]); ctx.fill();
27417
+ ctx.beginPath(); ctx.arc(-4.2, 3.4, 1.9, 0, Math.PI * 2); ctx.arc(4.1, 3.4, 1.9, 0, Math.PI * 2); ctx.fill();
27418
+ ctx.fillStyle = 'rgba(255,255,255,0.85)';
27419
+ ctx.beginPath(); ctx.arc(-4.2, 3.4, 0.7, 0, Math.PI * 2); ctx.arc(4.1, 3.4, 0.7, 0, Math.PI * 2); ctx.fill();
27420
+ ctx.restore();
27421
+ }
27422
+
27375
27423
  SMap.prototype._drawImage = function (id) {
27376
27424
  if (typeof id !== 'string' || id.indexOf(IMAGE_PREFIX) !== 0) { return; }
27377
27425
  var m = this.engine;
@@ -27552,7 +27600,7 @@ function smapCreateSdk(engine) {
27552
27600
  if (hit.o.marker && popupOptions.offset === undefined && this._popupDefaults.offset === undefined) {
27553
27601
  // Just above the pin as it is drawn now (its scale may follow the zoom).
27554
27602
  var mk = hit.o.marker, size = mk.size || 32, k = scaleAt(mk.scale, m.getZoom ? m.getZoom() : 0);
27555
- popupOptions.offset = mk.shape === 'circle' ? Math.round((size / 2) * k + 4) : Math.round((markerGeometry('pin', size).tipToHead + size / 2) * k + 2);
27603
+ popupOptions.offset = mk.shape === 'circle' || mk.shape === 'vehicle' ? Math.round((size / 2) * k + 4) : Math.round((markerGeometry('pin', size).tipToHead + size / 2) * k + 2);
27556
27604
  }
27557
27605
  // Above the pin, always (as on Superroute's route map); the map pans to make room instead of flipping it below.
27558
27606
  if (hit.o.marker && popupOptions.anchor === undefined && this._popupDefaults.anchor === undefined) { popupOptions.anchor = 'bottom'; }
@@ -27819,6 +27867,7 @@ function smapCreateSdk(engine) {
27819
27867
  // The engine refuses a filter key whose value is undefined (unclustered points have none).
27820
27868
  if (layer.filter === undefined) { delete layer.filter; }
27821
27869
  if (layer.minzoom === undefined) { delete layer.minzoom; }
27870
+ if (layer.maxzoom === undefined) { delete layer.maxzoom; }
27822
27871
  if (layer.paint === undefined) { delete layer.paint; }
27823
27872
  // A layer's own minzoom (a marker's textMinzoom / badgeMinzoom) never shows it before the layer's own range.
27824
27873
  for (var k in range) { layer[k] = k === 'minzoom' && layer.minzoom !== undefined ? Math.max(layer.minzoom, range.minzoom) : range[k]; }
@@ -27856,7 +27905,7 @@ function smapCreateSdk(engine) {
27856
27905
  // Pins like Superroute's route map, drawn on the GPU: one image per colour pair (and per number, per badge),
27857
27906
  // made the first time a feature shows it. Numbers and badges are their own layers above the pins.
27858
27907
  var mk = o.marker;
27859
- var shape = mk.shape === 'circle' ? 'circle' : 'pin';
27908
+ var shape = mk.shape === 'circle' || mk.shape === 'vehicle' ? mk.shape : 'pin';
27860
27909
  var size = mk.size || 32;
27861
27910
  var bw = mk.borderWidth !== undefined ? mk.borderWidth : 3;
27862
27911
  var g = markerGeometry(shape, size);
@@ -27867,22 +27916,29 @@ function smapCreateSdk(engine) {
27867
27916
  // Opacity of the pin, its number and its badge together (the route map draws settled stops at .72).
27868
27917
  var markerPaint = mk.opacity !== undefined ? { 'icon-opacity': valueExpr(mk.opacity, 1) } : undefined;
27869
27918
  main = src + '-points';
27919
+ // From bakeZoom on (default 13) a pin's number is drawn into its own image, so an upper pin covers a lower
27920
+ // one completely. Further out, where thousands of pins can be in view, the numbers stay one layer above the
27921
+ // pins (an image per colour pair instead of per pin and number). bakeZoom: false keeps them separate.
27922
+ var plainPin = drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]);
27923
+ var bakeZoom = mk.text !== undefined && mk.bakeZoom !== false ? (mk.bakeZoom !== undefined ? mk.bakeZoom : 13) : null;
27924
+ var pinImage = bakeZoom === null ? plainPin : ['step', ['zoom'], plainPin, bakeZoom,
27925
+ drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'), str(mk.text, '')])];
27870
27926
  var markerSpecs = [];
27871
27927
  var addMarker = function (layer) { markerSpecs.push(JSON.parse(JSON.stringify(layer))); add(layer); };
27872
27928
  addMarker({
27873
27929
  id: main, type: 'symbol', filter: only, paint: markerPaint,
27874
27930
  layout: {
27875
- 'icon-image': drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]),
27876
- 'icon-anchor': shape === 'circle' ? 'center' : 'bottom',
27931
+ 'icon-image': pinImage,
27932
+ 'icon-anchor': shape === 'pin' ? 'bottom' : 'center',
27877
27933
  // The image has room for the shadow below the tip: the tip, not the image's edge, is at the position.
27878
- 'icon-offset': [0, shape === 'circle' ? 0 : PIN_PAD], 'icon-size': scale,
27934
+ 'icon-offset': [0, shape === 'pin' ? PIN_PAD : 0], 'icon-size': scale,
27879
27935
  'icon-allow-overlap': true, 'icon-ignore-placement': true,
27880
27936
  },
27881
27937
  });
27882
27938
  if (mk.text !== undefined) {
27883
27939
  var text = str(mk.text, '');
27884
27940
  addMarker({
27885
- id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, paint: markerPaint,
27941
+ id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
27886
27942
  layout: {
27887
27943
  'icon-image': drawnImageId('num', [str(mk.textColor, '#ffffff'), String(size), text]),
27888
27944
  'icon-offset': [0, -g.tipToHead], 'icon-size': scale, 'icon-allow-overlap': true, 'icon-ignore-placement': true,
@@ -27929,7 +27985,9 @@ function smapCreateSdk(engine) {
27929
27985
  if (o.label) {
27930
27986
  add({
27931
27987
  id: src + '-labels', type: 'symbol', filter: only,
27932
- layout: { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.2], 'text-anchor': 'top', 'text-optional': true },
27988
+ layout: o.labelPosition === 'right'
27989
+ ? { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 11, 'text-offset': [1.3, 0], 'text-anchor': 'left', 'text-optional': true, 'text-max-width': 12 }
27990
+ : { 'text-field': ['to-string', ['get', o.label]], 'text-font': ['Noto Sans Regular'], 'text-size': 12, 'text-offset': [0, 1.2], 'text-anchor': 'top', 'text-optional': true },
27933
27991
  paint: { 'text-color': '#1f2937', 'text-halo-color': '#ffffff', 'text-halo-width': 1.4 },
27934
27992
  });
27935
27993
  }
@@ -28011,6 +28069,7 @@ function smapCreateSdk(engine) {
28011
28069
  if (layer.filter === undefined) { delete layer.filter; }
28012
28070
  if (layer.paint === undefined) { delete layer.paint; }
28013
28071
  if (layer.minzoom === undefined) { delete layer.minzoom; }
28072
+ if (layer.maxzoom === undefined) { delete layer.maxzoom; }
28014
28073
  m.addLayer(layer);
28015
28074
  def.engineLayers.push(layer.id);
28016
28075
  if (!top) { top = layer.id; }
@@ -28070,7 +28129,7 @@ function SMapMap(options) {
28070
28129
  SMapMap.prototype = core.Map.prototype;
28071
28130
  return new core.Map(options);
28072
28131
  }
28073
- var smapVersion = "1.0.5";
28132
+ var smapVersion = "1.0.6";
28074
28133
  var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
28075
28134
  /** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
28076
28135
  function smapGetEngine() { return smapLoad().engine; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superrouteca/smap",
3
- "version": "1.0.5",
3
+ "version": "1.0.6",
4
4
  "description": "sMap for the web: maps from Superroute's sMap service, the Web SDK (map view) and the JS client (tokens, nearest node, styles, tiles).",
5
5
  "license": "MIT",
6
6
  "author": "Goodneo Solutions",
package/types/smap.d.cts CHANGED
@@ -104,7 +104,7 @@ export type StyleValue<T = string> = T | { property: string; default?: T } | unk
104
104
  /** Pins like Superroute's route map, drawn on the GPU (one image per colour pair, made when first shown). */
105
105
  export interface MarkerOptions {
106
106
  /** `pin` (default: a teardrop whose tip is at the position) or `circle` (centred on it). */
107
- shape?: 'pin' | 'circle';
107
+ shape?: 'pin' | 'circle' | 'vehicle';
108
108
  /** Inside colour (default white). */
109
109
  fill?: StyleValue;
110
110
  /** Border colour (default the layer's `color`). */
@@ -124,6 +124,8 @@ export interface MarkerOptions {
124
124
  scale?: number | unknown[];
125
125
  /** The pin under the pointer comes to the front (default true). */
126
126
  hoverRaise?: boolean;
127
+ /** From this zoom on (default 13) the number is drawn into the pin, so an upper pin covers a lower one completely; false keeps it a separate layer. */
128
+ bakeZoom?: number | false;
127
129
  /**
128
130
  * Pins piled up under a click (default on): with `threshold` (4) or more within `radius` (12 px) the map zooms in by
129
131
  * `zoomBy` (2) up to `maxZoom` (17); close up, or for pins at one spot, a chooser lists them (`label(properties)`).
@@ -153,6 +155,8 @@ export interface PointOptions extends CommonLayerOptions {
153
155
  iconAnchor?: string;
154
156
  /** The property written under each point. */
155
157
  label?: string;
158
+ /** Where the label sits: below the point (default) or 'right' of it. */
159
+ labelPosition?: 'below' | 'right';
156
160
  }
157
161
 
158
162
  export interface LineOptions extends CommonLayerOptions {
package/types/smap.d.mts CHANGED
@@ -104,7 +104,7 @@ export type StyleValue<T = string> = T | { property: string; default?: T } | unk
104
104
  /** Pins like Superroute's route map, drawn on the GPU (one image per colour pair, made when first shown). */
105
105
  export interface MarkerOptions {
106
106
  /** `pin` (default: a teardrop whose tip is at the position) or `circle` (centred on it). */
107
- shape?: 'pin' | 'circle';
107
+ shape?: 'pin' | 'circle' | 'vehicle';
108
108
  /** Inside colour (default white). */
109
109
  fill?: StyleValue;
110
110
  /** Border colour (default the layer's `color`). */
@@ -124,6 +124,8 @@ export interface MarkerOptions {
124
124
  scale?: number | unknown[];
125
125
  /** The pin under the pointer comes to the front (default true). */
126
126
  hoverRaise?: boolean;
127
+ /** From this zoom on (default 13) the number is drawn into the pin, so an upper pin covers a lower one completely; false keeps it a separate layer. */
128
+ bakeZoom?: number | false;
127
129
  /**
128
130
  * Pins piled up under a click (default on): with `threshold` (4) or more within `radius` (12 px) the map zooms in by
129
131
  * `zoomBy` (2) up to `maxZoom` (17); close up, or for pins at one spot, a chooser lists them (`label(properties)`).
@@ -153,6 +155,8 @@ export interface PointOptions extends CommonLayerOptions {
153
155
  iconAnchor?: string;
154
156
  /** The property written under each point. */
155
157
  label?: string;
158
+ /** Where the label sits: below the point (default) or 'right' of it. */
159
+ labelPosition?: 'below' | 'right';
156
160
  }
157
161
 
158
162
  export interface LineOptions extends CommonLayerOptions {