@superrouteca/smap 1.2.0 → 1.2.2

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,18 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.2 — 2026-10-29
4
+
5
+ - Plan editor: opening a distance snapshot sends the plan's points (every job location and vehicle start and end), so
6
+ the legs between them are measured ahead and edits are answered faster. Nothing to change in your code; servers
7
+ that do not read the points work as before.
8
+
9
+ ## 1.2.1 — 2026-10-28
10
+
11
+ - Pins: the number and the count badge are drawn into each pin at every zoom, so where pins overlap the upper pin
12
+ covers the lower one with its number and badge, as on Superroute's route map. Before, further out than zoom 13
13
+ numbers and badges were their own layers above every pin. `bakeZoom` (a zoom) keeps the former drawing further out
14
+ than it; `bakeZoom: false` at every zoom.
15
+
3
16
  ## 1.2.0 — 2026-10-28
4
17
 
5
18
  - Maps without WebGL: where the browser has no WebGL 2 (old computers, remote desktops, graphics acceleration
package/README.md CHANGED
@@ -450,8 +450,8 @@ editor.destroy();
450
450
  fast path may appear or go). `state().path` and the `change` event's `path` say where the last answer came from
451
451
  (`'fast'` or `'standard'`); `fastPath: false` keeps every call on the standard path. The snapshot works on both
452
452
  paths with the same id.
453
- - **Distance snapshot**: the editor opens one (`POST /v1/plan/matrix`) and names it in every call, so legs measured
454
- once are not measured again. When an answer says the snapshot expired (`X-Plan-Matrix: expired`, or a `410`), it
453
+ - **Distance snapshot**: the editor opens one (`POST /v1/plan/matrix`, with the plan's points so the legs between
454
+ them can be measured ahead) and names it in every call, so legs measured once are not measured again. When an answer says the snapshot expired (`X-Plan-Matrix: expired`, or a `410`), it
455
455
  opens a new one and carries on; a snapshot that cannot be opened never stops an edit. `destroy()` closes it.
456
456
  - **Calls in flight**: a newer edit cancels the call before it. An edit you can see the outcome of (a move, an order,
457
457
  a removal) is drawn at once and priced when the answer arrives; `pending` in the `change` event says it is not
package/dist/SOURCE.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
- "sdkVersion": "1.2.0",
2
+ "sdkVersion": "1.2.2",
3
3
  "engine": "maplibre-gl 6.16.0",
4
4
  "files": {
5
- "smap.js": "fba23ad160ae1a42578ca8250128ad9bb2e26ccff3d369784da6a1062a30301b",
5
+ "smap.js": "b5c9dae63b6999dbc28ec9ae76a828e1cfd061a6c2acb157f631e75786c26367",
6
6
  "smap.css": "9bc4b14821de97da199e6af4f18541eeaf53a2ae91e5831e7224f57fe6b77660",
7
- "smap-client.js": "1ef8bd3903d6a163cb9ce409cb3d6635beb2a9afbede6b7a9c079c3acdb53035",
7
+ "smap-client.js": "169f1b6efc7241404be51e21e40238b20098b471f7d560809a7a4cfde5116f5a",
8
8
  "smap-client.d.ts": "5f0df11bf60c5a9ced34522a284de81260c27871678ab772f16da9ccf6883f58",
9
- "smap.mjs": "c79d2307250322d76d2470acd4487dfe4b4346df613a664e95b257ba3c4cd294",
10
- "smap.cjs": "795eb63715a35d107f5ba30fa8fa6e1840a0b8f1c2edaf7fadbcfa7e6004cedb",
11
- "smap-client.cjs": "e697643beb6dfa5b120a6b4f910b575a6d65862ae9c2a1ccdfb28b8da2320101",
12
- "smap-client.mjs": "17aa1babdc6da105c89b1bf0a1e70a4ee29a720a1436f46860851aa8b4185ed2",
9
+ "smap.mjs": "9f2e3dc715d33b647475d6082a39843a5f5acf683d45f2a8373c0e5a361c4092",
10
+ "smap.cjs": "6e51c2b7df527b09a77cc551d4d7c8c06a7f884333d920c7c2009029cea41890",
11
+ "smap-client.cjs": "0328f85f3313d55b3a120b123dea5a1f319fceaa42b3321ff9122c6b15ed31bf",
12
+ "smap-client.mjs": "b649b0ff23677164ccca36122ddc244689d6dfbd8cb69604ce245304c13b06cd",
13
13
  "smap-client.d.cts": "e0fe4eb54cd66b831116082f42bd2f7de260f9f12a2741f4004f8cf10c252116",
14
14
  "smap-client.d.mts": "96830d316ddb33b4ff8edb8b6f810337479636114f75c4170d95038ffda74103",
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.2.0 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
2
+ /*! sMap JS client 1.2.2 — 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.2.0';
22
+ var VERSION = '1.2.2';
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.2.0 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
1
+ /*! sMap JS client 1.2.2 — 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.2.0';
21
+ var VERSION = '1.2.2';
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.2.0 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
4
+ /*! sMap JS client 1.2.2 — 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.2.0';
24
+ var VERSION = '1.2.2';
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.2.0. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
3
+ /*! sMap Web SDK 1.2.2. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
4
4
 
5
5
  var smapCore = null;
6
6
  function smapCreateEngine() {
@@ -28461,7 +28461,7 @@ function smapCreateCanvas() {
28461
28461
  var exports = module.exports;
28462
28462
  var define;
28463
28463
  var self = {};
28464
- // sMap canvas renderer (M61): the map drawn on a 2D canvas for browsers without WebGL 2. It answers the part of the
28464
+ // sMap canvas renderer (M67): the map drawn on a 2D canvas for browsers without WebGL 2. It answers the part of the
28465
28465
  // rendering engine's map interface the Web SDK uses — camera, events, sources, layers of the kinds the SDK makes
28466
28466
  // (raster, fill, line, circle, symbol, heatmap), hit tests, controls and popups — so the SDK's calls work unchanged.
28467
28467
  // The street map comes as raster tiles. What needs WebGL (3D buildings, terrain, tilting, turning, custom 3D layers,
@@ -30757,6 +30757,9 @@ function smapCreatePlan() {
30757
30757
  var body = {};
30758
30758
  if (this._context.region) { body.region = this._context.region; }
30759
30759
  if (this._context.profile) { body.profile = this._context.profile; }
30760
+ // The plan's points, so the server can measure their legs into the snapshot ahead of the edits (E24).
30761
+ var points = this._points();
30762
+ if (points.length) { body.points = points; }
30760
30763
  this._openingKey = key;
30761
30764
  var opening = this._transport.send('POST', PATHS.matrix, body).then(function (res) {
30762
30765
  var id = res.data && res.data.matrix;
@@ -30781,6 +30784,27 @@ function smapCreatePlan() {
30781
30784
  return opening;
30782
30785
  };
30783
30786
 
30787
+ // The plan's points as a snapshot opening sends them: every job location, then every vehicle's start and end, exactly
30788
+ // as the plan holds them (the numbers the plan calls send, since legs are matched exactly), each sent once, first seen
30789
+ // first.
30790
+ PlanEditor.prototype._points = function () {
30791
+ var entry = this._history[this._at];
30792
+ var out = [];
30793
+ var seen = {};
30794
+ function add(v) {
30795
+ var ll = lngLat(v);
30796
+ if (!ll || !isFinite(ll[0]) || !isFinite(ll[1])) { return; }
30797
+ var p = { lat: ll[1], lng: ll[0] };
30798
+ var k = p.lat + ',' + p.lng;
30799
+ if (seen[k]) { return; }
30800
+ seen[k] = true;
30801
+ out.push(p);
30802
+ }
30803
+ ((entry && entry.jobs) || []).forEach(function (j) { add(j && j.location); });
30804
+ (this._vehicles || []).forEach(function (v) { if (v) { add(v.start); add(v.end); } });
30805
+ return out;
30806
+ };
30807
+
30784
30808
  // ------------------------------------------------------------------ texts (14 languages)
30785
30809
 
30786
30810
  var TEXT_KEYS = ['editor', 'busy', 'undo', 'redo', 'refine', 'total', 'distance', 'drivingTime', 'stops', 'violations',
@@ -31590,7 +31614,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31590
31614
  })(typeof self !== 'undefined' ? self : this, function (engine, interop, canvasLib, plan) {
31591
31615
  'use strict';
31592
31616
 
31593
- var VERSION = '1.2.0';
31617
+ var VERSION = '1.2.2';
31594
31618
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
31595
31619
  var BASEMAP_SOURCE = 'basemap';
31596
31620
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -31815,28 +31839,51 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31815
31839
  },
31816
31840
  };
31817
31841
  },
31818
- // pinnum|shape|fill|border|borderWidth|size|textColor|text: a pin with its number drawn in, so a pin above covers a
31819
- // pin below completely (used close up, where few pins are in view; further out the number is its own layer).
31842
+ // pinnum|shape|fill|border|borderWidth|size|textColor|badge|text: a pin with its number and its count badge drawn
31843
+ // in, so a pin above covers a pin below completely, number and badge included, as one element of the route map's
31844
+ // pins does. An empty number or badge is left out; the text comes last because it may hold '|'.
31820
31845
  pinnum: function (args, measure) {
31821
31846
  var pin = IMAGE_KINDS.pin(args.slice(0, 5));
31822
- var size = Number(args[4]), color = args[5], text = args.slice(6).join('|');
31847
+ var size = Number(args[4]), color = args[5], badgeText = args[6] || '', text = args.slice(7).join('|');
31823
31848
  var px = 14 * size / 32, font = 'bold ' + px + 'px monospace';
31849
+ var badge = badgeText !== '' ? IMAGE_KINDS.badge([badgeText], measure) : null;
31850
+ var g = markerGeometry(args[0], size);
31851
+ // The badge's centre is where its own layer puts it: g.badge from the tip, i.e. from the head's centre for a
31852
+ // pin (the image's centre), so the image is widened on both sides when the badge reaches past it.
31853
+ var w = pin.width, h = pin.height;
31854
+ if (badge) {
31855
+ var reach = Math.abs(g.badge[0]) + badge.width / 2;
31856
+ w = Math.max(w, Math.ceil(2 * reach));
31857
+ }
31824
31858
  return {
31825
- width: pin.width, height: pin.height,
31859
+ width: w, height: h,
31826
31860
  draw: function (ctx, ratio) {
31827
- pin.draw(ctx, ratio);
31828
- if (text === '') { return; }
31829
31861
  ctx.save();
31830
- ctx.font = font;
31831
- ctx.textAlign = 'center';
31832
- ctx.textBaseline = 'middle';
31833
- ctx.shadowColor = 'rgba(0,0,0,0.5)';
31834
- ctx.shadowOffsetX = ratio;
31835
- ctx.shadowOffsetY = ratio;
31836
- ctx.shadowBlur = 2 * ratio;
31837
- ctx.fillStyle = color;
31838
- ctx.fillText(text, pin.width / 2, pin.height / 2 + 0.5);
31862
+ ctx.translate((w - pin.width) / 2, 0);
31863
+ pin.draw(ctx, ratio);
31839
31864
  ctx.restore();
31865
+ if (text !== '') {
31866
+ ctx.save();
31867
+ ctx.font = font;
31868
+ ctx.textAlign = 'center';
31869
+ ctx.textBaseline = 'middle';
31870
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
31871
+ ctx.shadowOffsetX = ratio;
31872
+ ctx.shadowOffsetY = ratio;
31873
+ ctx.shadowBlur = 2 * ratio;
31874
+ ctx.fillStyle = color;
31875
+ ctx.fillText(text, w / 2, h / 2 + 0.5);
31876
+ ctx.restore();
31877
+ }
31878
+ if (badge) {
31879
+ // A pin's head is the image's centre and its tip half the image height lower; the badge sits g.badge from
31880
+ // the tip (a circle's centre is the image's centre).
31881
+ var tipY = args[0] === 'pin' ? h / 2 + g.tipToHead : h / 2;
31882
+ ctx.save();
31883
+ ctx.translate(w / 2 + g.badge[0] - badge.width / 2, tipY + g.badge[1] - badge.height / 2);
31884
+ badge.draw(ctx, ratio);
31885
+ ctx.restore();
31886
+ }
31840
31887
  },
31841
31888
  };
31842
31889
  },
@@ -32002,7 +32049,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
32002
32049
  * minWidth, offset, anchor, closeButton, closeOnClick, focusAfterOpen)
32003
32050
  * @param {string|Node} [options.popupLoading] shown while a popup's content is a pending promise (default: dots)
32004
32051
  * @param {'auto'|'webgl'|'canvas'} [options.renderer] 'auto' (default): the WebGL map, or the canvas map (raster
32005
- * tiles, layers drawn in 2D) where the browser has no WebGL 2 (M61)
32052
+ * tiles, layers drawn in 2D) where the browser has no WebGL 2 (M67)
32006
32053
  * Every other option (center, zoom, pitch, bearing, minZoom, maxZoom, maxBounds, hash, …) goes to the engine.
32007
32054
  */
32008
32055
  function SMap(options) {
@@ -32058,7 +32105,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
32058
32105
  this._toolHandlers = [];
32059
32106
 
32060
32107
  var self = this;
32061
- // The renderer (M61): the WebGL engine, or the canvas map where there is no WebGL 2 (or when asked for).
32108
+ // The renderer (M67): the WebGL engine, or the canvas map where there is no WebGL 2 (or when asked for).
32062
32109
  var want = options.renderer || 'auto';
32063
32110
  // Asked of the page itself; without a page (a server, a test) the WebGL engine is kept.
32064
32111
  var hasPage = typeof document !== 'undefined' && !!document && typeof document.createElement === 'function';
@@ -34405,7 +34452,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34405
34452
  */
34406
34453
  SMap.prototype.setBaseImagery = function (imagery) {
34407
34454
  if (imagery && (!Array.isArray(imagery.tiles) || !imagery.tiles.length)) { throw new Error('sMap: base imagery needs tiles'); }
34408
- // Google's and Bing's tiles only through their own terms: Google's with setGoogleBase (M31, M66).
34455
+ // Google's and Bing's tiles only through their own terms: Google's with setGoogleBase (M31, M72).
34409
34456
  if (imagery) { interop.refuseForbidden(imagery.tiles); }
34410
34457
  return this._setBaseImagery(imagery);
34411
34458
  };
@@ -35446,13 +35493,31 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35446
35493
  // Opacity of the pin, its number and its badge together (the route map draws settled stops at .72).
35447
35494
  var markerPaint = mk.opacity !== undefined ? { 'icon-opacity': valueExpr(mk.opacity, 1) } : undefined;
35448
35495
  main = src + '-points';
35449
- // From bakeZoom on (default 13) a pin's number is drawn into its own image, so an upper pin covers a lower
35450
- // one completely. Further out, where thousands of pins can be in view, the numbers stay one layer above the
35451
- // pins (an image per colour pair instead of per pin and number). bakeZoom: false keeps them separate.
35496
+ // A pin's number and count badge are drawn into its own image (as one element of the route map's pins), so an
35497
+ // upper pin covers a lower one completely, number and badge included. bakeZoom (a zoom) keeps them their own
35498
+ // layers above the pins further out than it — fewer images where thousands of pins are in view, but numbers
35499
+ // over every pin there — and bakeZoom: false keeps them apart at every zoom. textMinzoom / badgeMinzoom start
35500
+ // them at their zoom either way.
35452
35501
  var plainPin = drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]);
35453
- var bakeZoom = mk.text !== undefined && mk.bakeZoom !== false ? (mk.bakeZoom !== undefined ? mk.bakeZoom : 13) : null;
35454
- var pinImage = bakeZoom === null ? plainPin : ['step', ['zoom'], plainPin, bakeZoom,
35455
- drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'), str(mk.text, '')])];
35502
+ var hasText = mk.text !== undefined, hasBadge = mk.badge !== undefined;
35503
+ var bakeZoom = (hasText || hasBadge) && mk.bakeZoom !== false ? (typeof mk.bakeZoom === 'number' ? mk.bakeZoom : 0) : null;
35504
+ var badgeNumber = ['to-number', valueExpr(mk.badge, 0), 0];
35505
+ var textMin = typeof mk.textMinzoom === 'number' ? mk.textMinzoom : 0;
35506
+ var badgeMin = typeof mk.badgeMinzoom === 'number' ? mk.badgeMinzoom : 0;
35507
+ var imageAt = function (z) {
35508
+ if (bakeZoom === null || z < bakeZoom) { return plainPin; }
35509
+ var withText = hasText && z >= textMin, withBadge = hasBadge && z >= badgeMin;
35510
+ if (!withText && !withBadge) { return plainPin; }
35511
+ return drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'),
35512
+ withBadge ? ['case', ['>', badgeNumber, 1], ['to-string', badgeNumber], ''] : '', withText ? str(mk.text, '') : '']);
35513
+ };
35514
+ var breaks = [bakeZoom, textMin, badgeMin].filter(function (z, i, all) { return typeof z === 'number' && z > 0 && all.indexOf(z) === i; })
35515
+ .sort(function (x, y) { return x - y; });
35516
+ var pinImage = imageAt(0);
35517
+ if (breaks.length) {
35518
+ pinImage = ['step', ['zoom'], imageAt(0)];
35519
+ breaks.forEach(function (z) { pinImage.push(z, imageAt(z)); });
35520
+ }
35456
35521
  var markerSpecs = [];
35457
35522
  var addMarker = function (layer) { markerSpecs.push(JSON.parse(JSON.stringify(layer))); add(layer); };
35458
35523
  addMarker({
@@ -35465,7 +35530,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35465
35530
  'icon-allow-overlap': true, 'icon-ignore-placement': true,
35466
35531
  },
35467
35532
  });
35468
- if (mk.text !== undefined) {
35533
+ if (hasText && bakeZoom !== 0) {
35469
35534
  var text = str(mk.text, '');
35470
35535
  addMarker({
35471
35536
  id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
@@ -35475,10 +35540,10 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35475
35540
  },
35476
35541
  });
35477
35542
  }
35478
- if (mk.badge !== undefined) {
35479
- var badge = ['to-number', valueExpr(mk.badge, 0), 0];
35543
+ if (hasBadge && bakeZoom !== 0) {
35544
+ var badge = badgeNumber;
35480
35545
  addMarker({
35481
- id: src + '-badges', type: 'symbol', filter: both(['>', badge, 1]), minzoom: mk.badgeMinzoom, paint: markerPaint,
35546
+ id: src + '-badges', type: 'symbol', filter: both(['>', badge, 1]), minzoom: mk.badgeMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
35482
35547
  layout: {
35483
35548
  'icon-image': drawnImageId('badge', [['to-string', badge]]),
35484
35549
  'icon-offset': g.badge, 'icon-size': scale,
@@ -35732,7 +35797,7 @@ function SMapMap(options) {
35732
35797
  SMapMap.prototype = core.Map.prototype;
35733
35798
  return new core.Map(options);
35734
35799
  }
35735
- var smapVersion = "1.2.0";
35800
+ var smapVersion = "1.2.2";
35736
35801
  var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
35737
35802
  /** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
35738
35803
  function smapGetEngine() { return smapLoad().engine; }
package/dist/smap.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! sMap Web SDK 1.2.0. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
1
+ /*! sMap Web SDK 1.2.2. 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';
@@ -28458,7 +28458,7 @@ function smapCreateCanvas() {
28458
28458
  var exports = module.exports;
28459
28459
  var define;
28460
28460
  var self = {};
28461
- // sMap canvas renderer (M61): the map drawn on a 2D canvas for browsers without WebGL 2. It answers the part of the
28461
+ // sMap canvas renderer (M67): the map drawn on a 2D canvas for browsers without WebGL 2. It answers the part of the
28462
28462
  // rendering engine's map interface the Web SDK uses — camera, events, sources, layers of the kinds the SDK makes
28463
28463
  // (raster, fill, line, circle, symbol, heatmap), hit tests, controls and popups — so the SDK's calls work unchanged.
28464
28464
  // The street map comes as raster tiles. What needs WebGL (3D buildings, terrain, tilting, turning, custom 3D layers,
@@ -30754,6 +30754,9 @@ function smapCreatePlan() {
30754
30754
  var body = {};
30755
30755
  if (this._context.region) { body.region = this._context.region; }
30756
30756
  if (this._context.profile) { body.profile = this._context.profile; }
30757
+ // The plan's points, so the server can measure their legs into the snapshot ahead of the edits (E24).
30758
+ var points = this._points();
30759
+ if (points.length) { body.points = points; }
30757
30760
  this._openingKey = key;
30758
30761
  var opening = this._transport.send('POST', PATHS.matrix, body).then(function (res) {
30759
30762
  var id = res.data && res.data.matrix;
@@ -30778,6 +30781,27 @@ function smapCreatePlan() {
30778
30781
  return opening;
30779
30782
  };
30780
30783
 
30784
+ // The plan's points as a snapshot opening sends them: every job location, then every vehicle's start and end, exactly
30785
+ // as the plan holds them (the numbers the plan calls send, since legs are matched exactly), each sent once, first seen
30786
+ // first.
30787
+ PlanEditor.prototype._points = function () {
30788
+ var entry = this._history[this._at];
30789
+ var out = [];
30790
+ var seen = {};
30791
+ function add(v) {
30792
+ var ll = lngLat(v);
30793
+ if (!ll || !isFinite(ll[0]) || !isFinite(ll[1])) { return; }
30794
+ var p = { lat: ll[1], lng: ll[0] };
30795
+ var k = p.lat + ',' + p.lng;
30796
+ if (seen[k]) { return; }
30797
+ seen[k] = true;
30798
+ out.push(p);
30799
+ }
30800
+ ((entry && entry.jobs) || []).forEach(function (j) { add(j && j.location); });
30801
+ (this._vehicles || []).forEach(function (v) { if (v) { add(v.start); add(v.end); } });
30802
+ return out;
30803
+ };
30804
+
30781
30805
  // ------------------------------------------------------------------ texts (14 languages)
30782
30806
 
30783
30807
  var TEXT_KEYS = ['editor', 'busy', 'undo', 'redo', 'refine', 'total', 'distance', 'drivingTime', 'stops', 'violations',
@@ -31587,7 +31611,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31587
31611
  })(typeof self !== 'undefined' ? self : this, function (engine, interop, canvasLib, plan) {
31588
31612
  'use strict';
31589
31613
 
31590
- var VERSION = '1.2.0';
31614
+ var VERSION = '1.2.2';
31591
31615
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
31592
31616
  var BASEMAP_SOURCE = 'basemap';
31593
31617
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -31812,28 +31836,51 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31812
31836
  },
31813
31837
  };
31814
31838
  },
31815
- // pinnum|shape|fill|border|borderWidth|size|textColor|text: a pin with its number drawn in, so a pin above covers a
31816
- // pin below completely (used close up, where few pins are in view; further out the number is its own layer).
31839
+ // pinnum|shape|fill|border|borderWidth|size|textColor|badge|text: a pin with its number and its count badge drawn
31840
+ // in, so a pin above covers a pin below completely, number and badge included, as one element of the route map's
31841
+ // pins does. An empty number or badge is left out; the text comes last because it may hold '|'.
31817
31842
  pinnum: function (args, measure) {
31818
31843
  var pin = IMAGE_KINDS.pin(args.slice(0, 5));
31819
- var size = Number(args[4]), color = args[5], text = args.slice(6).join('|');
31844
+ var size = Number(args[4]), color = args[5], badgeText = args[6] || '', text = args.slice(7).join('|');
31820
31845
  var px = 14 * size / 32, font = 'bold ' + px + 'px monospace';
31846
+ var badge = badgeText !== '' ? IMAGE_KINDS.badge([badgeText], measure) : null;
31847
+ var g = markerGeometry(args[0], size);
31848
+ // The badge's centre is where its own layer puts it: g.badge from the tip, i.e. from the head's centre for a
31849
+ // pin (the image's centre), so the image is widened on both sides when the badge reaches past it.
31850
+ var w = pin.width, h = pin.height;
31851
+ if (badge) {
31852
+ var reach = Math.abs(g.badge[0]) + badge.width / 2;
31853
+ w = Math.max(w, Math.ceil(2 * reach));
31854
+ }
31821
31855
  return {
31822
- width: pin.width, height: pin.height,
31856
+ width: w, height: h,
31823
31857
  draw: function (ctx, ratio) {
31824
- pin.draw(ctx, ratio);
31825
- if (text === '') { return; }
31826
31858
  ctx.save();
31827
- ctx.font = font;
31828
- ctx.textAlign = 'center';
31829
- ctx.textBaseline = 'middle';
31830
- ctx.shadowColor = 'rgba(0,0,0,0.5)';
31831
- ctx.shadowOffsetX = ratio;
31832
- ctx.shadowOffsetY = ratio;
31833
- ctx.shadowBlur = 2 * ratio;
31834
- ctx.fillStyle = color;
31835
- ctx.fillText(text, pin.width / 2, pin.height / 2 + 0.5);
31859
+ ctx.translate((w - pin.width) / 2, 0);
31860
+ pin.draw(ctx, ratio);
31836
31861
  ctx.restore();
31862
+ if (text !== '') {
31863
+ ctx.save();
31864
+ ctx.font = font;
31865
+ ctx.textAlign = 'center';
31866
+ ctx.textBaseline = 'middle';
31867
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
31868
+ ctx.shadowOffsetX = ratio;
31869
+ ctx.shadowOffsetY = ratio;
31870
+ ctx.shadowBlur = 2 * ratio;
31871
+ ctx.fillStyle = color;
31872
+ ctx.fillText(text, w / 2, h / 2 + 0.5);
31873
+ ctx.restore();
31874
+ }
31875
+ if (badge) {
31876
+ // A pin's head is the image's centre and its tip half the image height lower; the badge sits g.badge from
31877
+ // the tip (a circle's centre is the image's centre).
31878
+ var tipY = args[0] === 'pin' ? h / 2 + g.tipToHead : h / 2;
31879
+ ctx.save();
31880
+ ctx.translate(w / 2 + g.badge[0] - badge.width / 2, tipY + g.badge[1] - badge.height / 2);
31881
+ badge.draw(ctx, ratio);
31882
+ ctx.restore();
31883
+ }
31837
31884
  },
31838
31885
  };
31839
31886
  },
@@ -31999,7 +32046,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31999
32046
  * minWidth, offset, anchor, closeButton, closeOnClick, focusAfterOpen)
32000
32047
  * @param {string|Node} [options.popupLoading] shown while a popup's content is a pending promise (default: dots)
32001
32048
  * @param {'auto'|'webgl'|'canvas'} [options.renderer] 'auto' (default): the WebGL map, or the canvas map (raster
32002
- * tiles, layers drawn in 2D) where the browser has no WebGL 2 (M61)
32049
+ * tiles, layers drawn in 2D) where the browser has no WebGL 2 (M67)
32003
32050
  * Every other option (center, zoom, pitch, bearing, minZoom, maxZoom, maxBounds, hash, …) goes to the engine.
32004
32051
  */
32005
32052
  function SMap(options) {
@@ -32055,7 +32102,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
32055
32102
  this._toolHandlers = [];
32056
32103
 
32057
32104
  var self = this;
32058
- // The renderer (M61): the WebGL engine, or the canvas map where there is no WebGL 2 (or when asked for).
32105
+ // The renderer (M67): the WebGL engine, or the canvas map where there is no WebGL 2 (or when asked for).
32059
32106
  var want = options.renderer || 'auto';
32060
32107
  // Asked of the page itself; without a page (a server, a test) the WebGL engine is kept.
32061
32108
  var hasPage = typeof document !== 'undefined' && !!document && typeof document.createElement === 'function';
@@ -34402,7 +34449,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34402
34449
  */
34403
34450
  SMap.prototype.setBaseImagery = function (imagery) {
34404
34451
  if (imagery && (!Array.isArray(imagery.tiles) || !imagery.tiles.length)) { throw new Error('sMap: base imagery needs tiles'); }
34405
- // Google's and Bing's tiles only through their own terms: Google's with setGoogleBase (M31, M66).
34452
+ // Google's and Bing's tiles only through their own terms: Google's with setGoogleBase (M31, M72).
34406
34453
  if (imagery) { interop.refuseForbidden(imagery.tiles); }
34407
34454
  return this._setBaseImagery(imagery);
34408
34455
  };
@@ -35443,13 +35490,31 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35443
35490
  // Opacity of the pin, its number and its badge together (the route map draws settled stops at .72).
35444
35491
  var markerPaint = mk.opacity !== undefined ? { 'icon-opacity': valueExpr(mk.opacity, 1) } : undefined;
35445
35492
  main = src + '-points';
35446
- // From bakeZoom on (default 13) a pin's number is drawn into its own image, so an upper pin covers a lower
35447
- // one completely. Further out, where thousands of pins can be in view, the numbers stay one layer above the
35448
- // pins (an image per colour pair instead of per pin and number). bakeZoom: false keeps them separate.
35493
+ // A pin's number and count badge are drawn into its own image (as one element of the route map's pins), so an
35494
+ // upper pin covers a lower one completely, number and badge included. bakeZoom (a zoom) keeps them their own
35495
+ // layers above the pins further out than it — fewer images where thousands of pins are in view, but numbers
35496
+ // over every pin there — and bakeZoom: false keeps them apart at every zoom. textMinzoom / badgeMinzoom start
35497
+ // them at their zoom either way.
35449
35498
  var plainPin = drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]);
35450
- var bakeZoom = mk.text !== undefined && mk.bakeZoom !== false ? (mk.bakeZoom !== undefined ? mk.bakeZoom : 13) : null;
35451
- var pinImage = bakeZoom === null ? plainPin : ['step', ['zoom'], plainPin, bakeZoom,
35452
- drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'), str(mk.text, '')])];
35499
+ var hasText = mk.text !== undefined, hasBadge = mk.badge !== undefined;
35500
+ var bakeZoom = (hasText || hasBadge) && mk.bakeZoom !== false ? (typeof mk.bakeZoom === 'number' ? mk.bakeZoom : 0) : null;
35501
+ var badgeNumber = ['to-number', valueExpr(mk.badge, 0), 0];
35502
+ var textMin = typeof mk.textMinzoom === 'number' ? mk.textMinzoom : 0;
35503
+ var badgeMin = typeof mk.badgeMinzoom === 'number' ? mk.badgeMinzoom : 0;
35504
+ var imageAt = function (z) {
35505
+ if (bakeZoom === null || z < bakeZoom) { return plainPin; }
35506
+ var withText = hasText && z >= textMin, withBadge = hasBadge && z >= badgeMin;
35507
+ if (!withText && !withBadge) { return plainPin; }
35508
+ return drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'),
35509
+ withBadge ? ['case', ['>', badgeNumber, 1], ['to-string', badgeNumber], ''] : '', withText ? str(mk.text, '') : '']);
35510
+ };
35511
+ var breaks = [bakeZoom, textMin, badgeMin].filter(function (z, i, all) { return typeof z === 'number' && z > 0 && all.indexOf(z) === i; })
35512
+ .sort(function (x, y) { return x - y; });
35513
+ var pinImage = imageAt(0);
35514
+ if (breaks.length) {
35515
+ pinImage = ['step', ['zoom'], imageAt(0)];
35516
+ breaks.forEach(function (z) { pinImage.push(z, imageAt(z)); });
35517
+ }
35453
35518
  var markerSpecs = [];
35454
35519
  var addMarker = function (layer) { markerSpecs.push(JSON.parse(JSON.stringify(layer))); add(layer); };
35455
35520
  addMarker({
@@ -35462,7 +35527,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35462
35527
  'icon-allow-overlap': true, 'icon-ignore-placement': true,
35463
35528
  },
35464
35529
  });
35465
- if (mk.text !== undefined) {
35530
+ if (hasText && bakeZoom !== 0) {
35466
35531
  var text = str(mk.text, '');
35467
35532
  addMarker({
35468
35533
  id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
@@ -35472,10 +35537,10 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35472
35537
  },
35473
35538
  });
35474
35539
  }
35475
- if (mk.badge !== undefined) {
35476
- var badge = ['to-number', valueExpr(mk.badge, 0), 0];
35540
+ if (hasBadge && bakeZoom !== 0) {
35541
+ var badge = badgeNumber;
35477
35542
  addMarker({
35478
- id: src + '-badges', type: 'symbol', filter: both(['>', badge, 1]), minzoom: mk.badgeMinzoom, paint: markerPaint,
35543
+ id: src + '-badges', type: 'symbol', filter: both(['>', badge, 1]), minzoom: mk.badgeMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
35479
35544
  layout: {
35480
35545
  'icon-image': drawnImageId('badge', [['to-string', badge]]),
35481
35546
  'icon-offset': g.badge, 'icon-size': scale,
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.2.0. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
2
+ /*! sMap Web SDK 1.2.2. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
3
3
 
4
4
  var smapCore = null;
5
5
  function smapCreateEngine() {
@@ -28460,7 +28460,7 @@ function smapCreateCanvas() {
28460
28460
  var exports = module.exports;
28461
28461
  var define;
28462
28462
  var self = {};
28463
- // sMap canvas renderer (M61): the map drawn on a 2D canvas for browsers without WebGL 2. It answers the part of the
28463
+ // sMap canvas renderer (M67): the map drawn on a 2D canvas for browsers without WebGL 2. It answers the part of the
28464
28464
  // rendering engine's map interface the Web SDK uses — camera, events, sources, layers of the kinds the SDK makes
28465
28465
  // (raster, fill, line, circle, symbol, heatmap), hit tests, controls and popups — so the SDK's calls work unchanged.
28466
28466
  // The street map comes as raster tiles. What needs WebGL (3D buildings, terrain, tilting, turning, custom 3D layers,
@@ -30756,6 +30756,9 @@ function smapCreatePlan() {
30756
30756
  var body = {};
30757
30757
  if (this._context.region) { body.region = this._context.region; }
30758
30758
  if (this._context.profile) { body.profile = this._context.profile; }
30759
+ // The plan's points, so the server can measure their legs into the snapshot ahead of the edits (E24).
30760
+ var points = this._points();
30761
+ if (points.length) { body.points = points; }
30759
30762
  this._openingKey = key;
30760
30763
  var opening = this._transport.send('POST', PATHS.matrix, body).then(function (res) {
30761
30764
  var id = res.data && res.data.matrix;
@@ -30780,6 +30783,27 @@ function smapCreatePlan() {
30780
30783
  return opening;
30781
30784
  };
30782
30785
 
30786
+ // The plan's points as a snapshot opening sends them: every job location, then every vehicle's start and end, exactly
30787
+ // as the plan holds them (the numbers the plan calls send, since legs are matched exactly), each sent once, first seen
30788
+ // first.
30789
+ PlanEditor.prototype._points = function () {
30790
+ var entry = this._history[this._at];
30791
+ var out = [];
30792
+ var seen = {};
30793
+ function add(v) {
30794
+ var ll = lngLat(v);
30795
+ if (!ll || !isFinite(ll[0]) || !isFinite(ll[1])) { return; }
30796
+ var p = { lat: ll[1], lng: ll[0] };
30797
+ var k = p.lat + ',' + p.lng;
30798
+ if (seen[k]) { return; }
30799
+ seen[k] = true;
30800
+ out.push(p);
30801
+ }
30802
+ ((entry && entry.jobs) || []).forEach(function (j) { add(j && j.location); });
30803
+ (this._vehicles || []).forEach(function (v) { if (v) { add(v.start); add(v.end); } });
30804
+ return out;
30805
+ };
30806
+
30783
30807
  // ------------------------------------------------------------------ texts (14 languages)
30784
30808
 
30785
30809
  var TEXT_KEYS = ['editor', 'busy', 'undo', 'redo', 'refine', 'total', 'distance', 'drivingTime', 'stops', 'violations',
@@ -31589,7 +31613,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31589
31613
  })(typeof self !== 'undefined' ? self : this, function (engine, interop, canvasLib, plan) {
31590
31614
  'use strict';
31591
31615
 
31592
- var VERSION = '1.2.0';
31616
+ var VERSION = '1.2.2';
31593
31617
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
31594
31618
  var BASEMAP_SOURCE = 'basemap';
31595
31619
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -31814,28 +31838,51 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31814
31838
  },
31815
31839
  };
31816
31840
  },
31817
- // pinnum|shape|fill|border|borderWidth|size|textColor|text: a pin with its number drawn in, so a pin above covers a
31818
- // pin below completely (used close up, where few pins are in view; further out the number is its own layer).
31841
+ // pinnum|shape|fill|border|borderWidth|size|textColor|badge|text: a pin with its number and its count badge drawn
31842
+ // in, so a pin above covers a pin below completely, number and badge included, as one element of the route map's
31843
+ // pins does. An empty number or badge is left out; the text comes last because it may hold '|'.
31819
31844
  pinnum: function (args, measure) {
31820
31845
  var pin = IMAGE_KINDS.pin(args.slice(0, 5));
31821
- var size = Number(args[4]), color = args[5], text = args.slice(6).join('|');
31846
+ var size = Number(args[4]), color = args[5], badgeText = args[6] || '', text = args.slice(7).join('|');
31822
31847
  var px = 14 * size / 32, font = 'bold ' + px + 'px monospace';
31848
+ var badge = badgeText !== '' ? IMAGE_KINDS.badge([badgeText], measure) : null;
31849
+ var g = markerGeometry(args[0], size);
31850
+ // The badge's centre is where its own layer puts it: g.badge from the tip, i.e. from the head's centre for a
31851
+ // pin (the image's centre), so the image is widened on both sides when the badge reaches past it.
31852
+ var w = pin.width, h = pin.height;
31853
+ if (badge) {
31854
+ var reach = Math.abs(g.badge[0]) + badge.width / 2;
31855
+ w = Math.max(w, Math.ceil(2 * reach));
31856
+ }
31823
31857
  return {
31824
- width: pin.width, height: pin.height,
31858
+ width: w, height: h,
31825
31859
  draw: function (ctx, ratio) {
31826
- pin.draw(ctx, ratio);
31827
- if (text === '') { return; }
31828
31860
  ctx.save();
31829
- ctx.font = font;
31830
- ctx.textAlign = 'center';
31831
- ctx.textBaseline = 'middle';
31832
- ctx.shadowColor = 'rgba(0,0,0,0.5)';
31833
- ctx.shadowOffsetX = ratio;
31834
- ctx.shadowOffsetY = ratio;
31835
- ctx.shadowBlur = 2 * ratio;
31836
- ctx.fillStyle = color;
31837
- ctx.fillText(text, pin.width / 2, pin.height / 2 + 0.5);
31861
+ ctx.translate((w - pin.width) / 2, 0);
31862
+ pin.draw(ctx, ratio);
31838
31863
  ctx.restore();
31864
+ if (text !== '') {
31865
+ ctx.save();
31866
+ ctx.font = font;
31867
+ ctx.textAlign = 'center';
31868
+ ctx.textBaseline = 'middle';
31869
+ ctx.shadowColor = 'rgba(0,0,0,0.5)';
31870
+ ctx.shadowOffsetX = ratio;
31871
+ ctx.shadowOffsetY = ratio;
31872
+ ctx.shadowBlur = 2 * ratio;
31873
+ ctx.fillStyle = color;
31874
+ ctx.fillText(text, w / 2, h / 2 + 0.5);
31875
+ ctx.restore();
31876
+ }
31877
+ if (badge) {
31878
+ // A pin's head is the image's centre and its tip half the image height lower; the badge sits g.badge from
31879
+ // the tip (a circle's centre is the image's centre).
31880
+ var tipY = args[0] === 'pin' ? h / 2 + g.tipToHead : h / 2;
31881
+ ctx.save();
31882
+ ctx.translate(w / 2 + g.badge[0] - badge.width / 2, tipY + g.badge[1] - badge.height / 2);
31883
+ badge.draw(ctx, ratio);
31884
+ ctx.restore();
31885
+ }
31839
31886
  },
31840
31887
  };
31841
31888
  },
@@ -32001,7 +32048,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
32001
32048
  * minWidth, offset, anchor, closeButton, closeOnClick, focusAfterOpen)
32002
32049
  * @param {string|Node} [options.popupLoading] shown while a popup's content is a pending promise (default: dots)
32003
32050
  * @param {'auto'|'webgl'|'canvas'} [options.renderer] 'auto' (default): the WebGL map, or the canvas map (raster
32004
- * tiles, layers drawn in 2D) where the browser has no WebGL 2 (M61)
32051
+ * tiles, layers drawn in 2D) where the browser has no WebGL 2 (M67)
32005
32052
  * Every other option (center, zoom, pitch, bearing, minZoom, maxZoom, maxBounds, hash, …) goes to the engine.
32006
32053
  */
32007
32054
  function SMap(options) {
@@ -32057,7 +32104,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
32057
32104
  this._toolHandlers = [];
32058
32105
 
32059
32106
  var self = this;
32060
- // The renderer (M61): the WebGL engine, or the canvas map where there is no WebGL 2 (or when asked for).
32107
+ // The renderer (M67): the WebGL engine, or the canvas map where there is no WebGL 2 (or when asked for).
32061
32108
  var want = options.renderer || 'auto';
32062
32109
  // Asked of the page itself; without a page (a server, a test) the WebGL engine is kept.
32063
32110
  var hasPage = typeof document !== 'undefined' && !!document && typeof document.createElement === 'function';
@@ -34404,7 +34451,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34404
34451
  */
34405
34452
  SMap.prototype.setBaseImagery = function (imagery) {
34406
34453
  if (imagery && (!Array.isArray(imagery.tiles) || !imagery.tiles.length)) { throw new Error('sMap: base imagery needs tiles'); }
34407
- // Google's and Bing's tiles only through their own terms: Google's with setGoogleBase (M31, M66).
34454
+ // Google's and Bing's tiles only through their own terms: Google's with setGoogleBase (M31, M72).
34408
34455
  if (imagery) { interop.refuseForbidden(imagery.tiles); }
34409
34456
  return this._setBaseImagery(imagery);
34410
34457
  };
@@ -35445,13 +35492,31 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35445
35492
  // Opacity of the pin, its number and its badge together (the route map draws settled stops at .72).
35446
35493
  var markerPaint = mk.opacity !== undefined ? { 'icon-opacity': valueExpr(mk.opacity, 1) } : undefined;
35447
35494
  main = src + '-points';
35448
- // From bakeZoom on (default 13) a pin's number is drawn into its own image, so an upper pin covers a lower
35449
- // one completely. Further out, where thousands of pins can be in view, the numbers stay one layer above the
35450
- // pins (an image per colour pair instead of per pin and number). bakeZoom: false keeps them separate.
35495
+ // A pin's number and count badge are drawn into its own image (as one element of the route map's pins), so an
35496
+ // upper pin covers a lower one completely, number and badge included. bakeZoom (a zoom) keeps them their own
35497
+ // layers above the pins further out than it — fewer images where thousands of pins are in view, but numbers
35498
+ // over every pin there — and bakeZoom: false keeps them apart at every zoom. textMinzoom / badgeMinzoom start
35499
+ // them at their zoom either way.
35451
35500
  var plainPin = drawnImageId('pin', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size)]);
35452
- var bakeZoom = mk.text !== undefined && mk.bakeZoom !== false ? (mk.bakeZoom !== undefined ? mk.bakeZoom : 13) : null;
35453
- var pinImage = bakeZoom === null ? plainPin : ['step', ['zoom'], plainPin, bakeZoom,
35454
- drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'), str(mk.text, '')])];
35501
+ var hasText = mk.text !== undefined, hasBadge = mk.badge !== undefined;
35502
+ var bakeZoom = (hasText || hasBadge) && mk.bakeZoom !== false ? (typeof mk.bakeZoom === 'number' ? mk.bakeZoom : 0) : null;
35503
+ var badgeNumber = ['to-number', valueExpr(mk.badge, 0), 0];
35504
+ var textMin = typeof mk.textMinzoom === 'number' ? mk.textMinzoom : 0;
35505
+ var badgeMin = typeof mk.badgeMinzoom === 'number' ? mk.badgeMinzoom : 0;
35506
+ var imageAt = function (z) {
35507
+ if (bakeZoom === null || z < bakeZoom) { return plainPin; }
35508
+ var withText = hasText && z >= textMin, withBadge = hasBadge && z >= badgeMin;
35509
+ if (!withText && !withBadge) { return plainPin; }
35510
+ return drawnImageId('pinnum', [shape, str(mk.fill, '#ffffff'), str(mk.border, color), String(bw), String(size), str(mk.textColor, '#ffffff'),
35511
+ withBadge ? ['case', ['>', badgeNumber, 1], ['to-string', badgeNumber], ''] : '', withText ? str(mk.text, '') : '']);
35512
+ };
35513
+ var breaks = [bakeZoom, textMin, badgeMin].filter(function (z, i, all) { return typeof z === 'number' && z > 0 && all.indexOf(z) === i; })
35514
+ .sort(function (x, y) { return x - y; });
35515
+ var pinImage = imageAt(0);
35516
+ if (breaks.length) {
35517
+ pinImage = ['step', ['zoom'], imageAt(0)];
35518
+ breaks.forEach(function (z) { pinImage.push(z, imageAt(z)); });
35519
+ }
35455
35520
  var markerSpecs = [];
35456
35521
  var addMarker = function (layer) { markerSpecs.push(JSON.parse(JSON.stringify(layer))); add(layer); };
35457
35522
  addMarker({
@@ -35464,7 +35529,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35464
35529
  'icon-allow-overlap': true, 'icon-ignore-placement': true,
35465
35530
  },
35466
35531
  });
35467
- if (mk.text !== undefined) {
35532
+ if (hasText && bakeZoom !== 0) {
35468
35533
  var text = str(mk.text, '');
35469
35534
  addMarker({
35470
35535
  id: src + '-numbers', type: 'symbol', filter: both(['!=', text, '']), minzoom: mk.textMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
@@ -35474,10 +35539,10 @@ function smapCreateSdk(engine, interop, canvas, plan) {
35474
35539
  },
35475
35540
  });
35476
35541
  }
35477
- if (mk.badge !== undefined) {
35478
- var badge = ['to-number', valueExpr(mk.badge, 0), 0];
35542
+ if (hasBadge && bakeZoom !== 0) {
35543
+ var badge = badgeNumber;
35479
35544
  addMarker({
35480
- id: src + '-badges', type: 'symbol', filter: both(['>', badge, 1]), minzoom: mk.badgeMinzoom, paint: markerPaint,
35545
+ id: src + '-badges', type: 'symbol', filter: both(['>', badge, 1]), minzoom: mk.badgeMinzoom, maxzoom: bakeZoom === null ? undefined : bakeZoom, paint: markerPaint,
35481
35546
  layout: {
35482
35547
  'icon-image': drawnImageId('badge', [['to-string', badge]]),
35483
35548
  'icon-offset': g.badge, 'icon-size': scale,
@@ -35731,7 +35796,7 @@ function SMapMap(options) {
35731
35796
  SMapMap.prototype = core.Map.prototype;
35732
35797
  return new core.Map(options);
35733
35798
  }
35734
- var smapVersion = "1.2.0";
35799
+ var smapVersion = "1.2.2";
35735
35800
  var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
35736
35801
  /** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
35737
35802
  function smapGetEngine() { return smapLoad().engine; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superrouteca/smap",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
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
@@ -257,7 +257,7 @@ export interface MarkerOptions {
257
257
  scale?: number | unknown[];
258
258
  /** The pin under the pointer comes to the front (default true). */
259
259
  hoverRaise?: boolean;
260
- /** 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. */
260
+ /** Numbers and badges are drawn into each pin, so an upper pin covers a lower one with them. A zoom keeps them their own layers above the pins further out (fewer images where thousands of pins are in view); false keeps them apart at every zoom. */
261
261
  bakeZoom?: number | false;
262
262
  /**
263
263
  * Pins piled up under a click (default on): with `threshold` (4) or more within `radius` (12 px) the map zooms in by
package/types/smap.d.mts CHANGED
@@ -257,7 +257,7 @@ export interface MarkerOptions {
257
257
  scale?: number | unknown[];
258
258
  /** The pin under the pointer comes to the front (default true). */
259
259
  hoverRaise?: boolean;
260
- /** 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. */
260
+ /** Numbers and badges are drawn into each pin, so an upper pin covers a lower one with them. A zoom keeps them their own layers above the pins further out (fewer images where thousands of pins are in view); false keeps them apart at every zoom. */
261
261
  bakeZoom?: number | false;
262
262
  /**
263
263
  * Pins piled up under a click (default on): with `threshold` (4) or more within `radius` (12 px) the map zooms in by