@superrouteca/smap 1.1.5 → 1.1.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 +25 -0
- package/README.md +104 -3
- package/dist/SOURCE.json +8 -8
- package/dist/smap-client.cjs +2 -2
- package/dist/smap-client.js +2 -2
- package/dist/smap-client.mjs +2 -2
- package/dist/smap.cjs +1770 -16
- package/dist/smap.css +61 -1
- package/dist/smap.js +1759 -13
- package/dist/smap.mjs +1768 -17
- package/package.json +1 -1
- package/types/smap.d.cts +382 -5
- package/types/smap.d.mts +382 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.6 — 2026-10-28
|
|
4
|
+
|
|
5
|
+
- Plan editor: `map.planEditor` lets a person edit a plan of routes on the map — drag a stop within its route or onto
|
|
6
|
+
another route, take stops off, place new orders at the positions the service ranks best and choose another of them,
|
|
7
|
+
lock stops and refine routes around them, undo and redo. Every edit is a call of the Open Platform's plan editing
|
|
8
|
+
with the whole plan, and its answer replaces the routes, distances, times and violations drawn; while a stop is
|
|
9
|
+
dragged, the plan with the stop there is priced as a preview. The editor opens a distance snapshot for faster
|
|
10
|
+
edits and opens a new one by itself when it expired. Credentials are a short-lived plan editor token that your own
|
|
11
|
+
server mints (the token string or the token answer as it is; renewed before it expires and after an expired
|
|
12
|
+
session) or a map key with plan editing switched on, both sent as a bearer token; refusals arrive as errors with
|
|
13
|
+
their reason. A ready-made panel shows routes, totals, violations, undo and redo, the
|
|
14
|
+
insert positions and errors in 14 languages; it can be restyled or left out for a panel of your own.
|
|
15
|
+
`sMap.PlanEditor` is the same editor without a map.
|
|
16
|
+
- `planEditorParts`: the parts a plan editor on another map library is built from — the features to draw, where a
|
|
17
|
+
dropped stop goes, a drag with its preview, hit testing in pixels, and the panel with its stylesheet. The tool
|
|
18
|
+
adapters use them.
|
|
19
|
+
- Map tools: the tools control is now the last button of the map's toolbar by default — the corner holding the zoom
|
|
20
|
+
buttons or compass, top left when there is none — and stays last when controls join that corner later. A corner
|
|
21
|
+
given in `tools.control` is kept as before. While its panel is open its corner is drawn above the map's other
|
|
22
|
+
corners, so nothing on the map covers it, and the panel opens towards the middle of the map.
|
|
23
|
+
- Map tools: data layers can be tools. `addTool(id, { layer: 'stops' })` or `{ layer: ['stops', 'stop-labels'] }`
|
|
24
|
+
shows and hides the layers with the tool, in the tools control and with `setToolVisible` / `setToolsVisible`, and
|
|
25
|
+
they are listed by `tools()` and saved through `onToolsChange` like every other tool. A layer that is not on the map
|
|
26
|
+
is left alone.
|
|
27
|
+
|
|
3
28
|
## 1.1.5 — 2026-10-28
|
|
4
29
|
|
|
5
30
|
- Map tools: the controls and panels floating on a map can be shown and hidden one by one or all at once. Register
|
package/README.md
CHANGED
|
@@ -203,8 +203,8 @@ an upper pin.
|
|
|
203
203
|
|
|
204
204
|
## Map tools: showing and hiding controls and panels
|
|
205
205
|
|
|
206
|
-
Register what floats on the map, and the map gets a tools control (one button,
|
|
207
|
-
tool, plus *Show all* and *Hide all*:
|
|
206
|
+
Register what floats on the map, and the map gets a tools control (one button, the last of the map's toolbar) that
|
|
207
|
+
shows or hides each tool, plus *Show all* and *Hide all*:
|
|
208
208
|
|
|
209
209
|
```js
|
|
210
210
|
const map = new sMap.Map({ container: 'map', style: 'light', tools: { hidden: saved.hidden } });
|
|
@@ -212,6 +212,7 @@ map.addTool('zoom', 'navigation', { options: { showCompass: false } }); // mad
|
|
|
212
212
|
map.addTool('fullscreen', 'fullscreen');
|
|
213
213
|
map.addTool('legend', document.getElementById('legend'), { label: 'Legend', position: 'bottom-left' });
|
|
214
214
|
map.addTool('orders', document.getElementById('orders'), { label: 'Orders list', position: 'none' }); // stays in the page
|
|
215
|
+
map.addTool('stops', { layer: ['stops', 'stop-labels'] }, { label: 'Stops' }); // data layers
|
|
215
216
|
map.onToolsChange((e) => save({ hidden: e.hidden }));
|
|
216
217
|
|
|
217
218
|
map.setToolVisible('legend', false);
|
|
@@ -225,12 +226,111 @@ map.tools(); // [{ id: 'zoom', label: 'Zoom', visible: true }, …]
|
|
|
225
226
|
- A hidden tool is not drawn and keeps its state (an open list stays open, typed text stays). Hiding a switch does not
|
|
226
227
|
turn off what it switches.
|
|
227
228
|
- The map data credit, the sMap badge and the tools control are never tools: *Hide all* leaves them.
|
|
229
|
+
- Data layers are tools too: `{ layer: 'stops' }` or `{ layer: ['stops', 'stop-labels'] }` names layers of
|
|
230
|
+
`addPoints` / `addLines` / `addPolygons` / `addData` / `addStyleLayer` and the external layers, or of the basemap.
|
|
231
|
+
Hiding the tool hides them (layout visibility), showing it shows them. A layer added later under an id of a hidden
|
|
232
|
+
tool starts hidden; an id without a layer (removed, or not added yet) is left alone. Removing a hidden data-layer
|
|
233
|
+
tool shows its layers again.
|
|
234
|
+
- Where the tools control sits (`tools.control`): by default (`'toolbar'`) it is the last button of the map's toolbar,
|
|
235
|
+
the corner holding the zoom buttons or compass (top left when there is none), and stays last when controls join
|
|
236
|
+
that corner later; a corner (`'top-right'`, …) places it there, as in 1.1.5. While its panel is open its corner is
|
|
237
|
+
drawn above the map's other corners, and the panel opens towards the middle of the map.
|
|
228
238
|
- `tools.control: false` leaves the tools control out (make your own buttons with the calls above);
|
|
229
239
|
`tools.labels: { button, showAll, hideAll }` translates it.
|
|
230
240
|
- The SDK keeps no state: give the starting state in `tools.hidden` and keep `onToolsChange`'s `hidden` where you
|
|
231
241
|
like (the page's storage, your server).
|
|
232
242
|
- The tools control joins the map with the first tool; a map without tools is unchanged.
|
|
233
243
|
|
|
244
|
+
## Plan editor: editing routes on the map
|
|
245
|
+
|
|
246
|
+
`map.planEditor(options)` lets a person edit a plan of routes on the map: drag a stop within its route or onto another
|
|
247
|
+
route, take stops off, place new orders at the positions the service ranks best, refine routes around locked stops,
|
|
248
|
+
and undo or redo. Every edit is one call of the Open Platform's plan editing (`plan.resequence`, `plan.move`,
|
|
249
|
+
`plan.remove`, `plan.insert`, `plan.refine`, `plan.recost`) with the whole plan; the answer replaces the plan drawn,
|
|
250
|
+
its distances, times and violations. Nothing is stored on a server: keep the plan from the `change` event.
|
|
251
|
+
|
|
252
|
+
```js
|
|
253
|
+
const editor = map.planEditor({
|
|
254
|
+
// A short-lived plan editor token from your own server, or { key: 'smk_…' }: a map key with plan editing switched on.
|
|
255
|
+
credentials: { token: () => fetch('/plan-token').then((r) => r.json()) },
|
|
256
|
+
plan: {
|
|
257
|
+
region: 'NA', profile: 'car',
|
|
258
|
+
vehicles: [{ id: 'van-1', start: { lat: 45.5017, lng: -73.5673 }, return_to_start: true }],
|
|
259
|
+
jobs: [{ id: 'order-1001', location: { lat: 45.5088, lng: -73.554 }, service_seconds: 300 }],
|
|
260
|
+
routes: [{ vehicle_id: 'van-1', stops: ['order-1001'] }],
|
|
261
|
+
},
|
|
262
|
+
language: 'fr', // the panel's language; default the map's
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
editor.on('change', (e) => save(e.plan)); // e.plan: vehicles, jobs, routes, unassigned
|
|
266
|
+
editor.on('error', (e) => report(e.error.code));
|
|
267
|
+
editor.on('busy', (e) => spinner(e.busy));
|
|
268
|
+
|
|
269
|
+
await editor.insert({ id: 'order-2001', location: { lat: 45.52, lng: -73.58 } });
|
|
270
|
+
editor.choices(); // the ranked positions answered for it
|
|
271
|
+
await editor.choose('order-2001', 1); // take the second one instead
|
|
272
|
+
editor.lock(['order-1001']);
|
|
273
|
+
await editor.refine({ vehicleIds: ['van-1'] });
|
|
274
|
+
await editor.undo();
|
|
275
|
+
editor.destroy();
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
| Call | What it does |
|
|
279
|
+
|---|---|
|
|
280
|
+
| `setPlan(plan)` / `getPlan()` / `getResult()` | Replace the plan (the history starts again; it is priced), read it, read its prices |
|
|
281
|
+
| `resequence(vehicleId, stops)` | A route's stops in a new order (`plan.resequence`) |
|
|
282
|
+
| `move(stops, vehicleId, position?)` | Stops to a route at a position from 1, counted without them; none: last (`plan.move`) |
|
|
283
|
+
| `remove(stops)` | Stops off their routes, to the unassigned (`plan.remove`) |
|
|
284
|
+
| `dropStop(stopId, vehicleId, position)` | A stop dropped before the stop at `position` as drawn: a resequence within its route, a move otherwise |
|
|
285
|
+
| `insert(jobs, { limit, objective, nearest }?)` | New jobs, or unassigned jobs by id, at their best positions (`plan.insert`); `choices()` and `choose(job, i)` |
|
|
286
|
+
| `refine({ vehicleIds, freeze }?)` | Re-orders routes around the locked stops (`lock`, `unlock`), kept only when better (`plan.refine`) |
|
|
287
|
+
| `undo()` / `redo()` | Back and forward through the edits; an answered plan is shown again without a call |
|
|
288
|
+
| `preview(routes)` / `clearPreview()` | Prices other routes without changing the plan; the map does this while a stop is dragged |
|
|
289
|
+
| `state()`, `on` / `off` (`change`, `busy`, `error`, `snapshot`, `preview`, `destroy`), `destroy()` | |
|
|
290
|
+
|
|
291
|
+
- **On the map**: the routes are drawn as layers `plan:legs`, `plan:starts`, `plan:stops` and `plan:unassigned`
|
|
292
|
+
(`id` changes the prefix, `colors` the route colours). Drag a stop onto another stop to put it before that stop, or
|
|
293
|
+
onto a route's line to put it into that leg. While a stop is dragged, the plan with the stop at that place is priced
|
|
294
|
+
and shown in the panel as a preview (`preview: false` turns it off).
|
|
295
|
+
- **The panel** is the map tool `plan-editor` (top left; `panel: { position, label }`): the total and each route's
|
|
296
|
+
distance, driving time and stops, violations, the stops with their arrival, *Lock* and *Remove*, the unassigned
|
|
297
|
+
jobs with *Place*, the positions an insert answered with *Choose*, undo / redo / refine, and errors. Stops can be
|
|
298
|
+
dragged between its lists too. It speaks English, Simplified and Traditional Chinese, German, Spanish, French,
|
|
299
|
+
Italian, Dutch, Portuguese, Serbian, Hungarian, Polish, Slovak and Czech; `labels` replaces any text
|
|
300
|
+
(`sMap.planTexts` lists them), and every colour is in the stylesheet under `.smap-plan-*`. `panel: false` leaves it
|
|
301
|
+
out, for a panel of your own built on `state()` and the events.
|
|
302
|
+
- **Credentials**: your own server mints the token with its API token and hands the answer on; the browser never
|
|
303
|
+
holds the API token. Both credentials are sent as `Authorization: Bearer …`, and the Open Platform answers the
|
|
304
|
+
page's origin (CORS) with `X-Plan-Matrix`, `Retry-After` and `X-RateLimit-*` readable.
|
|
305
|
+
|
|
306
|
+
```js
|
|
307
|
+
// On your server (Node.js shown): POST /v1/plan/token with the API token.
|
|
308
|
+
app.get('/plan-token', async (req, res) => {
|
|
309
|
+
const r = await fetch('https://open.superroute.ca/v1/plan/token', {
|
|
310
|
+
method: 'POST',
|
|
311
|
+
headers: { Authorization: 'Bearer ' + process.env.SUPERROUTE_API_TOKEN, 'Content-Type': 'application/json' },
|
|
312
|
+
body: JSON.stringify({ ttl_seconds: 900, origins: ['dispatch.example.com'] }),
|
|
313
|
+
});
|
|
314
|
+
res.status(r.status).json((await r.json()).data); // { access_token: 'sr_pet_…', expires_at, expires_in, scopes, origins }
|
|
315
|
+
});
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
The provider may return the token string or that answer; the editor asks again 30 seconds before `expires_at`, and
|
|
319
|
+
once more after a `401 SESSION_EXPIRED` before repeating the call. Every other refusal is an `error` with its
|
|
320
|
+
`code` and `reason` and is not repeated: `401 SESSION_REVOKED` (`key_revoked`, `entitlements_changed`: mint a new
|
|
321
|
+
token), `401 TOKEN_INVALID`, and `403 NOT_AUTHORIZED` (`origin_not_allowed`, `no_plan_capability`,
|
|
322
|
+
`plan_editing_off`, `key_off`, `plan_routes_only`). A map key needs *Allow plan editing* switched on and the page's
|
|
323
|
+
origin on its allowlist. In an app shell (macOS, Windows) pass `app: 'apple:<bundle id>'` or `'windows:<id>'`: it
|
|
324
|
+
is sent as `X-SMap-App`.
|
|
325
|
+
- **Distance snapshot**: the editor opens one (`POST /v1/plan/matrix`) and names it in every call, so legs measured
|
|
326
|
+
once are not measured again. When an answer says the snapshot expired (`X-Plan-Matrix: expired`, or a `410`), it
|
|
327
|
+
opens a new one and carries on; a snapshot that cannot be opened never stops an edit. `destroy()` closes it.
|
|
328
|
+
- **Calls in flight**: a newer edit cancels the call before it. An edit you can see the outcome of (a move, an order,
|
|
329
|
+
a removal) is drawn at once and priced when the answer arrives; `pending` in the `change` event says it is not
|
|
330
|
+
priced yet. A refused edit is taken back and reported as `error`.
|
|
331
|
+
- `new sMap.PlanEditor(options)` is the same editor without a map, for an interface of your own.
|
|
332
|
+
- `examples/plan-editor.html` runs the editor against a stand-in of the service in the page (build first).
|
|
333
|
+
|
|
234
334
|
## Style layers and sources
|
|
235
335
|
|
|
236
336
|
Sources and layers of the Style Specification v8 are passed to the map as they are, so a layer copied from Mapbox
|
|
@@ -434,7 +534,8 @@ its bottom-right corner, left of the attribution (above it when the map is too n
|
|
|
434
534
|
## Where the code comes from
|
|
435
535
|
|
|
436
536
|
The sources are in `src/` (the Web SDK `smap.js` and `smap.css`, the parts that need no map `smap-interop.js`:
|
|
437
|
-
formats, basemap options, slots, URLs, PMTiles; the JS client `smap-client.js` and its
|
|
537
|
+
formats, basemap options, slots, URLs, PMTiles; the plan editor `smap-plan.js`; the JS client `smap-client.js` and its
|
|
538
|
+
types).
|
|
438
539
|
`npm run build` writes `dist/` from them and the rendering engine (an exact devDependency); `dist/SOURCE.json`
|
|
439
540
|
records the version, the engine and the hash of every file. `dist/` is committed and never edited by hand. The type
|
|
440
541
|
declarations of the map SDK are in `types/`.
|
package/dist/SOURCE.json
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
{
|
|
2
|
-
"sdkVersion": "1.1.
|
|
2
|
+
"sdkVersion": "1.1.6",
|
|
3
3
|
"engine": "maplibre-gl 6.16.0",
|
|
4
4
|
"files": {
|
|
5
|
-
"smap.js": "
|
|
6
|
-
"smap.css": "
|
|
7
|
-
"smap-client.js": "
|
|
5
|
+
"smap.js": "1d0821da7bf4c5891a67336750974fd66109bb148f6dc8b248f398ebc1d7dfd9",
|
|
6
|
+
"smap.css": "d82adcd1b8e2b5d1a6ebe0ee648bc24eb84471bc956e3c8ac3cc7dc1f447d96f",
|
|
7
|
+
"smap-client.js": "ef0b0915f86ec57c4cb654f01db4530e75123101d595b1e718db4b645097856b",
|
|
8
8
|
"smap-client.d.ts": "5f0df11bf60c5a9ced34522a284de81260c27871678ab772f16da9ccf6883f58",
|
|
9
|
-
"smap.mjs": "
|
|
10
|
-
"smap.cjs": "
|
|
11
|
-
"smap-client.cjs": "
|
|
12
|
-
"smap-client.mjs": "
|
|
9
|
+
"smap.mjs": "bd333a886917566235b970f6a2709acb004670fdb5040c9a9380ad3b654dd923",
|
|
10
|
+
"smap.cjs": "ed8a33d2457eb243d59a50cc70fb92f1e59382c8f97520ed98ede99adf24d303",
|
|
11
|
+
"smap-client.cjs": "f5aa773fbaf1cfe46a3e37de246c9918609729b3d624aa624f380466ef56bb6f",
|
|
12
|
+
"smap-client.mjs": "6b895d27a13b026ac5e5aecdb608495f3337cda0650a7b2cceaaad8d54c032bd",
|
|
13
13
|
"smap-client.d.cts": "e0fe4eb54cd66b831116082f42bd2f7de260f9f12a2741f4004f8cf10c252116",
|
|
14
14
|
"smap-client.d.mts": "96830d316ddb33b4ff8edb8b6f810337479636114f75c4170d95038ffda74103",
|
|
15
15
|
"vendor-licenses.txt": "ee5fc05a0677eaf69601d2c7db0d9ecd6cc27c3abc1d0733bc9ed34707cf8ef2"
|
package/dist/smap-client.cjs
CHANGED
|
@@ -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.1.
|
|
2
|
+
/*! sMap JS client 1.1.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.1.
|
|
22
|
+
var VERSION = '1.1.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
|
|
package/dist/smap-client.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
/*! sMap JS client 1.1.
|
|
1
|
+
/*! sMap JS client 1.1.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.1.
|
|
21
|
+
var VERSION = '1.1.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
|
|
package/dist/smap-client.mjs
CHANGED
|
@@ -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.1.
|
|
4
|
+
/*! sMap JS client 1.1.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.1.
|
|
24
|
+
var VERSION = '1.1.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
|
|