@superrouteca/smap 1.2.2 → 1.2.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,17 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.2.6 — 2026-10-29
4
+
5
+ - `selectArea`: every layer named in `layers` is in the answer's `features`, an empty list when the shape caught
6
+ nothing of it (before, such a layer was missing, so `features.stops.map(…)` failed on an empty selection).
7
+
8
+ ## 1.2.5 — 2026-10-29
9
+
10
+ - Canvas map (`renderer: 'canvas'`, and browsers without WebGL 2): pans smoothly on computers whose graphics are
11
+ emulated in software. The map and its pin images are drawn by the processor, and while the map only moves the
12
+ drawn picture is shifted into place (a margin is drawn past each edge, `panMargin`) and drawn again when the move
13
+ ends. Measured on the dispatcher under software emulation: a full redraw from about 715 ms to 35 ms per frame.
14
+
3
15
  ## 1.2.2 — 2026-10-29
4
16
 
5
17
  - Plan editor: opening a distance snapshot sends the plan's points (every job location and vehicle start and end), so
package/dist/SOURCE.json CHANGED
@@ -1,15 +1,15 @@
1
1
  {
2
- "sdkVersion": "1.2.2",
2
+ "sdkVersion": "1.2.6",
3
3
  "engine": "maplibre-gl 6.16.0",
4
4
  "files": {
5
- "smap.js": "b5c9dae63b6999dbc28ec9ae76a828e1cfd061a6c2acb157f631e75786c26367",
5
+ "smap.js": "12037a7d4d0381d904a52b9f90286e55909dc01ea33184f0ab0774a940b2c8fe",
6
6
  "smap.css": "9bc4b14821de97da199e6af4f18541eeaf53a2ae91e5831e7224f57fe6b77660",
7
- "smap-client.js": "169f1b6efc7241404be51e21e40238b20098b471f7d560809a7a4cfde5116f5a",
7
+ "smap-client.js": "97b38c8725251b1552e89daf59144af0b89160574ac935424bedbe387fde3179",
8
8
  "smap-client.d.ts": "5f0df11bf60c5a9ced34522a284de81260c27871678ab772f16da9ccf6883f58",
9
- "smap.mjs": "9f2e3dc715d33b647475d6082a39843a5f5acf683d45f2a8373c0e5a361c4092",
10
- "smap.cjs": "6e51c2b7df527b09a77cc551d4d7c8c06a7f884333d920c7c2009029cea41890",
11
- "smap-client.cjs": "0328f85f3313d55b3a120b123dea5a1f319fceaa42b3321ff9122c6b15ed31bf",
12
- "smap-client.mjs": "b649b0ff23677164ccca36122ddc244689d6dfbd8cb69604ce245304c13b06cd",
9
+ "smap.mjs": "e96a8d122b69f9b817b76fe4d9756790bec8d1ff60269ff9ba045662ce2945f2",
10
+ "smap.cjs": "fed668c3735beef940cedbd94ee285100ed0b9d340353a91e9e3059b9cc93e19",
11
+ "smap-client.cjs": "ed2b961cf1cdaf70f74011e9281146986ba898e3c3fde2ae776b441587107d2f",
12
+ "smap-client.mjs": "969e36da07758913d7d95e40e75f569143d20c46eda33edd18a8c44069756580",
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.2 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
2
+ /*! sMap JS client 1.2.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.2.2';
22
+ var VERSION = '1.2.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.2.2 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
1
+ /*! sMap JS client 1.2.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.2.2';
21
+ var VERSION = '1.2.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.2.2 — tokens, the nearest node, styles, tiles and raw reads, without a map view. */
4
+ /*! sMap JS client 1.2.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.2.2';
24
+ var VERSION = '1.2.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.2.2. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
3
+ /*! sMap Web SDK 1.2.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() {
@@ -28756,6 +28756,12 @@ function smapCreateCanvas() {
28756
28756
  this._maxZoom = options.maxZoom !== undefined ? options.maxZoom : 22;
28757
28757
  this._drawn = [];
28758
28758
  this._dirty = false;
28759
+ // Pixels drawn past each edge of the view. While the map only moves, the drawn picture is shifted into place
28760
+ // instead of being drawn again, and drawn again once the move ends or the margin runs out: redrawing a whole
28761
+ // canvas on every frame is what made panning stall on computers without graphics acceleration.
28762
+ this._panMargin = options.panMargin;
28763
+ this._at = null;
28764
+ this._redraw = false;
28759
28765
  container.classList.add('maplibregl-map', 'smap-canvas-map');
28760
28766
  var canvasContainer = this._doc.createElement('div');
28761
28767
  canvasContainer.className = 'maplibregl-canvas-container maplibregl-interactive';
@@ -28857,7 +28863,7 @@ function smapCreateCanvas() {
28857
28863
  this._fire('move', {});
28858
28864
  if (zoomed) { this._fire('zoom', {}); }
28859
28865
  };
28860
- CanvasMap.prototype._end = function (zoomed) { this._fire('moveend', {}); if (zoomed) { this._fire('zoomend', {}); } this._fire('idle', {}); };
28866
+ CanvasMap.prototype._end = function (zoomed) { if (this._shifted()) { this._changed(); } this._fire('moveend', {}); if (zoomed) { this._fire('zoomend', {}); } this._fire('idle', {}); };
28861
28867
  CanvasMap.prototype.jumpTo = function (o) {
28862
28868
  var z0 = this._zoom;
28863
28869
  this._set(o.center, o.zoom);
@@ -28931,16 +28937,43 @@ function smapCreateCanvas() {
28931
28937
  var win = this._doc.defaultView || {}, ratio = Math.min(3, win.devicePixelRatio || 1);
28932
28938
  if (w === this._w && h === this._h && ratio === this._ratio) { return false; }
28933
28939
  this._w = w; this._h = h; this._ratio = ratio;
28934
- this._canvas.width = Math.round(w * ratio); this._canvas.height = Math.round(h * ratio);
28935
- this._canvas.style.width = w + 'px'; this._canvas.style.height = h + 'px';
28940
+ var m = this._panMargin;
28941
+ this._pad = m === undefined || m === null ? Math.min(400, Math.round(Math.max(w, h) / 3)) : Math.max(0, Math.round(m));
28942
+ var pw = w + 2 * this._pad, ph = h + 2 * this._pad;
28943
+ this._canvas.width = Math.round(pw * ratio); this._canvas.height = Math.round(ph * ratio);
28944
+ this._canvas.style.width = pw + 'px'; this._canvas.style.height = ph + 'px';
28945
+ this._canvas.style.willChange = 'transform';
28946
+ this._at = null;
28947
+ this._place(0, 0);
28936
28948
  return true;
28937
28949
  };
28950
+ // Where the view's centre is now, in world pixels at the drawn zoom, against where the picture was drawn.
28951
+ CanvasMap.prototype._offset = function () {
28952
+ if (!this._at || this._at.zoom !== this._zoom) { return null; }
28953
+ var s = this._scale();
28954
+ var x = lngX(this._center.lng, s) - this._at.x, y = latY(this._center.lat, s) - this._at.y;
28955
+ // Across the antimeridian the centre's longitude wraps; take the nearer copy of the world.
28956
+ if (x > s / 2) { x -= s; } else if (x < -s / 2) { x += s; }
28957
+ return { x: x, y: y };
28958
+ };
28959
+ CanvasMap.prototype._shifted = function () { var o = this._offset(); return !!o && (o.x !== 0 || o.y !== 0); };
28960
+ CanvasMap.prototype._place = function (dx, dy) {
28961
+ var p = this._pad || 0;
28962
+ this._canvas.style.transform = 'translate(' + (-p - dx) + 'px,' + (-p - dy) + 'px)';
28963
+ };
28938
28964
  CanvasMap.prototype.triggerRepaint = function () { this._changed(); };
28939
- CanvasMap.prototype._changed = function () {
28965
+ CanvasMap.prototype._changed = function (moved) {
28966
+ if (!moved) { this._redraw = true; }
28940
28967
  if (this._dirty || this._removed) { return; }
28941
28968
  this._dirty = true;
28942
28969
  var self = this;
28943
- raf(function () { self._dirty = false; self._render(); });
28970
+ raf(function () {
28971
+ self._dirty = false;
28972
+ var o = self._redraw ? null : self._offset();
28973
+ if (o && Math.abs(o.x) <= self._pad && Math.abs(o.y) <= self._pad) { self._place(o.x, o.y); self._fire('render', {}); return; }
28974
+ self._redraw = false;
28975
+ self._render();
28976
+ });
28944
28977
  };
28945
28978
  CanvasMap.prototype.loaded = function () { return true; };
28946
28979
  CanvasMap.prototype.isStyleLoaded = function () { return true; };
@@ -29017,7 +29050,9 @@ function smapCreateCanvas() {
29017
29050
  if (image && image.data && image.width !== undefined && !(image.getContext)) {
29018
29051
  canvas = this._doc.createElement('canvas');
29019
29052
  canvas.width = image.width; canvas.height = image.height;
29020
- var ctx = canvas.getContext('2d');
29053
+ // Kept by the processor like the map's own canvas: an image held by the graphics process is read back each
29054
+ // time it is drawn, which under software emulation cost most of a second per frame with a hundred pins.
29055
+ var ctx = canvas.getContext('2d', { willReadFrequently: true });
29021
29056
  var data = ctx.createImageData(image.width, image.height);
29022
29057
  data.data.set(image.data);
29023
29058
  ctx.putImageData(data, 0, 0);
@@ -29075,10 +29110,22 @@ function smapCreateCanvas() {
29075
29110
  };
29076
29111
 
29077
29112
  CanvasMap.prototype._render = function () {
29078
- var ctx = this._canvas.getContext('2d');
29113
+ // A canvas the processor draws: this map is for computers whose graphics are emulated in software, where the
29114
+ // browser handing a canvas to the graphics process to draw took most of a second per frame (measured: 0.6 s
29115
+ // under software emulation against 2 ms drawn by the processor).
29116
+ var ctx = this._canvas.getContext('2d', { willReadFrequently: true });
29079
29117
  if (!ctx) { return; }
29080
- var r = this._ratio;
29118
+ var r = this._ratio, pad = this._pad || 0, w = this._w, h = this._h;
29081
29119
  ctx.setTransform(r, 0, 0, r, 0, 0);
29120
+ // The picture covers the view and its margin: drawn as a view that much larger around the same centre.
29121
+ this._w = w + 2 * pad; this._h = h + 2 * pad;
29122
+ try { this._paint(ctx); } finally { this._w = w; this._h = h; }
29123
+ var s = this._scale();
29124
+ this._at = { zoom: this._zoom, x: lngX(this._center.lng, s), y: latY(this._center.lat, s) };
29125
+ this._place(0, 0);
29126
+ this._fire('render', {});
29127
+ };
29128
+ CanvasMap.prototype._paint = function (ctx) {
29082
29129
  ctx.clearRect(0, 0, this._w, this._h);
29083
29130
  this._drawn = [];
29084
29131
  var self = this, zoom = this._zoom;
@@ -29102,7 +29149,6 @@ function smapCreateCanvas() {
29102
29149
  else if (l.type === 'symbol') { self._drawSymbol(ctx, l, f, c); }
29103
29150
  }
29104
29151
  });
29105
- this._fire('render', {});
29106
29152
  };
29107
29153
 
29108
29154
  CanvasMap.prototype._drawRaster = function (ctx, l) {
@@ -29386,6 +29432,9 @@ function smapCreateCanvas() {
29386
29432
  if (!at) { x0 = 0; y0 = 0; x1 = this._w; y1 = this._h; }
29387
29433
  else if (Array.isArray(at) && Array.isArray(at[0])) { x0 = Math.min(at[0][0], at[1][0]); x1 = Math.max(at[0][0], at[1][0]); y0 = Math.min(at[0][1], at[1][1]); y1 = Math.max(at[0][1], at[1][1]); }
29388
29434
  else { var p = Array.isArray(at) ? { x: at[0], y: at[1] } : at; x0 = x1 = p.x; y0 = y1 = p.y; }
29435
+ // From view points to the drawn picture's: it starts a margin before the view and may have been shifted since.
29436
+ var shift = this._offset() || { x: 0, y: 0 }, pad = this._pad || 0;
29437
+ x0 += pad + shift.x; x1 += pad + shift.x; y0 += pad + shift.y; y1 += pad + shift.y;
29389
29438
  var out = [], seen = [];
29390
29439
  for (var i = this._drawn.length - 1; i >= 0; i--) {
29391
29440
  var d = this._drawn[i];
@@ -31614,7 +31663,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31614
31663
  })(typeof self !== 'undefined' ? self : this, function (engine, interop, canvasLib, plan) {
31615
31664
  'use strict';
31616
31665
 
31617
- var VERSION = '1.2.2';
31666
+ var VERSION = '1.2.6';
31618
31667
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
31619
31668
  var BASEMAP_SOURCE = 'basemap';
31620
31669
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -34322,7 +34371,9 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34322
34371
 
34323
34372
  /**
34324
34373
  * Selects features by drawing on the map: 'box' (press and drag) or 'lasso' (press and draw a free shape). Resolves
34325
- * with { features: { layerId: [features] }, geometry } once the shape is drawn, or null when Escape cancels it.
34374
+ * with { features: { layerId: [features] }, geometry } once the shape is drawn, or null when Escape cancels it. Every
34375
+ * layer named in `layers` is in `features` (an empty list when nothing of it was caught); without `layers`, only the
34376
+ * layers something was caught in.
34326
34377
  * A point (or custom marker) counts when it is inside; a line or area when any of its vertices is.
34327
34378
  * @param {Object} [o] mode ('box' default, 'lasso'), layers (default every point, custom marker, line and area layer)
34328
34379
  */
@@ -34404,7 +34455,8 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34404
34455
  (function walk(c) { if (found || !c) { return; } if (typeof c[0] === 'number') { found = inside(c); } else { c.forEach(walk); } })(f.geometry && f.geometry.coordinates);
34405
34456
  return found;
34406
34457
  });
34407
- if (hits.length) { out[id] = hits; }
34458
+ // A layer the caller named is always answered, empty when the shape caught none of it.
34459
+ if (hits.length || layers) { out[id] = hits; }
34408
34460
  });
34409
34461
  return out;
34410
34462
  };
@@ -35797,7 +35849,7 @@ function SMapMap(options) {
35797
35849
  SMapMap.prototype = core.Map.prototype;
35798
35850
  return new core.Map(options);
35799
35851
  }
35800
- var smapVersion = "1.2.2";
35852
+ var smapVersion = "1.2.6";
35801
35853
  var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
35802
35854
  /** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
35803
35855
  function smapGetEngine() { return smapLoad().engine; }
package/dist/smap.js CHANGED
@@ -1,4 +1,4 @@
1
- /*! sMap Web SDK 1.2.2. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
1
+ /*! sMap Web SDK 1.2.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';
@@ -28753,6 +28753,12 @@ function smapCreateCanvas() {
28753
28753
  this._maxZoom = options.maxZoom !== undefined ? options.maxZoom : 22;
28754
28754
  this._drawn = [];
28755
28755
  this._dirty = false;
28756
+ // Pixels drawn past each edge of the view. While the map only moves, the drawn picture is shifted into place
28757
+ // instead of being drawn again, and drawn again once the move ends or the margin runs out: redrawing a whole
28758
+ // canvas on every frame is what made panning stall on computers without graphics acceleration.
28759
+ this._panMargin = options.panMargin;
28760
+ this._at = null;
28761
+ this._redraw = false;
28756
28762
  container.classList.add('maplibregl-map', 'smap-canvas-map');
28757
28763
  var canvasContainer = this._doc.createElement('div');
28758
28764
  canvasContainer.className = 'maplibregl-canvas-container maplibregl-interactive';
@@ -28854,7 +28860,7 @@ function smapCreateCanvas() {
28854
28860
  this._fire('move', {});
28855
28861
  if (zoomed) { this._fire('zoom', {}); }
28856
28862
  };
28857
- CanvasMap.prototype._end = function (zoomed) { this._fire('moveend', {}); if (zoomed) { this._fire('zoomend', {}); } this._fire('idle', {}); };
28863
+ CanvasMap.prototype._end = function (zoomed) { if (this._shifted()) { this._changed(); } this._fire('moveend', {}); if (zoomed) { this._fire('zoomend', {}); } this._fire('idle', {}); };
28858
28864
  CanvasMap.prototype.jumpTo = function (o) {
28859
28865
  var z0 = this._zoom;
28860
28866
  this._set(o.center, o.zoom);
@@ -28928,16 +28934,43 @@ function smapCreateCanvas() {
28928
28934
  var win = this._doc.defaultView || {}, ratio = Math.min(3, win.devicePixelRatio || 1);
28929
28935
  if (w === this._w && h === this._h && ratio === this._ratio) { return false; }
28930
28936
  this._w = w; this._h = h; this._ratio = ratio;
28931
- this._canvas.width = Math.round(w * ratio); this._canvas.height = Math.round(h * ratio);
28932
- this._canvas.style.width = w + 'px'; this._canvas.style.height = h + 'px';
28937
+ var m = this._panMargin;
28938
+ this._pad = m === undefined || m === null ? Math.min(400, Math.round(Math.max(w, h) / 3)) : Math.max(0, Math.round(m));
28939
+ var pw = w + 2 * this._pad, ph = h + 2 * this._pad;
28940
+ this._canvas.width = Math.round(pw * ratio); this._canvas.height = Math.round(ph * ratio);
28941
+ this._canvas.style.width = pw + 'px'; this._canvas.style.height = ph + 'px';
28942
+ this._canvas.style.willChange = 'transform';
28943
+ this._at = null;
28944
+ this._place(0, 0);
28933
28945
  return true;
28934
28946
  };
28947
+ // Where the view's centre is now, in world pixels at the drawn zoom, against where the picture was drawn.
28948
+ CanvasMap.prototype._offset = function () {
28949
+ if (!this._at || this._at.zoom !== this._zoom) { return null; }
28950
+ var s = this._scale();
28951
+ var x = lngX(this._center.lng, s) - this._at.x, y = latY(this._center.lat, s) - this._at.y;
28952
+ // Across the antimeridian the centre's longitude wraps; take the nearer copy of the world.
28953
+ if (x > s / 2) { x -= s; } else if (x < -s / 2) { x += s; }
28954
+ return { x: x, y: y };
28955
+ };
28956
+ CanvasMap.prototype._shifted = function () { var o = this._offset(); return !!o && (o.x !== 0 || o.y !== 0); };
28957
+ CanvasMap.prototype._place = function (dx, dy) {
28958
+ var p = this._pad || 0;
28959
+ this._canvas.style.transform = 'translate(' + (-p - dx) + 'px,' + (-p - dy) + 'px)';
28960
+ };
28935
28961
  CanvasMap.prototype.triggerRepaint = function () { this._changed(); };
28936
- CanvasMap.prototype._changed = function () {
28962
+ CanvasMap.prototype._changed = function (moved) {
28963
+ if (!moved) { this._redraw = true; }
28937
28964
  if (this._dirty || this._removed) { return; }
28938
28965
  this._dirty = true;
28939
28966
  var self = this;
28940
- raf(function () { self._dirty = false; self._render(); });
28967
+ raf(function () {
28968
+ self._dirty = false;
28969
+ var o = self._redraw ? null : self._offset();
28970
+ if (o && Math.abs(o.x) <= self._pad && Math.abs(o.y) <= self._pad) { self._place(o.x, o.y); self._fire('render', {}); return; }
28971
+ self._redraw = false;
28972
+ self._render();
28973
+ });
28941
28974
  };
28942
28975
  CanvasMap.prototype.loaded = function () { return true; };
28943
28976
  CanvasMap.prototype.isStyleLoaded = function () { return true; };
@@ -29014,7 +29047,9 @@ function smapCreateCanvas() {
29014
29047
  if (image && image.data && image.width !== undefined && !(image.getContext)) {
29015
29048
  canvas = this._doc.createElement('canvas');
29016
29049
  canvas.width = image.width; canvas.height = image.height;
29017
- var ctx = canvas.getContext('2d');
29050
+ // Kept by the processor like the map's own canvas: an image held by the graphics process is read back each
29051
+ // time it is drawn, which under software emulation cost most of a second per frame with a hundred pins.
29052
+ var ctx = canvas.getContext('2d', { willReadFrequently: true });
29018
29053
  var data = ctx.createImageData(image.width, image.height);
29019
29054
  data.data.set(image.data);
29020
29055
  ctx.putImageData(data, 0, 0);
@@ -29072,10 +29107,22 @@ function smapCreateCanvas() {
29072
29107
  };
29073
29108
 
29074
29109
  CanvasMap.prototype._render = function () {
29075
- var ctx = this._canvas.getContext('2d');
29110
+ // A canvas the processor draws: this map is for computers whose graphics are emulated in software, where the
29111
+ // browser handing a canvas to the graphics process to draw took most of a second per frame (measured: 0.6 s
29112
+ // under software emulation against 2 ms drawn by the processor).
29113
+ var ctx = this._canvas.getContext('2d', { willReadFrequently: true });
29076
29114
  if (!ctx) { return; }
29077
- var r = this._ratio;
29115
+ var r = this._ratio, pad = this._pad || 0, w = this._w, h = this._h;
29078
29116
  ctx.setTransform(r, 0, 0, r, 0, 0);
29117
+ // The picture covers the view and its margin: drawn as a view that much larger around the same centre.
29118
+ this._w = w + 2 * pad; this._h = h + 2 * pad;
29119
+ try { this._paint(ctx); } finally { this._w = w; this._h = h; }
29120
+ var s = this._scale();
29121
+ this._at = { zoom: this._zoom, x: lngX(this._center.lng, s), y: latY(this._center.lat, s) };
29122
+ this._place(0, 0);
29123
+ this._fire('render', {});
29124
+ };
29125
+ CanvasMap.prototype._paint = function (ctx) {
29079
29126
  ctx.clearRect(0, 0, this._w, this._h);
29080
29127
  this._drawn = [];
29081
29128
  var self = this, zoom = this._zoom;
@@ -29099,7 +29146,6 @@ function smapCreateCanvas() {
29099
29146
  else if (l.type === 'symbol') { self._drawSymbol(ctx, l, f, c); }
29100
29147
  }
29101
29148
  });
29102
- this._fire('render', {});
29103
29149
  };
29104
29150
 
29105
29151
  CanvasMap.prototype._drawRaster = function (ctx, l) {
@@ -29383,6 +29429,9 @@ function smapCreateCanvas() {
29383
29429
  if (!at) { x0 = 0; y0 = 0; x1 = this._w; y1 = this._h; }
29384
29430
  else if (Array.isArray(at) && Array.isArray(at[0])) { x0 = Math.min(at[0][0], at[1][0]); x1 = Math.max(at[0][0], at[1][0]); y0 = Math.min(at[0][1], at[1][1]); y1 = Math.max(at[0][1], at[1][1]); }
29385
29431
  else { var p = Array.isArray(at) ? { x: at[0], y: at[1] } : at; x0 = x1 = p.x; y0 = y1 = p.y; }
29432
+ // From view points to the drawn picture's: it starts a margin before the view and may have been shifted since.
29433
+ var shift = this._offset() || { x: 0, y: 0 }, pad = this._pad || 0;
29434
+ x0 += pad + shift.x; x1 += pad + shift.x; y0 += pad + shift.y; y1 += pad + shift.y;
29386
29435
  var out = [], seen = [];
29387
29436
  for (var i = this._drawn.length - 1; i >= 0; i--) {
29388
29437
  var d = this._drawn[i];
@@ -31611,7 +31660,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31611
31660
  })(typeof self !== 'undefined' ? self : this, function (engine, interop, canvasLib, plan) {
31612
31661
  'use strict';
31613
31662
 
31614
- var VERSION = '1.2.2';
31663
+ var VERSION = '1.2.6';
31615
31664
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
31616
31665
  var BASEMAP_SOURCE = 'basemap';
31617
31666
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -34319,7 +34368,9 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34319
34368
 
34320
34369
  /**
34321
34370
  * Selects features by drawing on the map: 'box' (press and drag) or 'lasso' (press and draw a free shape). Resolves
34322
- * with { features: { layerId: [features] }, geometry } once the shape is drawn, or null when Escape cancels it.
34371
+ * with { features: { layerId: [features] }, geometry } once the shape is drawn, or null when Escape cancels it. Every
34372
+ * layer named in `layers` is in `features` (an empty list when nothing of it was caught); without `layers`, only the
34373
+ * layers something was caught in.
34323
34374
  * A point (or custom marker) counts when it is inside; a line or area when any of its vertices is.
34324
34375
  * @param {Object} [o] mode ('box' default, 'lasso'), layers (default every point, custom marker, line and area layer)
34325
34376
  */
@@ -34401,7 +34452,8 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34401
34452
  (function walk(c) { if (found || !c) { return; } if (typeof c[0] === 'number') { found = inside(c); } else { c.forEach(walk); } })(f.geometry && f.geometry.coordinates);
34402
34453
  return found;
34403
34454
  });
34404
- if (hits.length) { out[id] = hits; }
34455
+ // A layer the caller named is always answered, empty when the shape caught none of it.
34456
+ if (hits.length || layers) { out[id] = hits; }
34405
34457
  });
34406
34458
  return out;
34407
34459
  };
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.2. Bundles maplibre-gl 6.16.0 (BSD-3-Clause), see vendor-licenses.txt. */
2
+ /*! sMap Web SDK 1.2.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() {
@@ -28755,6 +28755,12 @@ function smapCreateCanvas() {
28755
28755
  this._maxZoom = options.maxZoom !== undefined ? options.maxZoom : 22;
28756
28756
  this._drawn = [];
28757
28757
  this._dirty = false;
28758
+ // Pixels drawn past each edge of the view. While the map only moves, the drawn picture is shifted into place
28759
+ // instead of being drawn again, and drawn again once the move ends or the margin runs out: redrawing a whole
28760
+ // canvas on every frame is what made panning stall on computers without graphics acceleration.
28761
+ this._panMargin = options.panMargin;
28762
+ this._at = null;
28763
+ this._redraw = false;
28758
28764
  container.classList.add('maplibregl-map', 'smap-canvas-map');
28759
28765
  var canvasContainer = this._doc.createElement('div');
28760
28766
  canvasContainer.className = 'maplibregl-canvas-container maplibregl-interactive';
@@ -28856,7 +28862,7 @@ function smapCreateCanvas() {
28856
28862
  this._fire('move', {});
28857
28863
  if (zoomed) { this._fire('zoom', {}); }
28858
28864
  };
28859
- CanvasMap.prototype._end = function (zoomed) { this._fire('moveend', {}); if (zoomed) { this._fire('zoomend', {}); } this._fire('idle', {}); };
28865
+ CanvasMap.prototype._end = function (zoomed) { if (this._shifted()) { this._changed(); } this._fire('moveend', {}); if (zoomed) { this._fire('zoomend', {}); } this._fire('idle', {}); };
28860
28866
  CanvasMap.prototype.jumpTo = function (o) {
28861
28867
  var z0 = this._zoom;
28862
28868
  this._set(o.center, o.zoom);
@@ -28930,16 +28936,43 @@ function smapCreateCanvas() {
28930
28936
  var win = this._doc.defaultView || {}, ratio = Math.min(3, win.devicePixelRatio || 1);
28931
28937
  if (w === this._w && h === this._h && ratio === this._ratio) { return false; }
28932
28938
  this._w = w; this._h = h; this._ratio = ratio;
28933
- this._canvas.width = Math.round(w * ratio); this._canvas.height = Math.round(h * ratio);
28934
- this._canvas.style.width = w + 'px'; this._canvas.style.height = h + 'px';
28939
+ var m = this._panMargin;
28940
+ this._pad = m === undefined || m === null ? Math.min(400, Math.round(Math.max(w, h) / 3)) : Math.max(0, Math.round(m));
28941
+ var pw = w + 2 * this._pad, ph = h + 2 * this._pad;
28942
+ this._canvas.width = Math.round(pw * ratio); this._canvas.height = Math.round(ph * ratio);
28943
+ this._canvas.style.width = pw + 'px'; this._canvas.style.height = ph + 'px';
28944
+ this._canvas.style.willChange = 'transform';
28945
+ this._at = null;
28946
+ this._place(0, 0);
28935
28947
  return true;
28936
28948
  };
28949
+ // Where the view's centre is now, in world pixels at the drawn zoom, against where the picture was drawn.
28950
+ CanvasMap.prototype._offset = function () {
28951
+ if (!this._at || this._at.zoom !== this._zoom) { return null; }
28952
+ var s = this._scale();
28953
+ var x = lngX(this._center.lng, s) - this._at.x, y = latY(this._center.lat, s) - this._at.y;
28954
+ // Across the antimeridian the centre's longitude wraps; take the nearer copy of the world.
28955
+ if (x > s / 2) { x -= s; } else if (x < -s / 2) { x += s; }
28956
+ return { x: x, y: y };
28957
+ };
28958
+ CanvasMap.prototype._shifted = function () { var o = this._offset(); return !!o && (o.x !== 0 || o.y !== 0); };
28959
+ CanvasMap.prototype._place = function (dx, dy) {
28960
+ var p = this._pad || 0;
28961
+ this._canvas.style.transform = 'translate(' + (-p - dx) + 'px,' + (-p - dy) + 'px)';
28962
+ };
28937
28963
  CanvasMap.prototype.triggerRepaint = function () { this._changed(); };
28938
- CanvasMap.prototype._changed = function () {
28964
+ CanvasMap.prototype._changed = function (moved) {
28965
+ if (!moved) { this._redraw = true; }
28939
28966
  if (this._dirty || this._removed) { return; }
28940
28967
  this._dirty = true;
28941
28968
  var self = this;
28942
- raf(function () { self._dirty = false; self._render(); });
28969
+ raf(function () {
28970
+ self._dirty = false;
28971
+ var o = self._redraw ? null : self._offset();
28972
+ if (o && Math.abs(o.x) <= self._pad && Math.abs(o.y) <= self._pad) { self._place(o.x, o.y); self._fire('render', {}); return; }
28973
+ self._redraw = false;
28974
+ self._render();
28975
+ });
28943
28976
  };
28944
28977
  CanvasMap.prototype.loaded = function () { return true; };
28945
28978
  CanvasMap.prototype.isStyleLoaded = function () { return true; };
@@ -29016,7 +29049,9 @@ function smapCreateCanvas() {
29016
29049
  if (image && image.data && image.width !== undefined && !(image.getContext)) {
29017
29050
  canvas = this._doc.createElement('canvas');
29018
29051
  canvas.width = image.width; canvas.height = image.height;
29019
- var ctx = canvas.getContext('2d');
29052
+ // Kept by the processor like the map's own canvas: an image held by the graphics process is read back each
29053
+ // time it is drawn, which under software emulation cost most of a second per frame with a hundred pins.
29054
+ var ctx = canvas.getContext('2d', { willReadFrequently: true });
29020
29055
  var data = ctx.createImageData(image.width, image.height);
29021
29056
  data.data.set(image.data);
29022
29057
  ctx.putImageData(data, 0, 0);
@@ -29074,10 +29109,22 @@ function smapCreateCanvas() {
29074
29109
  };
29075
29110
 
29076
29111
  CanvasMap.prototype._render = function () {
29077
- var ctx = this._canvas.getContext('2d');
29112
+ // A canvas the processor draws: this map is for computers whose graphics are emulated in software, where the
29113
+ // browser handing a canvas to the graphics process to draw took most of a second per frame (measured: 0.6 s
29114
+ // under software emulation against 2 ms drawn by the processor).
29115
+ var ctx = this._canvas.getContext('2d', { willReadFrequently: true });
29078
29116
  if (!ctx) { return; }
29079
- var r = this._ratio;
29117
+ var r = this._ratio, pad = this._pad || 0, w = this._w, h = this._h;
29080
29118
  ctx.setTransform(r, 0, 0, r, 0, 0);
29119
+ // The picture covers the view and its margin: drawn as a view that much larger around the same centre.
29120
+ this._w = w + 2 * pad; this._h = h + 2 * pad;
29121
+ try { this._paint(ctx); } finally { this._w = w; this._h = h; }
29122
+ var s = this._scale();
29123
+ this._at = { zoom: this._zoom, x: lngX(this._center.lng, s), y: latY(this._center.lat, s) };
29124
+ this._place(0, 0);
29125
+ this._fire('render', {});
29126
+ };
29127
+ CanvasMap.prototype._paint = function (ctx) {
29081
29128
  ctx.clearRect(0, 0, this._w, this._h);
29082
29129
  this._drawn = [];
29083
29130
  var self = this, zoom = this._zoom;
@@ -29101,7 +29148,6 @@ function smapCreateCanvas() {
29101
29148
  else if (l.type === 'symbol') { self._drawSymbol(ctx, l, f, c); }
29102
29149
  }
29103
29150
  });
29104
- this._fire('render', {});
29105
29151
  };
29106
29152
 
29107
29153
  CanvasMap.prototype._drawRaster = function (ctx, l) {
@@ -29385,6 +29431,9 @@ function smapCreateCanvas() {
29385
29431
  if (!at) { x0 = 0; y0 = 0; x1 = this._w; y1 = this._h; }
29386
29432
  else if (Array.isArray(at) && Array.isArray(at[0])) { x0 = Math.min(at[0][0], at[1][0]); x1 = Math.max(at[0][0], at[1][0]); y0 = Math.min(at[0][1], at[1][1]); y1 = Math.max(at[0][1], at[1][1]); }
29387
29433
  else { var p = Array.isArray(at) ? { x: at[0], y: at[1] } : at; x0 = x1 = p.x; y0 = y1 = p.y; }
29434
+ // From view points to the drawn picture's: it starts a margin before the view and may have been shifted since.
29435
+ var shift = this._offset() || { x: 0, y: 0 }, pad = this._pad || 0;
29436
+ x0 += pad + shift.x; x1 += pad + shift.x; y0 += pad + shift.y; y1 += pad + shift.y;
29388
29437
  var out = [], seen = [];
29389
29438
  for (var i = this._drawn.length - 1; i >= 0; i--) {
29390
29439
  var d = this._drawn[i];
@@ -31613,7 +31662,7 @@ function smapCreateSdk(engine, interop, canvas, plan) {
31613
31662
  })(typeof self !== 'undefined' ? self : this, function (engine, interop, canvasLib, plan) {
31614
31663
  'use strict';
31615
31664
 
31616
- var VERSION = '1.2.2';
31665
+ var VERSION = '1.2.6';
31617
31666
  var FLAVORS = ['light', 'white', 'grayscale', 'dark', 'black'];
31618
31667
  var BASEMAP_SOURCE = 'basemap';
31619
31668
  var BUILDINGS_LAYER = 'smap-buildings-3d';
@@ -34321,7 +34370,9 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34321
34370
 
34322
34371
  /**
34323
34372
  * Selects features by drawing on the map: 'box' (press and drag) or 'lasso' (press and draw a free shape). Resolves
34324
- * with { features: { layerId: [features] }, geometry } once the shape is drawn, or null when Escape cancels it.
34373
+ * with { features: { layerId: [features] }, geometry } once the shape is drawn, or null when Escape cancels it. Every
34374
+ * layer named in `layers` is in `features` (an empty list when nothing of it was caught); without `layers`, only the
34375
+ * layers something was caught in.
34325
34376
  * A point (or custom marker) counts when it is inside; a line or area when any of its vertices is.
34326
34377
  * @param {Object} [o] mode ('box' default, 'lasso'), layers (default every point, custom marker, line and area layer)
34327
34378
  */
@@ -34403,7 +34454,8 @@ function smapCreateSdk(engine, interop, canvas, plan) {
34403
34454
  (function walk(c) { if (found || !c) { return; } if (typeof c[0] === 'number') { found = inside(c); } else { c.forEach(walk); } })(f.geometry && f.geometry.coordinates);
34404
34455
  return found;
34405
34456
  });
34406
- if (hits.length) { out[id] = hits; }
34457
+ // A layer the caller named is always answered, empty when the shape caught none of it.
34458
+ if (hits.length || layers) { out[id] = hits; }
34407
34459
  });
34408
34460
  return out;
34409
34461
  };
@@ -35796,7 +35848,7 @@ function SMapMap(options) {
35796
35848
  SMapMap.prototype = core.Map.prototype;
35797
35849
  return new core.Map(options);
35798
35850
  }
35799
- var smapVersion = "1.2.2";
35851
+ var smapVersion = "1.2.6";
35800
35852
  var smapStyles = ['light', 'white', 'grayscale', 'dark', 'black'];
35801
35853
  /** The bundled rendering engine, for anything the SDK does not wrap (loaded on the first call). */
35802
35854
  function smapGetEngine() { return smapLoad().engine; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@superrouteca/smap",
3
- "version": "1.2.2",
3
+ "version": "1.2.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
@@ -489,6 +489,8 @@ export interface MapOptions {
489
489
  * hillshade or 3D models; the map fires 'smap.unsupported' with { feature } for each of them that is asked for.
490
490
  */
491
491
  renderer?: 'auto' | 'webgl' | 'canvas';
492
+ /** Canvas map only: pixels drawn past each edge, so panning shifts the drawn picture instead of drawing it again (default a third of the longer side, at most 400; 0 draws every frame). */
493
+ panMargin?: number;
492
494
  /** A map token; or */
493
495
  token?: string;
494
496
  /** Called at start and again two minutes before each token expires. */
@@ -725,7 +727,7 @@ export declare class Map {
725
727
  addPolygons(id: string, data: LayerData, options?: PolygonOptions): this;
726
728
  /** A drawing and editing tool (G3): polygons, rectangles, circles, lines and points, holes, snapping, undo. */
727
729
  drawTool(options?: DrawToolOptions): DrawTool;
728
- /** Selects features by drawing a box or a lasso (G4); null when Escape cancels. */
730
+ /** Selects features by drawing a box or a lasso (G4); null when Escape cancels. Every layer named in `layers` is in `features` (empty when nothing of it was caught). */
729
731
  selectArea(options?: { mode?: 'box' | 'lasso'; layers?: string[] }): Promise<{ features: Record<string, GeoJSONFeature[]>; geometry: Record<string, unknown> } | null>;
730
732
  /** Google's base maps read straight from Google with your Map Tiles session (G10); null takes them off. */
731
733
  setGoogleBase(google: GoogleBaseOptions | null): this;
package/types/smap.d.mts CHANGED
@@ -489,6 +489,8 @@ export interface MapOptions {
489
489
  * hillshade or 3D models; the map fires 'smap.unsupported' with { feature } for each of them that is asked for.
490
490
  */
491
491
  renderer?: 'auto' | 'webgl' | 'canvas';
492
+ /** Canvas map only: pixels drawn past each edge, so panning shifts the drawn picture instead of drawing it again (default a third of the longer side, at most 400; 0 draws every frame). */
493
+ panMargin?: number;
492
494
  /** A map token; or */
493
495
  token?: string;
494
496
  /** Called at start and again two minutes before each token expires. */
@@ -725,7 +727,7 @@ export declare class Map {
725
727
  addPolygons(id: string, data: LayerData, options?: PolygonOptions): this;
726
728
  /** A drawing and editing tool (G3): polygons, rectangles, circles, lines and points, holes, snapping, undo. */
727
729
  drawTool(options?: DrawToolOptions): DrawTool;
728
- /** Selects features by drawing a box or a lasso (G4); null when Escape cancels. */
730
+ /** Selects features by drawing a box or a lasso (G4); null when Escape cancels. Every layer named in `layers` is in `features` (empty when nothing of it was caught). */
729
731
  selectArea(options?: { mode?: 'box' | 'lasso'; layers?: string[] }): Promise<{ features: Record<string, GeoJSONFeature[]>; geometry: Record<string, unknown> } | null>;
730
732
  /** Google's base maps read straight from Google with your Map Tiles session (G10); null takes them off. */
731
733
  setGoogleBase(google: GoogleBaseOptions | null): this;