@geospatial-sdk/openlayers 0.0.5-dev.55 → 0.0.5-dev.56
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/dist/map/apply-context-diff.d.ts.map +1 -1
- package/dist/map/apply-context-diff.js +9 -1
- package/dist/map/create-map.d.ts +1 -1
- package/dist/map/create-map.d.ts.map +1 -1
- package/dist/map/create-map.js +56 -30
- package/dist/map/feature-hover.js +1 -1
- package/dist/map/handle-errors.d.ts +0 -7
- package/dist/map/handle-errors.d.ts.map +1 -1
- package/dist/map/handle-errors.js +10 -14
- package/dist/map/index.d.ts +2 -1
- package/dist/map/index.d.ts.map +1 -1
- package/dist/map/index.js +2 -1
- package/dist/map/layer-update.d.ts.map +1 -1
- package/dist/map/layer-update.js +1 -1
- package/dist/map/listen.d.ts +4 -0
- package/dist/map/listen.d.ts.map +1 -0
- package/dist/map/listen.js +70 -0
- package/dist/map/register-events.d.ts +16 -2
- package/dist/map/register-events.d.ts.map +1 -1
- package/dist/map/register-events.js +172 -81
- package/dist/map/resolved-map-state.d.ts +8 -0
- package/dist/map/resolved-map-state.d.ts.map +1 -0
- package/dist/map/resolved-map-state.js +26 -0
- package/lib/map/apply-context-diff.ts +16 -5
- package/lib/map/create-map.test.ts +127 -38
- package/lib/map/create-map.ts +82 -37
- package/lib/map/feature-hover.ts +1 -1
- package/lib/map/handle-errors.test.ts +13 -36
- package/lib/map/handle-errors.ts +10 -28
- package/lib/map/index.ts +2 -1
- package/lib/map/layer-update.ts +3 -2
- package/lib/map/listen.test.ts +977 -0
- package/lib/map/listen.ts +123 -0
- package/lib/map/register-events.ts +229 -109
- package/lib/map/resolved-map-state.ts +38 -0
- package/package.json +3 -3
- package/lib/map/register-events.test.ts +0 -259
|
@@ -7,9 +7,9 @@ import {
|
|
|
7
7
|
import {
|
|
8
8
|
MAP_CTX_EXTENT_FIXTURE,
|
|
9
9
|
MAP_CTX_FIXTURE,
|
|
10
|
-
MAP_CTX_LAYER_GEOTIFF_FIXTURE,
|
|
11
10
|
MAP_CTX_LAYER_GEOJSON_FIXTURE,
|
|
12
11
|
MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE,
|
|
12
|
+
MAP_CTX_LAYER_GEOTIFF_FIXTURE,
|
|
13
13
|
MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE,
|
|
14
14
|
MAP_CTX_LAYER_MVT_FIXTURE,
|
|
15
15
|
MAP_CTX_LAYER_OGCAPI_FIXTURE,
|
|
@@ -44,10 +44,8 @@ import {
|
|
|
44
44
|
createView,
|
|
45
45
|
resetMapFromContext,
|
|
46
46
|
} from "./create-map.js";
|
|
47
|
-
import {
|
|
48
|
-
|
|
49
|
-
tileLoadErrorCatchFunction,
|
|
50
|
-
} from "./handle-errors.js";
|
|
47
|
+
import { tileLoadErrorCatchFunction } from "./handle-errors.js";
|
|
48
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
51
49
|
|
|
52
50
|
vi.mock("./handle-errors", async (importOriginal) => {
|
|
53
51
|
const actual =
|
|
@@ -59,18 +57,22 @@ vi.mock("./handle-errors", async (importOriginal) => {
|
|
|
59
57
|
};
|
|
60
58
|
});
|
|
61
59
|
|
|
62
|
-
|
|
60
|
+
beforeEach(() => {
|
|
61
|
+
vi.useFakeTimers();
|
|
63
62
|
vi.clearAllMocks();
|
|
64
63
|
});
|
|
65
64
|
|
|
66
65
|
describe("MapContextService", () => {
|
|
67
66
|
describe("#createLayer", () => {
|
|
68
67
|
let layerModel: MapContextLayer, layer: Layer;
|
|
68
|
+
const eventCallback = vi.fn();
|
|
69
69
|
|
|
70
70
|
describe("XYZ", () => {
|
|
71
71
|
beforeEach(async () => {
|
|
72
72
|
layerModel = MAP_CTX_LAYER_XYZ_FIXTURE;
|
|
73
73
|
layer = await createLayer(layerModel);
|
|
74
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
75
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
74
76
|
});
|
|
75
77
|
it("create a tile layer", () => {
|
|
76
78
|
expect(layer).toBeTruthy();
|
|
@@ -108,13 +110,25 @@ describe("MapContextService", () => {
|
|
|
108
110
|
tileLoadFunction(tile, "http://example.com/tile");
|
|
109
111
|
expect(tileLoadErrorCatchFunction).toHaveBeenCalled();
|
|
110
112
|
});
|
|
113
|
+
it("emits a loaded event initially", async () => {
|
|
114
|
+
await vi.runAllTimersAsync();
|
|
115
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
116
|
+
layerState: {
|
|
117
|
+
loaded: true,
|
|
118
|
+
},
|
|
119
|
+
target: layer,
|
|
120
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
121
|
+
});
|
|
122
|
+
});
|
|
111
123
|
});
|
|
112
|
-
describe("OGCAPI", () => {
|
|
124
|
+
describe("OGCAPI (as geojson items)", () => {
|
|
113
125
|
beforeEach(async () => {
|
|
114
126
|
layerModel = MAP_CTX_LAYER_OGCAPI_FIXTURE;
|
|
115
127
|
layer = await createLayer(layerModel);
|
|
128
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
129
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
116
130
|
});
|
|
117
|
-
it("create a vector
|
|
131
|
+
it("create a vector layer", () => {
|
|
118
132
|
expect(layer).toBeTruthy();
|
|
119
133
|
expect(layer).toBeInstanceOf(VectorLayer);
|
|
120
134
|
});
|
|
@@ -124,7 +138,7 @@ describe("MapContextService", () => {
|
|
|
124
138
|
expect(layer.get("label")).toBeUndefined();
|
|
125
139
|
expect(layer.getSource()?.getAttributions()).toBeNull();
|
|
126
140
|
});
|
|
127
|
-
it("create a
|
|
141
|
+
it("create a vector source", () => {
|
|
128
142
|
const source = layer.getSource();
|
|
129
143
|
expect(source).toBeInstanceOf(VectorSource);
|
|
130
144
|
});
|
|
@@ -135,11 +149,23 @@ describe("MapContextService", () => {
|
|
|
135
149
|
"https://demo.ldproxy.net/zoomstack/collections/airports/items?f=json",
|
|
136
150
|
);
|
|
137
151
|
});
|
|
152
|
+
it("emits a loaded event initially", async () => {
|
|
153
|
+
await vi.runAllTimersAsync();
|
|
154
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
155
|
+
layerState: {
|
|
156
|
+
loaded: true,
|
|
157
|
+
},
|
|
158
|
+
target: layer,
|
|
159
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
160
|
+
});
|
|
161
|
+
});
|
|
138
162
|
});
|
|
139
163
|
describe("WMS", () => {
|
|
140
164
|
beforeEach(async () => {
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
layerModel = MAP_CTX_LAYER_WMS_FIXTURE;
|
|
166
|
+
layer = await createLayer(layerModel);
|
|
167
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
168
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
143
169
|
});
|
|
144
170
|
it("create a tile layer", () => {
|
|
145
171
|
expect(layer).toBeTruthy();
|
|
@@ -189,12 +215,24 @@ describe("MapContextService", () => {
|
|
|
189
215
|
tileLoadFunction(tile, "http://example.com/tile");
|
|
190
216
|
expect(tileLoadErrorCatchFunction).toHaveBeenCalled();
|
|
191
217
|
});
|
|
218
|
+
it("emits a loaded event initially", async () => {
|
|
219
|
+
await vi.runAllTimersAsync();
|
|
220
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
221
|
+
layerState: {
|
|
222
|
+
loaded: true,
|
|
223
|
+
},
|
|
224
|
+
target: layer,
|
|
225
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
226
|
+
});
|
|
227
|
+
});
|
|
192
228
|
});
|
|
193
229
|
|
|
194
230
|
describe("WFS", () => {
|
|
195
231
|
beforeEach(async () => {
|
|
196
|
-
|
|
197
|
-
|
|
232
|
+
layerModel = MAP_CTX_LAYER_WFS_FIXTURE;
|
|
233
|
+
layer = await createLayer(layerModel);
|
|
234
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
235
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
198
236
|
});
|
|
199
237
|
it("create a vector layer", () => {
|
|
200
238
|
expect(layer).toBeTruthy();
|
|
@@ -224,17 +262,15 @@ describe("MapContextService", () => {
|
|
|
224
262
|
"https://www.datagrandest.fr/geoserver/region-grand-est/ows?service=WFS&version=1.1.0&request=GetFeature&outputFormat=application%2Fjson&typename=ms%3Acommune_actuelle_3857&srsname=EPSG%3A3857&bbox=10%2C20%2C30%2C40%2CEPSG%3A3857&maxFeatures=10000",
|
|
225
263
|
);
|
|
226
264
|
});
|
|
227
|
-
it("
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
it("should call handleEndpointError", () => {
|
|
237
|
-
expect(handleEndpointError).toHaveBeenCalled();
|
|
265
|
+
it("emits a loaded event initially", async () => {
|
|
266
|
+
await vi.runAllTimersAsync();
|
|
267
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
268
|
+
layerState: {
|
|
269
|
+
loaded: true,
|
|
270
|
+
},
|
|
271
|
+
target: layer,
|
|
272
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
273
|
+
});
|
|
238
274
|
});
|
|
239
275
|
});
|
|
240
276
|
|
|
@@ -243,6 +279,11 @@ describe("MapContextService", () => {
|
|
|
243
279
|
beforeEach(async () => {
|
|
244
280
|
layerModel = MAP_CTX_LAYER_GEOJSON_FIXTURE;
|
|
245
281
|
layer = await createLayer(layerModel);
|
|
282
|
+
layer.on(
|
|
283
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
284
|
+
eventCallback,
|
|
285
|
+
);
|
|
286
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
246
287
|
});
|
|
247
288
|
it("create a VectorLayer", () => {
|
|
248
289
|
expect(layer).toBeTruthy();
|
|
@@ -264,6 +305,16 @@ describe("MapContextService", () => {
|
|
|
264
305
|
.data as FeatureCollection;
|
|
265
306
|
expect(features.length).toBe(data.features.length);
|
|
266
307
|
});
|
|
308
|
+
it("emits a loaded event", async () => {
|
|
309
|
+
await vi.runAllTimersAsync();
|
|
310
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
311
|
+
layerState: {
|
|
312
|
+
loaded: true,
|
|
313
|
+
},
|
|
314
|
+
target: layer,
|
|
315
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
316
|
+
});
|
|
317
|
+
});
|
|
267
318
|
});
|
|
268
319
|
describe("with inline data as string", () => {
|
|
269
320
|
beforeEach(async () => {
|
|
@@ -316,6 +367,11 @@ describe("MapContextService", () => {
|
|
|
316
367
|
beforeEach(async () => {
|
|
317
368
|
layerModel = MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE;
|
|
318
369
|
layer = await createLayer(layerModel);
|
|
370
|
+
layer.on(
|
|
371
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
372
|
+
eventCallback,
|
|
373
|
+
);
|
|
374
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
319
375
|
});
|
|
320
376
|
it("create a VectorLayer", () => {
|
|
321
377
|
expect(layer).toBeTruthy();
|
|
@@ -335,6 +391,16 @@ describe("MapContextService", () => {
|
|
|
335
391
|
(layerModel as MapContextLayerGeojson).url,
|
|
336
392
|
);
|
|
337
393
|
});
|
|
394
|
+
it("emits a loaded event as well as data info eventually", async () => {
|
|
395
|
+
await vi.runAllTimersAsync();
|
|
396
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
397
|
+
layerState: {
|
|
398
|
+
loaded: true,
|
|
399
|
+
},
|
|
400
|
+
target: layer,
|
|
401
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
402
|
+
});
|
|
403
|
+
});
|
|
338
404
|
});
|
|
339
405
|
});
|
|
340
406
|
|
|
@@ -342,6 +408,8 @@ describe("MapContextService", () => {
|
|
|
342
408
|
beforeEach(async () => {
|
|
343
409
|
layerModel = MAP_CTX_LAYER_WMTS_FIXTURE;
|
|
344
410
|
layer = await createLayer(layerModel);
|
|
411
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
412
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
345
413
|
});
|
|
346
414
|
it("create a tile layer", () => {
|
|
347
415
|
expect(layer).toBeTruthy();
|
|
@@ -366,20 +434,15 @@ describe("MapContextService", () => {
|
|
|
366
434
|
"https://services.geo.sg.ch/wss/service/SG00066_WMTS/guest/tile/1.0.0/SG00066/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}",
|
|
367
435
|
]);
|
|
368
436
|
});
|
|
369
|
-
it("
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
};
|
|
379
|
-
layer = await createLayer(layerModel);
|
|
380
|
-
});
|
|
381
|
-
it("should call handleEndpointError", () => {
|
|
382
|
-
expect(handleEndpointError).toHaveBeenCalled();
|
|
437
|
+
it("emits a loaded event initially", async () => {
|
|
438
|
+
await vi.runAllTimersAsync();
|
|
439
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
440
|
+
layerState: {
|
|
441
|
+
loaded: true,
|
|
442
|
+
},
|
|
443
|
+
target: layer,
|
|
444
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
445
|
+
});
|
|
383
446
|
});
|
|
384
447
|
});
|
|
385
448
|
describe("WMTS without default style given", () => {
|
|
@@ -389,6 +452,8 @@ describe("MapContextService", () => {
|
|
|
389
452
|
url: "https://wmts/no-default-style",
|
|
390
453
|
};
|
|
391
454
|
layer = await createLayer(layerModel);
|
|
455
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
456
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
392
457
|
});
|
|
393
458
|
it("uses the first style as default", async () => {
|
|
394
459
|
const source = layer.getSource() as WMTS;
|
|
@@ -400,6 +465,8 @@ describe("MapContextService", () => {
|
|
|
400
465
|
beforeEach(async () => {
|
|
401
466
|
layerModel = MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE;
|
|
402
467
|
layer = await createLayer(layerModel);
|
|
468
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
469
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
403
470
|
});
|
|
404
471
|
it("create a tile layer", () => {
|
|
405
472
|
expect(layer).toBeTruthy();
|
|
@@ -414,12 +481,24 @@ describe("MapContextService", () => {
|
|
|
414
481
|
const source = layer.getSource();
|
|
415
482
|
expect(source).toBeInstanceOf(VectorTile);
|
|
416
483
|
});
|
|
484
|
+
it("emits a loaded event initially", async () => {
|
|
485
|
+
await vi.runAllTimersAsync();
|
|
486
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
487
|
+
layerState: {
|
|
488
|
+
loaded: true,
|
|
489
|
+
},
|
|
490
|
+
target: layer,
|
|
491
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
492
|
+
});
|
|
493
|
+
});
|
|
417
494
|
});
|
|
418
495
|
|
|
419
496
|
describe("MVT", () => {
|
|
420
497
|
beforeEach(async () => {
|
|
421
498
|
layerModel = MAP_CTX_LAYER_MVT_FIXTURE;
|
|
422
499
|
layer = await createLayer(layerModel);
|
|
500
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
501
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
423
502
|
});
|
|
424
503
|
it("create a VectorTileLayer", () => {
|
|
425
504
|
expect(layer).toBeTruthy();
|
|
@@ -434,6 +513,16 @@ describe("MapContextService", () => {
|
|
|
434
513
|
expect(layer.getOpacity()).toBe(1);
|
|
435
514
|
expect(layer.get("label")).toBeUndefined();
|
|
436
515
|
});
|
|
516
|
+
it("emits a loaded event initially", async () => {
|
|
517
|
+
await vi.runAllTimersAsync();
|
|
518
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
519
|
+
layerState: {
|
|
520
|
+
loaded: true,
|
|
521
|
+
},
|
|
522
|
+
target: layer,
|
|
523
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
524
|
+
});
|
|
525
|
+
});
|
|
437
526
|
});
|
|
438
527
|
|
|
439
528
|
describe("GeoTIFF", () => {
|
package/lib/map/create-map.ts
CHANGED
|
@@ -25,16 +25,14 @@ import OGCVectorTile from "ol/source/OGCVectorTile.js";
|
|
|
25
25
|
import WMTS from "ol/source/WMTS.js";
|
|
26
26
|
import MVT from "ol/format/MVT.js";
|
|
27
27
|
import {
|
|
28
|
+
EndpointError,
|
|
28
29
|
OgcApiEndpoint,
|
|
29
30
|
WfsEndpoint,
|
|
30
31
|
WmtsEndpoint,
|
|
31
32
|
} from "@camptocamp/ogc-client";
|
|
32
33
|
import { MapboxVectorLayer } from "ol-mapbox-style";
|
|
33
34
|
import { Tile } from "ol";
|
|
34
|
-
import {
|
|
35
|
-
handleEndpointError,
|
|
36
|
-
tileLoadErrorCatchFunction,
|
|
37
|
-
} from "./handle-errors.js";
|
|
35
|
+
import { tileLoadErrorCatchFunction } from "./handle-errors.js";
|
|
38
36
|
import VectorTile from "ol/source/VectorTile.js";
|
|
39
37
|
import GeoTIFF from "ol/source/GeoTIFF.js";
|
|
40
38
|
import WebGLTileLayer from "ol/layer/WebGLTile.js";
|
|
@@ -45,6 +43,13 @@ import {
|
|
|
45
43
|
updateLayerProperties,
|
|
46
44
|
} from "./layer-update.js";
|
|
47
45
|
import { initHoverLayer } from "./feature-hover.js";
|
|
46
|
+
import {
|
|
47
|
+
emitLayerCreationError,
|
|
48
|
+
emitLayerLoadingError,
|
|
49
|
+
emitLayerLoadingStatusSuccess,
|
|
50
|
+
propagateLayerStateChangeEventToMap,
|
|
51
|
+
} from "./register-events.js";
|
|
52
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
48
53
|
|
|
49
54
|
// Register proj4 with OpenLayers so that arbitrary EPSG codes
|
|
50
55
|
// (e.g., UTM zones from GeoTIFF metadata) can be reprojected to the map projection
|
|
@@ -53,9 +58,15 @@ register(proj4);
|
|
|
53
58
|
const GEOJSON = new GeoJSON();
|
|
54
59
|
const WFS_MAX_FEATURES = 10000;
|
|
55
60
|
|
|
61
|
+
// We need to defer some events being dispatched to make sure they are caught by the map
|
|
62
|
+
// where the layers sit
|
|
63
|
+
// FIXME: this should be better handled in a separate module!
|
|
64
|
+
const defer = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
65
|
+
|
|
56
66
|
export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
57
67
|
const { type } = layerModel;
|
|
58
|
-
let layer: Layer
|
|
68
|
+
let layer: Layer;
|
|
69
|
+
|
|
59
70
|
switch (type) {
|
|
60
71
|
case "xyz":
|
|
61
72
|
{
|
|
@@ -82,8 +93,10 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
82
93
|
});
|
|
83
94
|
layer.setSource(source);
|
|
84
95
|
}
|
|
96
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
85
97
|
}
|
|
86
98
|
break;
|
|
99
|
+
|
|
87
100
|
case "wms":
|
|
88
101
|
{
|
|
89
102
|
layer = new TileLayer({});
|
|
@@ -104,9 +117,10 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
104
117
|
);
|
|
105
118
|
});
|
|
106
119
|
layer.setSource(source);
|
|
120
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
107
121
|
}
|
|
108
|
-
|
|
109
122
|
break;
|
|
123
|
+
|
|
110
124
|
case "wmts": {
|
|
111
125
|
const olLayer = new TileLayer({});
|
|
112
126
|
const endpoint = new WmtsEndpoint(layerModel.url);
|
|
@@ -138,11 +152,16 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
138
152
|
}),
|
|
139
153
|
);
|
|
140
154
|
})
|
|
155
|
+
.then(() => emitLayerLoadingStatusSuccess(olLayer))
|
|
141
156
|
.catch((e) => {
|
|
142
|
-
|
|
157
|
+
const httpStatus =
|
|
158
|
+
e instanceof EndpointError ? e.httpStatus : undefined;
|
|
159
|
+
emitLayerLoadingError(olLayer, e, httpStatus);
|
|
143
160
|
});
|
|
144
|
-
|
|
161
|
+
layer = olLayer;
|
|
162
|
+
break;
|
|
145
163
|
}
|
|
164
|
+
|
|
146
165
|
case "wfs": {
|
|
147
166
|
const olLayer = new VectorLayer({
|
|
148
167
|
style: layerModel.style ?? defaultStyle,
|
|
@@ -169,28 +188,35 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
169
188
|
}),
|
|
170
189
|
);
|
|
171
190
|
})
|
|
191
|
+
.then(() => emitLayerLoadingStatusSuccess(olLayer))
|
|
172
192
|
.catch((e) => {
|
|
173
|
-
|
|
193
|
+
const httpStatus =
|
|
194
|
+
e instanceof EndpointError ? e.httpStatus : undefined;
|
|
195
|
+
emitLayerLoadingError(olLayer, e, httpStatus);
|
|
174
196
|
});
|
|
175
197
|
layer = olLayer;
|
|
176
198
|
break;
|
|
177
199
|
}
|
|
200
|
+
|
|
178
201
|
case "maplibre-style": {
|
|
179
202
|
layer = new MapboxVectorLayer({
|
|
180
203
|
styleUrl: layerModel.styleUrl,
|
|
181
204
|
accessToken: layerModel.accessToken,
|
|
182
205
|
}) as unknown as Layer;
|
|
206
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
183
207
|
break;
|
|
184
208
|
}
|
|
209
|
+
|
|
185
210
|
case "geojson": {
|
|
211
|
+
layer = new VectorLayer({
|
|
212
|
+
style: layerModel.style ?? defaultStyle,
|
|
213
|
+
});
|
|
214
|
+
let source: VectorSource;
|
|
186
215
|
if (layerModel.url !== undefined) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
attributions: layerModel.attributions,
|
|
192
|
-
}),
|
|
193
|
-
style: layerModel.style ?? defaultStyle,
|
|
216
|
+
source = new VectorSource({
|
|
217
|
+
format: new GeoJSON(),
|
|
218
|
+
url: layerModel.url,
|
|
219
|
+
attributions: layerModel.attributions,
|
|
194
220
|
});
|
|
195
221
|
} else {
|
|
196
222
|
let geojson = layerModel.data;
|
|
@@ -206,16 +232,17 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
206
232
|
featureProjection: "EPSG:3857",
|
|
207
233
|
dataProjection: "EPSG:4326",
|
|
208
234
|
}) as Feature<Geometry>[];
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
attributions: layerModel.attributions,
|
|
213
|
-
}),
|
|
214
|
-
style: layerModel.style ?? defaultStyle,
|
|
235
|
+
source = new VectorSource({
|
|
236
|
+
features,
|
|
237
|
+
attributions: layerModel.attributions,
|
|
215
238
|
});
|
|
216
239
|
}
|
|
240
|
+
layer.setSource(source);
|
|
241
|
+
// FIXME: actually track layer loading and data info
|
|
242
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
217
243
|
break;
|
|
218
244
|
}
|
|
245
|
+
|
|
219
246
|
case "ogcapi": {
|
|
220
247
|
const ogcEndpoint = new OgcApiEndpoint(layerModel.url);
|
|
221
248
|
let layerUrl: string;
|
|
@@ -249,17 +276,21 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
249
276
|
layerModel.collection,
|
|
250
277
|
layerModel.options,
|
|
251
278
|
);
|
|
279
|
+
const source = new VectorSource({
|
|
280
|
+
format: new GeoJSON(),
|
|
281
|
+
url: layerUrl,
|
|
282
|
+
attributions: layerModel.attributions,
|
|
283
|
+
});
|
|
252
284
|
layer = new VectorLayer({
|
|
253
|
-
source
|
|
254
|
-
format: new GeoJSON(),
|
|
255
|
-
url: layerUrl,
|
|
256
|
-
attributions: layerModel.attributions,
|
|
257
|
-
}),
|
|
285
|
+
source,
|
|
258
286
|
style: layerModel.style ?? defaultStyle,
|
|
259
287
|
});
|
|
260
288
|
}
|
|
289
|
+
// FIXME: actually track layer loading
|
|
290
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
261
291
|
break;
|
|
262
292
|
}
|
|
293
|
+
|
|
263
294
|
case "geotiff": {
|
|
264
295
|
const geoTiffSource = new GeoTIFF({
|
|
265
296
|
sources: [{ url: layerModel.url }],
|
|
@@ -268,26 +299,38 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
268
299
|
layer = new WebGLTileLayer({
|
|
269
300
|
source: geoTiffSource,
|
|
270
301
|
});
|
|
302
|
+
// FIXME: actually track tile loading
|
|
303
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
271
304
|
break;
|
|
272
305
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
306
|
+
|
|
307
|
+
default: {
|
|
308
|
+
// we create an empty placeholder layer so that we still have a corresponding layer in OL
|
|
309
|
+
layer = new VectorLayer({
|
|
310
|
+
properties: {
|
|
311
|
+
[`${GEOSPATIAL_SDK_PREFIX}layer-with-error`]: true,
|
|
312
|
+
},
|
|
313
|
+
});
|
|
314
|
+
defer().then(() =>
|
|
315
|
+
emitLayerCreationError(
|
|
316
|
+
layer,
|
|
317
|
+
new Error(`Unrecognized layer type: ${JSON.stringify(layerModel)}`),
|
|
318
|
+
),
|
|
319
|
+
);
|
|
320
|
+
}
|
|
278
321
|
}
|
|
279
322
|
|
|
280
|
-
updateLayerProperties(layerModel, layer);
|
|
323
|
+
updateLayerProperties(layerModel, layer!);
|
|
281
324
|
|
|
282
|
-
return layer
|
|
325
|
+
return layer!;
|
|
283
326
|
}
|
|
284
327
|
|
|
285
|
-
export function updateLayerInMap(
|
|
328
|
+
export async function updateLayerInMap(
|
|
286
329
|
map: Map,
|
|
287
330
|
layerModel: MapContextLayer,
|
|
288
331
|
layerPosition: number,
|
|
289
332
|
previousLayerModel: MapContextLayer,
|
|
290
|
-
) {
|
|
333
|
+
): Promise<void> {
|
|
291
334
|
const layers = map.getLayers();
|
|
292
335
|
const updatedLayer = layers.item(layerPosition) as Layer;
|
|
293
336
|
|
|
@@ -299,8 +342,9 @@ export function updateLayerInMap(
|
|
|
299
342
|
|
|
300
343
|
// dispose and recreate layer
|
|
301
344
|
updatedLayer.dispose();
|
|
302
|
-
createLayer(layerModel).then((layer) => {
|
|
345
|
+
await createLayer(layerModel).then((layer) => {
|
|
303
346
|
layers.setAt(layerPosition, layer);
|
|
347
|
+
propagateLayerStateChangeEventToMap(map, layer);
|
|
304
348
|
});
|
|
305
349
|
}
|
|
306
350
|
|
|
@@ -369,6 +413,7 @@ export async function resetMapFromContext(
|
|
|
369
413
|
for (const layerModel of context.layers) {
|
|
370
414
|
const layer = await createLayer(layerModel);
|
|
371
415
|
map.addLayer(layer);
|
|
416
|
+
propagateLayerStateChangeEventToMap(map, layer);
|
|
372
417
|
}
|
|
373
418
|
initHoverLayer(map);
|
|
374
419
|
return map;
|
package/lib/map/feature-hover.ts
CHANGED
|
@@ -108,7 +108,7 @@ export function initHoverLayer(map: OlMap) {
|
|
|
108
108
|
);
|
|
109
109
|
const features = Array.from(featuresByLayer.values()).flat();
|
|
110
110
|
map.dispatchEvent({
|
|
111
|
-
type: FeaturesHoverEventType
|
|
111
|
+
type: `${GEOSPATIAL_SDK_PREFIX}${FeaturesHoverEventType}`,
|
|
112
112
|
features,
|
|
113
113
|
featuresByLayer,
|
|
114
114
|
} as unknown as BaseEvent);
|
|
@@ -1,13 +1,6 @@
|
|
|
1
|
-
import { ImageTile, Tile } from "ol";
|
|
2
|
-
import TileState from "ol/TileState.js";
|
|
3
|
-
import { SourceLoadErrorEvent } from "@geospatial-sdk/core";
|
|
4
|
-
import {
|
|
5
|
-
handleTileError,
|
|
6
|
-
tileLoadErrorCatchFunction,
|
|
7
|
-
} from "./handle-errors.js";
|
|
8
1
|
import TileLayer from "ol/layer/Tile.js";
|
|
9
2
|
import VectorLayer from "ol/layer/Vector.js";
|
|
10
|
-
import {
|
|
3
|
+
import { emitLayerLoadingError } from "./register-events.js";
|
|
11
4
|
|
|
12
5
|
globalThis.URL.createObjectURL = vi.fn(() => "blob:http://example.com/blob");
|
|
13
6
|
|
|
@@ -26,45 +19,29 @@ globalThis.fetch = vi.fn().mockImplementation((url: string) => {
|
|
|
26
19
|
});
|
|
27
20
|
|
|
28
21
|
describe("handle-errors", () => {
|
|
29
|
-
let tile: Tile;
|
|
30
|
-
|
|
31
|
-
beforeEach(() => {
|
|
32
|
-
tile = new ImageTile(
|
|
33
|
-
[0, 0, 0],
|
|
34
|
-
TileState.IDLE,
|
|
35
|
-
"",
|
|
36
|
-
null,
|
|
37
|
-
() => tileLoadErrorCatchFunction,
|
|
38
|
-
);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
22
|
describe("handleEndpointError", () => {
|
|
42
23
|
it("should dispatch SourceLoadErrorEvent", () => {
|
|
43
|
-
const endpointErrorMock: EndpointError = {
|
|
44
|
-
name: "Error",
|
|
45
|
-
message: "FORBIDDEN",
|
|
46
|
-
httpStatus: 403,
|
|
47
|
-
};
|
|
48
24
|
const layer = new VectorLayer({});
|
|
49
25
|
const dispatchEventSpy = vi.spyOn(layer, "dispatchEvent");
|
|
50
|
-
|
|
51
|
-
expect(dispatchEventSpy).toHaveBeenCalledWith(
|
|
52
|
-
|
|
53
|
-
|
|
26
|
+
emitLayerLoadingError(layer, new Error("FORBIDDEN"), 403);
|
|
27
|
+
expect(dispatchEventSpy).toHaveBeenCalledWith({
|
|
28
|
+
type: "--geospatial-sdk-layer-loading-error",
|
|
29
|
+
error: new Error("FORBIDDEN"),
|
|
30
|
+
httpStatus: 403,
|
|
31
|
+
});
|
|
54
32
|
});
|
|
55
33
|
});
|
|
56
34
|
|
|
57
35
|
describe("handleTileError", () => {
|
|
58
36
|
it("should set tile state to ERROR and dispatch SourceLoadErrorEvent", () => {
|
|
59
|
-
const response = new Response("Forbidden", { status: 403 });
|
|
60
37
|
const layer = new TileLayer({});
|
|
61
38
|
const dispatchEventSpy = vi.spyOn(layer, "dispatchEvent");
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
);
|
|
39
|
+
emitLayerLoadingError(layer, new Error("Forbidden"), 403);
|
|
40
|
+
expect(dispatchEventSpy).toHaveBeenCalledWith({
|
|
41
|
+
type: "--geospatial-sdk-layer-loading-error",
|
|
42
|
+
error: new Error("Forbidden"),
|
|
43
|
+
httpStatus: 403,
|
|
44
|
+
});
|
|
68
45
|
});
|
|
69
46
|
});
|
|
70
47
|
});
|