@superrouteca/smap-arcgis 1.1.1 → 1.1.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 +9 -0
- package/README.md +5 -1
- package/package.json +2 -2
- package/src/basemap.js +117 -1
- package/src/index.js +1 -1
- package/types/index.d.ts +5 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.1.2 — 2026-10-27
|
|
4
|
+
|
|
5
|
+
- Restyling quickly no longer logs errors: `setStyle()`, `setLanguage()` and `setBasemap()` called in quick
|
|
6
|
+
succession are applied one at a time and the latest call wins. A call that a newer one replaced resolves quietly
|
|
7
|
+
instead of rejecting with "Request canceled"; a real failure (network, a style that does not load) still rejects.
|
|
8
|
+
- A restyle started while the map switches between the raster and the vector basemap waits until the vector layer is
|
|
9
|
+
shown again and then applies to it, and a basemap switched away from during a restyle no longer reports "Cannot read
|
|
10
|
+
properties of null (reading 'setStyleResources')". Removing the basemap during a restyle throws nothing.
|
|
11
|
+
|
|
3
12
|
## 1.1.1 — 2026-10-27
|
|
4
13
|
|
|
5
14
|
- The vector basemap draws: water, land, roads, labels and icons appear in every colour scheme and label language
|
package/README.md
CHANGED
|
@@ -44,7 +44,11 @@ An existing ArcGIS app switches its basemap and keeps every layer: `map.basemap
|
|
|
44
44
|
- `basemap: { pois: { visible: false }, roads: { color: '#ffffff' } }` shows, hides and recolours the basemap by
|
|
45
45
|
class (vector), as the sMap map's `setBasemap`.
|
|
46
46
|
- `basemap.smap.setStyle(style)` / `setLanguage(language)` / `setBasemap(overrides)` (vector) restyle the layer in
|
|
47
|
-
place.
|
|
47
|
+
place. Calls in quick succession (buttons clicked fast) are applied one at a time and the latest call wins: a state
|
|
48
|
+
replaced before it loaded is skipped, and a style is loaded only once the layer view is ready (also right after the
|
|
49
|
+
map's basemap was switched back to it). Each call returns a promise of the layer: the latest call resolves when its
|
|
50
|
+
state is shown and rejects when it fails (network, a style that does not load); a call that a newer one superseded
|
|
51
|
+
resolves with the layer once the newer call has settled and never rejects, so handlers need no `catch` for it.
|
|
48
52
|
|
|
49
53
|
The layers on their own: `createVectorTileLayer(options)` and `createRasterTileLayer(options)`.
|
|
50
54
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@superrouteca/smap-arcgis",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.2",
|
|
4
4
|
"description": "sMap for the ArcGIS Maps SDK for JavaScript: sMap basemaps (vector and raster), sMap layers as graphics, and ArcGIS REST services on sMap maps.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Goodneo Solutions",
|
|
@@ -50,7 +50,7 @@
|
|
|
50
50
|
"typecheck": "tsc -p test/tsconfig.json"
|
|
51
51
|
},
|
|
52
52
|
"dependencies": {
|
|
53
|
-
"@superrouteca/smap": "1.1.
|
|
53
|
+
"@superrouteca/smap": "1.1.2"
|
|
54
54
|
},
|
|
55
55
|
"peerDependencies": {
|
|
56
56
|
"@arcgis/core": "^5.0.0"
|
package/src/basemap.js
CHANGED
|
@@ -62,6 +62,122 @@ export function installInterceptor(sessionOrOptions) {
|
|
|
62
62
|
return handle;
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
function isAbort(error) {
|
|
66
|
+
return !!error && (error.name === 'AbortError' || error.name === 'cancel' || /^cancell?ed:/.test(String(error.name || '')));
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The views that drew the layer (learnt from its layerview-create events), the layer views it has now, and a guard
|
|
71
|
+
* on each of those layer views: ArcGIS 5.x's VectorTileLayerView2D reads its tile container after awaiting the
|
|
72
|
+
* sprites of a new style, and the container is gone when the layer view was detached meanwhile (the basemap switched
|
|
73
|
+
* away during a restyle), which ArcGIS raised as an unhandled "Cannot read properties of null
|
|
74
|
+
* (reading 'setStyleResources')". A failure of a layer view that is no longer attached is dropped; any other is kept.
|
|
75
|
+
*/
|
|
76
|
+
function trackLayerViews(layer) {
|
|
77
|
+
const views = new Set();
|
|
78
|
+
const layerViews = new Set();
|
|
79
|
+
const guard = (lv) => {
|
|
80
|
+
if (!lv || lv.__smapGuarded || typeof lv._loadStyle !== 'function') { return; }
|
|
81
|
+
const own = lv._loadStyle;
|
|
82
|
+
lv._loadStyle = function smapGuardedLoadStyle(...args) {
|
|
83
|
+
const gone = () => this.destroyed || this.attached === false || !this._vectorTileContainer;
|
|
84
|
+
try {
|
|
85
|
+
return Promise.resolve(own.apply(this, args)).catch((error) => { if (gone()) { return undefined; } throw error; });
|
|
86
|
+
} catch (error) {
|
|
87
|
+
if (gone()) { return Promise.resolve(); }
|
|
88
|
+
throw error;
|
|
89
|
+
}
|
|
90
|
+
};
|
|
91
|
+
lv.__smapGuarded = true;
|
|
92
|
+
};
|
|
93
|
+
if (typeof layer.on === 'function') {
|
|
94
|
+
layer.on('layerview-create', (e) => {
|
|
95
|
+
if (e && e.view) { views.add(e.view); }
|
|
96
|
+
if (e && e.layerView) { layerViews.add(e.layerView); guard(e.layerView); }
|
|
97
|
+
});
|
|
98
|
+
layer.on('layerview-destroy', (e) => { if (e && e.layerView) { layerViews.delete(e.layerView); } });
|
|
99
|
+
}
|
|
100
|
+
return {
|
|
101
|
+
/**
|
|
102
|
+
* Resolves once every view that shows the layer has its layer view (one being created after a basemap switch is
|
|
103
|
+
* waited for); a view that drops the layer meanwhile is left out. Never rejects.
|
|
104
|
+
*/
|
|
105
|
+
async settled() {
|
|
106
|
+
const waits = [];
|
|
107
|
+
for (const view of views) {
|
|
108
|
+
if (!view || view.destroyed) { views.delete(view); continue; }
|
|
109
|
+
const all = view.map && view.map.allLayers;
|
|
110
|
+
const shown = all && typeof all.includes === 'function' ? all.includes(layer) : false;
|
|
111
|
+
if (!shown || typeof view.whenLayerView !== 'function') { continue; }
|
|
112
|
+
waits.push(Promise.resolve().then(() => view.whenLayerView(layer)).then((lv) => {
|
|
113
|
+
guard(lv);
|
|
114
|
+
return typeof (lv && lv.when) === 'function' ? lv.when() : lv;
|
|
115
|
+
}).catch(() => undefined));
|
|
116
|
+
}
|
|
117
|
+
await Promise.all(waits);
|
|
118
|
+
},
|
|
119
|
+
};
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
/**
|
|
123
|
+
* Restyles `layer` with what `styled()` gives for the state wanted now. Calls are serialised and coalesced, so the
|
|
124
|
+
* latest call wins: one restyle runs at a time, a state that was replaced before it was loaded is not loaded, and the
|
|
125
|
+
* style is loaded only once the layer's layer views are ready (never into one being re-created after a basemap
|
|
126
|
+
* switch). Each call returns a promise of the layer: the latest call resolves when its state is shown and rejects
|
|
127
|
+
* when it fails (network, a style that does not load); a call that a newer one superseded resolves with the layer once
|
|
128
|
+
* the newer call has settled, and never rejects. A destroyed layer (the basemap removed) resolves every call.
|
|
129
|
+
*/
|
|
130
|
+
function restyler(layer, styled) {
|
|
131
|
+
const views = trackLayerViews(layer);
|
|
132
|
+
let wanted = 0;
|
|
133
|
+
let running = false;
|
|
134
|
+
const waiting = [];
|
|
135
|
+
const gone = () => !!layer.destroyed;
|
|
136
|
+
const settle = (upTo, error) => {
|
|
137
|
+
for (let i = waiting.length - 1; i >= 0; i--) {
|
|
138
|
+
const w = waiting[i];
|
|
139
|
+
if (w.gen > upTo) { continue; }
|
|
140
|
+
waiting.splice(i, 1);
|
|
141
|
+
if (error && w.gen === upTo) { w.reject(error); } else { w.resolve(layer); }
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
const run = async () => {
|
|
145
|
+
running = true;
|
|
146
|
+
let aborts = 0;
|
|
147
|
+
try {
|
|
148
|
+
while (waiting.length) {
|
|
149
|
+
const gen = wanted;
|
|
150
|
+
if (gone()) { settle(gen); break; }
|
|
151
|
+
try {
|
|
152
|
+
const doc = await styled();
|
|
153
|
+
if (gen !== wanted || gone()) { continue; }
|
|
154
|
+
await views.settled();
|
|
155
|
+
if (gen !== wanted || gone()) { continue; }
|
|
156
|
+
await layer.loadStyle(doc);
|
|
157
|
+
if (gen !== wanted) { continue; }
|
|
158
|
+
aborts = 0;
|
|
159
|
+
settle(gen);
|
|
160
|
+
} catch (error) {
|
|
161
|
+
if (gone()) { settle(wanted); break; }
|
|
162
|
+
if (gen !== wanted) { continue; }
|
|
163
|
+
// Cancelled by something else than this adapter (another loadStyle on the layer): try again, twice at most.
|
|
164
|
+
if (isAbort(error) && aborts++ < 2) { continue; }
|
|
165
|
+
aborts = 0;
|
|
166
|
+
settle(gen, error);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
} finally {
|
|
170
|
+
running = false;
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
return () => {
|
|
174
|
+
const gen = ++wanted;
|
|
175
|
+
const promise = new Promise((resolve, reject) => { waiting.push({ gen, resolve, reject }); });
|
|
176
|
+
if (!running) { run(); }
|
|
177
|
+
return promise;
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
|
|
65
181
|
/**
|
|
66
182
|
* A VectorTileLayer drawing sMap's vector tiles in a scheme or custom style, labels in `language`. The style is read
|
|
67
183
|
* through the session (nearest node, failover, key, token); tiles, fonts and sprites through the interceptor.
|
|
@@ -95,7 +211,7 @@ export async function createVectorTileLayer(options = {}) {
|
|
|
95
211
|
style: await styled(),
|
|
96
212
|
...(options.layerProps || {}),
|
|
97
213
|
});
|
|
98
|
-
const reload =
|
|
214
|
+
const reload = restyler(layer, styled);
|
|
99
215
|
layer.smap = {
|
|
100
216
|
session,
|
|
101
217
|
get style() { return style; },
|
package/src/index.js
CHANGED
|
@@ -4,4 +4,4 @@ export { installInterceptor, createVectorTileLayer, createRasterTileLayer, creat
|
|
|
4
4
|
export { toArcGISStyle, styleLanguage, nameExpression } from './style.js';
|
|
5
5
|
export { createGraphicsLayer, symbolFor, toArcGisGeometries, toFeatures, zoomToScale } from './graphics.js';
|
|
6
6
|
export { addArcGisService, queryFeatures, serviceInfo, parseServiceUrl, withToken, symbolToSimplestyle, rendererStyle } from './services.js';
|
|
7
|
-
export const version = '1.1.
|
|
7
|
+
export const version = '1.1.2';
|
package/types/index.d.ts
CHANGED
|
@@ -81,6 +81,11 @@ export interface SMapLayerInfo {
|
|
|
81
81
|
readonly language?: string;
|
|
82
82
|
}
|
|
83
83
|
export interface SMapVectorInfo extends SMapLayerInfo {
|
|
84
|
+
/**
|
|
85
|
+
* setStyle / setLanguage / setBasemap are applied one at a time and the latest call wins. The latest call resolves
|
|
86
|
+
* when its state is shown and rejects when it fails; a call a newer one superseded resolves with the layer once the
|
|
87
|
+
* newer call has settled and never rejects.
|
|
88
|
+
*/
|
|
84
89
|
setStyle(style: StyleName): Promise<VectorTileLayer>;
|
|
85
90
|
setLanguage(language: string): Promise<VectorTileLayer>;
|
|
86
91
|
/** Basemap options by class (contract §1), replacing the previous ones; `{}` clears. */
|