@geospatial-sdk/openlayers 0.0.5-dev.55 → 0.0.5-dev.57
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 +85 -44
- 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 +178 -40
- package/lib/map/create-map.ts +114 -55
- 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,10 @@ import {
|
|
|
44
44
|
createView,
|
|
45
45
|
resetMapFromContext,
|
|
46
46
|
} from "./create-map.js";
|
|
47
|
-
import {
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
47
|
+
import { tileLoadErrorCatchFunction } from "./handle-errors.js";
|
|
48
|
+
import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
|
|
49
|
+
import ImageLayer from "ol/layer/Image.js";
|
|
50
|
+
import ImageWMS from "ol/source/ImageWMS.js";
|
|
51
51
|
|
|
52
52
|
vi.mock("./handle-errors", async (importOriginal) => {
|
|
53
53
|
const actual =
|
|
@@ -59,18 +59,22 @@ vi.mock("./handle-errors", async (importOriginal) => {
|
|
|
59
59
|
};
|
|
60
60
|
});
|
|
61
61
|
|
|
62
|
-
|
|
62
|
+
beforeEach(() => {
|
|
63
|
+
vi.useFakeTimers();
|
|
63
64
|
vi.clearAllMocks();
|
|
64
65
|
});
|
|
65
66
|
|
|
66
67
|
describe("MapContextService", () => {
|
|
67
68
|
describe("#createLayer", () => {
|
|
68
69
|
let layerModel: MapContextLayer, layer: Layer;
|
|
70
|
+
const eventCallback = vi.fn();
|
|
69
71
|
|
|
70
72
|
describe("XYZ", () => {
|
|
71
73
|
beforeEach(async () => {
|
|
72
74
|
layerModel = MAP_CTX_LAYER_XYZ_FIXTURE;
|
|
73
75
|
layer = await createLayer(layerModel);
|
|
76
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
77
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
74
78
|
});
|
|
75
79
|
it("create a tile layer", () => {
|
|
76
80
|
expect(layer).toBeTruthy();
|
|
@@ -108,13 +112,25 @@ describe("MapContextService", () => {
|
|
|
108
112
|
tileLoadFunction(tile, "http://example.com/tile");
|
|
109
113
|
expect(tileLoadErrorCatchFunction).toHaveBeenCalled();
|
|
110
114
|
});
|
|
115
|
+
it("emits a loaded event initially", async () => {
|
|
116
|
+
await vi.runAllTimersAsync();
|
|
117
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
118
|
+
layerState: {
|
|
119
|
+
loaded: true,
|
|
120
|
+
},
|
|
121
|
+
target: layer,
|
|
122
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
123
|
+
});
|
|
124
|
+
});
|
|
111
125
|
});
|
|
112
|
-
describe("OGCAPI", () => {
|
|
126
|
+
describe("OGCAPI (as geojson items)", () => {
|
|
113
127
|
beforeEach(async () => {
|
|
114
128
|
layerModel = MAP_CTX_LAYER_OGCAPI_FIXTURE;
|
|
115
129
|
layer = await createLayer(layerModel);
|
|
130
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
131
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
116
132
|
});
|
|
117
|
-
it("create a vector
|
|
133
|
+
it("create a vector layer", () => {
|
|
118
134
|
expect(layer).toBeTruthy();
|
|
119
135
|
expect(layer).toBeInstanceOf(VectorLayer);
|
|
120
136
|
});
|
|
@@ -124,7 +140,7 @@ describe("MapContextService", () => {
|
|
|
124
140
|
expect(layer.get("label")).toBeUndefined();
|
|
125
141
|
expect(layer.getSource()?.getAttributions()).toBeNull();
|
|
126
142
|
});
|
|
127
|
-
it("create a
|
|
143
|
+
it("create a vector source", () => {
|
|
128
144
|
const source = layer.getSource();
|
|
129
145
|
expect(source).toBeInstanceOf(VectorSource);
|
|
130
146
|
});
|
|
@@ -135,11 +151,23 @@ describe("MapContextService", () => {
|
|
|
135
151
|
"https://demo.ldproxy.net/zoomstack/collections/airports/items?f=json",
|
|
136
152
|
);
|
|
137
153
|
});
|
|
154
|
+
it("emits a loaded event initially", async () => {
|
|
155
|
+
await vi.runAllTimersAsync();
|
|
156
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
157
|
+
layerState: {
|
|
158
|
+
loaded: true,
|
|
159
|
+
},
|
|
160
|
+
target: layer,
|
|
161
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
162
|
+
});
|
|
163
|
+
});
|
|
138
164
|
});
|
|
139
165
|
describe("WMS", () => {
|
|
140
166
|
beforeEach(async () => {
|
|
141
|
-
|
|
142
|
-
|
|
167
|
+
layerModel = MAP_CTX_LAYER_WMS_FIXTURE;
|
|
168
|
+
layer = await createLayer(layerModel);
|
|
169
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
170
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
143
171
|
});
|
|
144
172
|
it("create a tile layer", () => {
|
|
145
173
|
expect(layer).toBeTruthy();
|
|
@@ -159,8 +187,11 @@ describe("MapContextService", () => {
|
|
|
159
187
|
it("set correct WMS params", () => {
|
|
160
188
|
const source = layer.getSource() as TileWMS;
|
|
161
189
|
const params = source.getParams();
|
|
162
|
-
expect(params
|
|
163
|
-
|
|
190
|
+
expect(params).toEqual({
|
|
191
|
+
LAYERS: (layerModel as MapContextLayerWms).name,
|
|
192
|
+
STYLES: (layerModel as MapContextLayerWms).style,
|
|
193
|
+
TILED: true,
|
|
194
|
+
});
|
|
164
195
|
});
|
|
165
196
|
it("set correct url without existing REQUEST and SERVICE params", () => {
|
|
166
197
|
const source = layer.getSource() as TileWMS;
|
|
@@ -189,12 +220,68 @@ describe("MapContextService", () => {
|
|
|
189
220
|
tileLoadFunction(tile, "http://example.com/tile");
|
|
190
221
|
expect(tileLoadErrorCatchFunction).toHaveBeenCalled();
|
|
191
222
|
});
|
|
223
|
+
it("emits a loaded event initially", async () => {
|
|
224
|
+
await vi.runAllTimersAsync();
|
|
225
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
226
|
+
layerState: {
|
|
227
|
+
loaded: true,
|
|
228
|
+
},
|
|
229
|
+
target: layer,
|
|
230
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
231
|
+
});
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
describe("not using tiles", () => {
|
|
235
|
+
beforeEach(async () => {
|
|
236
|
+
layerModel = { ...MAP_CTX_LAYER_WMS_FIXTURE, useTiles: false };
|
|
237
|
+
layer = await createLayer(layerModel);
|
|
238
|
+
layer.on(
|
|
239
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
240
|
+
eventCallback,
|
|
241
|
+
);
|
|
242
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
243
|
+
});
|
|
244
|
+
it("create an image layer", () => {
|
|
245
|
+
expect(layer).toBeTruthy();
|
|
246
|
+
expect(layer).toBeInstanceOf(ImageLayer);
|
|
247
|
+
});
|
|
248
|
+
it("set correct layer properties", () => {
|
|
249
|
+
expect(layer.getVisible()).toBe(false);
|
|
250
|
+
expect(layer.getOpacity()).toBe(0.5);
|
|
251
|
+
expect(layer.get("label")).toBe("Communes");
|
|
252
|
+
// @ts-expect-error TS2554 we're not providing a view extent here
|
|
253
|
+
expect(layer.getSource()?.getAttributions()!()).toEqual([
|
|
254
|
+
"camptocamp",
|
|
255
|
+
]);
|
|
256
|
+
});
|
|
257
|
+
it("create an ImageWMS source", () => {
|
|
258
|
+
const source = layer.getSource();
|
|
259
|
+
expect(source).toBeInstanceOf(ImageWMS);
|
|
260
|
+
});
|
|
261
|
+
it("set correct WMS params", () => {
|
|
262
|
+
const source = layer.getSource() as ImageWMS;
|
|
263
|
+
const params = source.getParams();
|
|
264
|
+
expect(params).toEqual({
|
|
265
|
+
LAYERS: (layerModel as MapContextLayerWms).name,
|
|
266
|
+
STYLES: (layerModel as MapContextLayerWms).style,
|
|
267
|
+
});
|
|
268
|
+
});
|
|
269
|
+
it("set correct url without existing REQUEST and SERVICE params", () => {
|
|
270
|
+
const source = layer.getSource() as ImageWMS;
|
|
271
|
+
const url = source.getUrl();
|
|
272
|
+
expect(url).toBe(
|
|
273
|
+
"https://www.datagrandest.fr/geoserver/region-grand-est/ows",
|
|
274
|
+
);
|
|
275
|
+
});
|
|
276
|
+
});
|
|
192
277
|
});
|
|
193
278
|
|
|
194
279
|
describe("WFS", () => {
|
|
195
280
|
beforeEach(async () => {
|
|
196
|
-
|
|
197
|
-
|
|
281
|
+
layerModel = MAP_CTX_LAYER_WFS_FIXTURE;
|
|
282
|
+
layer = await createLayer(layerModel);
|
|
283
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
284
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
198
285
|
});
|
|
199
286
|
it("create a vector layer", () => {
|
|
200
287
|
expect(layer).toBeTruthy();
|
|
@@ -224,17 +311,15 @@ describe("MapContextService", () => {
|
|
|
224
311
|
"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
312
|
);
|
|
226
313
|
});
|
|
227
|
-
it("
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
it("should call handleEndpointError", () => {
|
|
237
|
-
expect(handleEndpointError).toHaveBeenCalled();
|
|
314
|
+
it("emits a loaded event initially", async () => {
|
|
315
|
+
await vi.runAllTimersAsync();
|
|
316
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
317
|
+
layerState: {
|
|
318
|
+
loaded: true,
|
|
319
|
+
},
|
|
320
|
+
target: layer,
|
|
321
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
322
|
+
});
|
|
238
323
|
});
|
|
239
324
|
});
|
|
240
325
|
|
|
@@ -243,6 +328,11 @@ describe("MapContextService", () => {
|
|
|
243
328
|
beforeEach(async () => {
|
|
244
329
|
layerModel = MAP_CTX_LAYER_GEOJSON_FIXTURE;
|
|
245
330
|
layer = await createLayer(layerModel);
|
|
331
|
+
layer.on(
|
|
332
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
333
|
+
eventCallback,
|
|
334
|
+
);
|
|
335
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
246
336
|
});
|
|
247
337
|
it("create a VectorLayer", () => {
|
|
248
338
|
expect(layer).toBeTruthy();
|
|
@@ -264,6 +354,16 @@ describe("MapContextService", () => {
|
|
|
264
354
|
.data as FeatureCollection;
|
|
265
355
|
expect(features.length).toBe(data.features.length);
|
|
266
356
|
});
|
|
357
|
+
it("emits a loaded event", async () => {
|
|
358
|
+
await vi.runAllTimersAsync();
|
|
359
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
360
|
+
layerState: {
|
|
361
|
+
loaded: true,
|
|
362
|
+
},
|
|
363
|
+
target: layer,
|
|
364
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
365
|
+
});
|
|
366
|
+
});
|
|
267
367
|
});
|
|
268
368
|
describe("with inline data as string", () => {
|
|
269
369
|
beforeEach(async () => {
|
|
@@ -316,6 +416,11 @@ describe("MapContextService", () => {
|
|
|
316
416
|
beforeEach(async () => {
|
|
317
417
|
layerModel = MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE;
|
|
318
418
|
layer = await createLayer(layerModel);
|
|
419
|
+
layer.on(
|
|
420
|
+
`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
421
|
+
eventCallback,
|
|
422
|
+
);
|
|
423
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
319
424
|
});
|
|
320
425
|
it("create a VectorLayer", () => {
|
|
321
426
|
expect(layer).toBeTruthy();
|
|
@@ -335,6 +440,16 @@ describe("MapContextService", () => {
|
|
|
335
440
|
(layerModel as MapContextLayerGeojson).url,
|
|
336
441
|
);
|
|
337
442
|
});
|
|
443
|
+
it("emits a loaded event as well as data info eventually", async () => {
|
|
444
|
+
await vi.runAllTimersAsync();
|
|
445
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
446
|
+
layerState: {
|
|
447
|
+
loaded: true,
|
|
448
|
+
},
|
|
449
|
+
target: layer,
|
|
450
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
451
|
+
});
|
|
452
|
+
});
|
|
338
453
|
});
|
|
339
454
|
});
|
|
340
455
|
|
|
@@ -342,6 +457,8 @@ describe("MapContextService", () => {
|
|
|
342
457
|
beforeEach(async () => {
|
|
343
458
|
layerModel = MAP_CTX_LAYER_WMTS_FIXTURE;
|
|
344
459
|
layer = await createLayer(layerModel);
|
|
460
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
461
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
345
462
|
});
|
|
346
463
|
it("create a tile layer", () => {
|
|
347
464
|
expect(layer).toBeTruthy();
|
|
@@ -366,20 +483,15 @@ describe("MapContextService", () => {
|
|
|
366
483
|
"https://services.geo.sg.ch/wss/service/SG00066_WMTS/guest/tile/1.0.0/SG00066/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}",
|
|
367
484
|
]);
|
|
368
485
|
});
|
|
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();
|
|
486
|
+
it("emits a loaded event initially", async () => {
|
|
487
|
+
await vi.runAllTimersAsync();
|
|
488
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
489
|
+
layerState: {
|
|
490
|
+
loaded: true,
|
|
491
|
+
},
|
|
492
|
+
target: layer,
|
|
493
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
494
|
+
});
|
|
383
495
|
});
|
|
384
496
|
});
|
|
385
497
|
describe("WMTS without default style given", () => {
|
|
@@ -389,6 +501,8 @@ describe("MapContextService", () => {
|
|
|
389
501
|
url: "https://wmts/no-default-style",
|
|
390
502
|
};
|
|
391
503
|
layer = await createLayer(layerModel);
|
|
504
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
505
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
392
506
|
});
|
|
393
507
|
it("uses the first style as default", async () => {
|
|
394
508
|
const source = layer.getSource() as WMTS;
|
|
@@ -400,6 +514,8 @@ describe("MapContextService", () => {
|
|
|
400
514
|
beforeEach(async () => {
|
|
401
515
|
layerModel = MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE;
|
|
402
516
|
layer = await createLayer(layerModel);
|
|
517
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
518
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
403
519
|
});
|
|
404
520
|
it("create a tile layer", () => {
|
|
405
521
|
expect(layer).toBeTruthy();
|
|
@@ -414,12 +530,24 @@ describe("MapContextService", () => {
|
|
|
414
530
|
const source = layer.getSource();
|
|
415
531
|
expect(source).toBeInstanceOf(VectorTile);
|
|
416
532
|
});
|
|
533
|
+
it("emits a loaded event initially", async () => {
|
|
534
|
+
await vi.runAllTimersAsync();
|
|
535
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
536
|
+
layerState: {
|
|
537
|
+
loaded: true,
|
|
538
|
+
},
|
|
539
|
+
target: layer,
|
|
540
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
541
|
+
});
|
|
542
|
+
});
|
|
417
543
|
});
|
|
418
544
|
|
|
419
545
|
describe("MVT", () => {
|
|
420
546
|
beforeEach(async () => {
|
|
421
547
|
layerModel = MAP_CTX_LAYER_MVT_FIXTURE;
|
|
422
548
|
layer = await createLayer(layerModel);
|
|
549
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-loading-status`, eventCallback);
|
|
550
|
+
layer.on(`${GEOSPATIAL_SDK_PREFIX}layer-data-info`, eventCallback);
|
|
423
551
|
});
|
|
424
552
|
it("create a VectorTileLayer", () => {
|
|
425
553
|
expect(layer).toBeTruthy();
|
|
@@ -434,6 +562,16 @@ describe("MapContextService", () => {
|
|
|
434
562
|
expect(layer.getOpacity()).toBe(1);
|
|
435
563
|
expect(layer.get("label")).toBeUndefined();
|
|
436
564
|
});
|
|
565
|
+
it("emits a loaded event initially", async () => {
|
|
566
|
+
await vi.runAllTimersAsync();
|
|
567
|
+
expect(eventCallback).toHaveBeenCalledWith({
|
|
568
|
+
layerState: {
|
|
569
|
+
loaded: true,
|
|
570
|
+
},
|
|
571
|
+
target: layer,
|
|
572
|
+
type: `${GEOSPATIAL_SDK_PREFIX}layer-loading-status`,
|
|
573
|
+
});
|
|
574
|
+
});
|
|
437
575
|
});
|
|
438
576
|
|
|
439
577
|
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,15 @@ 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";
|
|
53
|
+
import ImageLayer from "ol/layer/Image.js";
|
|
54
|
+
import ImageWMS from "ol/source/ImageWMS.js";
|
|
48
55
|
|
|
49
56
|
// Register proj4 with OpenLayers so that arbitrary EPSG codes
|
|
50
57
|
// (e.g., UTM zones from GeoTIFF metadata) can be reprojected to the map projection
|
|
@@ -53,9 +60,15 @@ register(proj4);
|
|
|
53
60
|
const GEOJSON = new GeoJSON();
|
|
54
61
|
const WFS_MAX_FEATURES = 10000;
|
|
55
62
|
|
|
63
|
+
// We need to defer some events being dispatched to make sure they are caught by the map
|
|
64
|
+
// where the layers sit
|
|
65
|
+
// FIXME: this should be better handled in a separate module!
|
|
66
|
+
const defer = () => new Promise((resolve) => setTimeout(resolve, 0));
|
|
67
|
+
|
|
56
68
|
export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
57
69
|
const { type } = layerModel;
|
|
58
|
-
let layer: Layer
|
|
70
|
+
let layer: Layer;
|
|
71
|
+
|
|
59
72
|
switch (type) {
|
|
60
73
|
case "xyz":
|
|
61
74
|
{
|
|
@@ -82,31 +95,46 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
82
95
|
});
|
|
83
96
|
layer.setSource(source);
|
|
84
97
|
}
|
|
98
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
85
99
|
}
|
|
86
100
|
break;
|
|
101
|
+
|
|
87
102
|
case "wms":
|
|
88
103
|
{
|
|
89
|
-
|
|
90
|
-
const
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
104
|
+
const url = removeSearchParams(layerModel.url, ["request", "service"]);
|
|
105
|
+
const params = {
|
|
106
|
+
LAYERS: layerModel.name,
|
|
107
|
+
...(layerModel.style && { STYLES: layerModel.style }),
|
|
108
|
+
};
|
|
109
|
+
if (layerModel.useTiles === false) {
|
|
110
|
+
layer = new ImageLayer({
|
|
111
|
+
source: new ImageWMS({
|
|
112
|
+
url,
|
|
113
|
+
params,
|
|
114
|
+
attributions: layerModel.attributions,
|
|
115
|
+
}),
|
|
116
|
+
});
|
|
117
|
+
} else {
|
|
118
|
+
layer = new TileLayer({
|
|
119
|
+
source: new TileWMS({
|
|
120
|
+
url,
|
|
121
|
+
params: { ...params, TILED: true },
|
|
122
|
+
gutter: 20,
|
|
123
|
+
attributions: layerModel.attributions,
|
|
124
|
+
tileLoadFunction: function (tile: Tile, src: string) {
|
|
125
|
+
return tileLoadErrorCatchFunction(
|
|
126
|
+
layer as TileLayer<TileWMS>,
|
|
127
|
+
tile,
|
|
128
|
+
src,
|
|
129
|
+
);
|
|
130
|
+
},
|
|
131
|
+
}),
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
107
135
|
}
|
|
108
|
-
|
|
109
136
|
break;
|
|
137
|
+
|
|
110
138
|
case "wmts": {
|
|
111
139
|
const olLayer = new TileLayer({});
|
|
112
140
|
const endpoint = new WmtsEndpoint(layerModel.url);
|
|
@@ -138,11 +166,16 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
138
166
|
}),
|
|
139
167
|
);
|
|
140
168
|
})
|
|
169
|
+
.then(() => emitLayerLoadingStatusSuccess(olLayer))
|
|
141
170
|
.catch((e) => {
|
|
142
|
-
|
|
171
|
+
const httpStatus =
|
|
172
|
+
e instanceof EndpointError ? e.httpStatus : undefined;
|
|
173
|
+
emitLayerLoadingError(olLayer, e, httpStatus);
|
|
143
174
|
});
|
|
144
|
-
|
|
175
|
+
layer = olLayer;
|
|
176
|
+
break;
|
|
145
177
|
}
|
|
178
|
+
|
|
146
179
|
case "wfs": {
|
|
147
180
|
const olLayer = new VectorLayer({
|
|
148
181
|
style: layerModel.style ?? defaultStyle,
|
|
@@ -169,28 +202,35 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
169
202
|
}),
|
|
170
203
|
);
|
|
171
204
|
})
|
|
205
|
+
.then(() => emitLayerLoadingStatusSuccess(olLayer))
|
|
172
206
|
.catch((e) => {
|
|
173
|
-
|
|
207
|
+
const httpStatus =
|
|
208
|
+
e instanceof EndpointError ? e.httpStatus : undefined;
|
|
209
|
+
emitLayerLoadingError(olLayer, e, httpStatus);
|
|
174
210
|
});
|
|
175
211
|
layer = olLayer;
|
|
176
212
|
break;
|
|
177
213
|
}
|
|
214
|
+
|
|
178
215
|
case "maplibre-style": {
|
|
179
216
|
layer = new MapboxVectorLayer({
|
|
180
217
|
styleUrl: layerModel.styleUrl,
|
|
181
218
|
accessToken: layerModel.accessToken,
|
|
182
219
|
}) as unknown as Layer;
|
|
220
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
183
221
|
break;
|
|
184
222
|
}
|
|
223
|
+
|
|
185
224
|
case "geojson": {
|
|
225
|
+
layer = new VectorLayer({
|
|
226
|
+
style: layerModel.style ?? defaultStyle,
|
|
227
|
+
});
|
|
228
|
+
let source: VectorSource;
|
|
186
229
|
if (layerModel.url !== undefined) {
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
attributions: layerModel.attributions,
|
|
192
|
-
}),
|
|
193
|
-
style: layerModel.style ?? defaultStyle,
|
|
230
|
+
source = new VectorSource({
|
|
231
|
+
format: new GeoJSON(),
|
|
232
|
+
url: layerModel.url,
|
|
233
|
+
attributions: layerModel.attributions,
|
|
194
234
|
});
|
|
195
235
|
} else {
|
|
196
236
|
let geojson = layerModel.data;
|
|
@@ -206,16 +246,17 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
206
246
|
featureProjection: "EPSG:3857",
|
|
207
247
|
dataProjection: "EPSG:4326",
|
|
208
248
|
}) as Feature<Geometry>[];
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
attributions: layerModel.attributions,
|
|
213
|
-
}),
|
|
214
|
-
style: layerModel.style ?? defaultStyle,
|
|
249
|
+
source = new VectorSource({
|
|
250
|
+
features,
|
|
251
|
+
attributions: layerModel.attributions,
|
|
215
252
|
});
|
|
216
253
|
}
|
|
254
|
+
layer.setSource(source);
|
|
255
|
+
// FIXME: actually track layer loading and data info
|
|
256
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
217
257
|
break;
|
|
218
258
|
}
|
|
259
|
+
|
|
219
260
|
case "ogcapi": {
|
|
220
261
|
const ogcEndpoint = new OgcApiEndpoint(layerModel.url);
|
|
221
262
|
let layerUrl: string;
|
|
@@ -249,17 +290,21 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
249
290
|
layerModel.collection,
|
|
250
291
|
layerModel.options,
|
|
251
292
|
);
|
|
293
|
+
const source = new VectorSource({
|
|
294
|
+
format: new GeoJSON(),
|
|
295
|
+
url: layerUrl,
|
|
296
|
+
attributions: layerModel.attributions,
|
|
297
|
+
});
|
|
252
298
|
layer = new VectorLayer({
|
|
253
|
-
source
|
|
254
|
-
format: new GeoJSON(),
|
|
255
|
-
url: layerUrl,
|
|
256
|
-
attributions: layerModel.attributions,
|
|
257
|
-
}),
|
|
299
|
+
source,
|
|
258
300
|
style: layerModel.style ?? defaultStyle,
|
|
259
301
|
});
|
|
260
302
|
}
|
|
303
|
+
// FIXME: actually track layer loading
|
|
304
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
261
305
|
break;
|
|
262
306
|
}
|
|
307
|
+
|
|
263
308
|
case "geotiff": {
|
|
264
309
|
const geoTiffSource = new GeoTIFF({
|
|
265
310
|
sources: [{ url: layerModel.url }],
|
|
@@ -268,26 +313,38 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
|
|
|
268
313
|
layer = new WebGLTileLayer({
|
|
269
314
|
source: geoTiffSource,
|
|
270
315
|
});
|
|
316
|
+
// FIXME: actually track tile loading
|
|
317
|
+
defer().then(() => emitLayerLoadingStatusSuccess(layer));
|
|
271
318
|
break;
|
|
272
319
|
}
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
320
|
+
|
|
321
|
+
default: {
|
|
322
|
+
// we create an empty placeholder layer so that we still have a corresponding layer in OL
|
|
323
|
+
layer = new VectorLayer({
|
|
324
|
+
properties: {
|
|
325
|
+
[`${GEOSPATIAL_SDK_PREFIX}layer-with-error`]: true,
|
|
326
|
+
},
|
|
327
|
+
});
|
|
328
|
+
defer().then(() =>
|
|
329
|
+
emitLayerCreationError(
|
|
330
|
+
layer,
|
|
331
|
+
new Error(`Unrecognized layer type: ${JSON.stringify(layerModel)}`),
|
|
332
|
+
),
|
|
333
|
+
);
|
|
334
|
+
}
|
|
278
335
|
}
|
|
279
336
|
|
|
280
|
-
updateLayerProperties(layerModel, layer);
|
|
337
|
+
updateLayerProperties(layerModel, layer!);
|
|
281
338
|
|
|
282
|
-
return layer
|
|
339
|
+
return layer!;
|
|
283
340
|
}
|
|
284
341
|
|
|
285
|
-
export function updateLayerInMap(
|
|
342
|
+
export async function updateLayerInMap(
|
|
286
343
|
map: Map,
|
|
287
344
|
layerModel: MapContextLayer,
|
|
288
345
|
layerPosition: number,
|
|
289
346
|
previousLayerModel: MapContextLayer,
|
|
290
|
-
) {
|
|
347
|
+
): Promise<void> {
|
|
291
348
|
const layers = map.getLayers();
|
|
292
349
|
const updatedLayer = layers.item(layerPosition) as Layer;
|
|
293
350
|
|
|
@@ -299,8 +356,9 @@ export function updateLayerInMap(
|
|
|
299
356
|
|
|
300
357
|
// dispose and recreate layer
|
|
301
358
|
updatedLayer.dispose();
|
|
302
|
-
createLayer(layerModel).then((layer) => {
|
|
359
|
+
await createLayer(layerModel).then((layer) => {
|
|
303
360
|
layers.setAt(layerPosition, layer);
|
|
361
|
+
propagateLayerStateChangeEventToMap(map, layer);
|
|
304
362
|
});
|
|
305
363
|
}
|
|
306
364
|
|
|
@@ -369,6 +427,7 @@ export async function resetMapFromContext(
|
|
|
369
427
|
for (const layerModel of context.layers) {
|
|
370
428
|
const layer = await createLayer(layerModel);
|
|
371
429
|
map.addLayer(layer);
|
|
430
|
+
propagateLayerStateChangeEventToMap(map, layer);
|
|
372
431
|
}
|
|
373
432
|
initHoverLayer(map);
|
|
374
433
|
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);
|