@geospatial-sdk/openlayers 0.0.5-dev.54 → 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.
Files changed (42) hide show
  1. package/dist/map/apply-context-diff.d.ts.map +1 -1
  2. package/dist/map/apply-context-diff.js +9 -1
  3. package/dist/map/create-map.d.ts +1 -1
  4. package/dist/map/create-map.d.ts.map +1 -1
  5. package/dist/map/create-map.js +73 -30
  6. package/dist/map/feature-hover.js +1 -1
  7. package/dist/map/handle-errors.d.ts +0 -7
  8. package/dist/map/handle-errors.d.ts.map +1 -1
  9. package/dist/map/handle-errors.js +10 -14
  10. package/dist/map/index.d.ts +2 -1
  11. package/dist/map/index.d.ts.map +1 -1
  12. package/dist/map/index.js +2 -1
  13. package/dist/map/layer-update.d.ts.map +1 -1
  14. package/dist/map/layer-update.js +1 -1
  15. package/dist/map/listen.d.ts.map +1 -1
  16. package/dist/map/listen.js +13 -26
  17. package/dist/map/register-events.d.ts +16 -2
  18. package/dist/map/register-events.d.ts.map +1 -1
  19. package/dist/map/register-events.js +172 -81
  20. package/lib/map/apply-context-diff.ts +16 -5
  21. package/lib/map/create-map.test.ts +172 -60
  22. package/lib/map/create-map.ts +100 -37
  23. package/lib/map/feature-hover.ts +1 -1
  24. package/lib/map/handle-errors.test.ts +13 -36
  25. package/lib/map/handle-errors.ts +10 -28
  26. package/lib/map/index.ts +2 -1
  27. package/lib/map/layer-update.ts +3 -2
  28. package/lib/map/listen.test.ts +977 -0
  29. package/lib/map/listen.ts +123 -0
  30. package/lib/map/register-events.ts +229 -109
  31. package/lib/map/resolved-map-state.ts +38 -0
  32. package/package.json +3 -3
  33. package/dist/map/feature-selection.d.ts +0 -8
  34. package/dist/map/feature-selection.d.ts.map +0 -1
  35. package/dist/map/feature-selection.js +0 -76
  36. package/dist/map/styles.d.ts +0 -16
  37. package/dist/map/styles.d.ts.map +0 -1
  38. package/dist/map/styles.js +0 -77
  39. package/dist/resolved-state/resolved-map-state.d.ts +0 -2
  40. package/dist/resolved-state/resolved-map-state.d.ts.map +0 -1
  41. package/dist/resolved-state/resolved-map-state.js +0 -1
  42. package/lib/map/register-events.test.ts +0 -259
@@ -1,17 +1,15 @@
1
- import { FeatureCollection } from "geojson";
2
- import TileLayer from "ol/layer/Tile.js";
3
- import VectorLayer from "ol/layer/Vector.js";
4
- import Map from "ol/Map.js";
5
- import TileWMS from "ol/source/TileWMS.js";
6
- import VectorSource from "ol/source/Vector.js";
7
- import XYZ from "ol/source/XYZ.js";
8
- import View from "ol/View.js";
9
- import GeoJSON from "ol/format/GeoJSON.js";
1
+ import {
2
+ MapContext,
3
+ MapContextLayer,
4
+ MapContextLayerGeojson,
5
+ MapContextLayerWms,
6
+ } from "@geospatial-sdk/core";
10
7
  import {
11
8
  MAP_CTX_EXTENT_FIXTURE,
12
9
  MAP_CTX_FIXTURE,
13
10
  MAP_CTX_LAYER_GEOJSON_FIXTURE,
14
11
  MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE,
12
+ MAP_CTX_LAYER_GEOTIFF_FIXTURE,
15
13
  MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE,
16
14
  MAP_CTX_LAYER_MVT_FIXTURE,
17
15
  MAP_CTX_LAYER_OGCAPI_FIXTURE,
@@ -20,31 +18,34 @@ import {
20
18
  MAP_CTX_LAYER_WMTS_FIXTURE,
21
19
  MAP_CTX_LAYER_XYZ_FIXTURE,
22
20
  } from "@geospatial-sdk/core/fixtures/map-context.fixtures.js";
23
- import {
24
- MapContext,
25
- MapContextLayer,
26
- MapContextLayerGeojson,
27
- MapContextLayerWms,
28
- } from "@geospatial-sdk/core";
21
+ import { FeatureCollection } from "geojson";
22
+ import { MapboxVectorLayer } from "ol-mapbox-style";
23
+ import { FeatureUrlFunction } from "ol/featureloader.js";
24
+ import GeoJSON from "ol/format/GeoJSON.js";
25
+ import ImageTile from "ol/ImageTile.js";
29
26
  import Layer from "ol/layer/Layer.js";
27
+ import TileLayer from "ol/layer/Tile.js";
28
+ import VectorLayer from "ol/layer/Vector.js";
29
+ import VectorTileLayer from "ol/layer/VectorTile.js";
30
+ import WebGLTileLayer from "ol/layer/WebGLTile.js";
31
+ import Map from "ol/Map.js";
32
+ import { VectorTile } from "ol/source.js";
33
+ import GeoTIFF from "ol/source/GeoTIFF.js";
34
+ import TileWMS from "ol/source/TileWMS.js";
35
+ import VectorSource from "ol/source/Vector.js";
36
+ import WMTS from "ol/source/WMTS.js";
37
+ import XYZ from "ol/source/XYZ.js";
38
+ import TileState from "ol/TileState.js";
39
+ import View from "ol/View.js";
40
+ import { beforeEach } from "vitest";
30
41
  import {
31
42
  createLayer,
32
43
  createMapFromContext,
33
44
  createView,
34
45
  resetMapFromContext,
35
46
  } from "./create-map.js";
36
- import WMTS from "ol/source/WMTS.js";
37
- import { VectorTile } from "ol/source.js";
38
- import { MapboxVectorLayer } from "ol-mapbox-style";
39
- import {
40
- handleEndpointError,
41
- tileLoadErrorCatchFunction,
42
- } from "./handle-errors.js";
43
- import ImageTile from "ol/ImageTile.js";
44
- import TileState from "ol/TileState.js";
45
- import VectorTileLayer from "ol/layer/VectorTile.js";
46
- import { FeatureUrlFunction } from "ol/featureloader.js";
47
- import { beforeEach } from "vitest";
47
+ import { tileLoadErrorCatchFunction } from "./handle-errors.js";
48
+ import { GEOSPATIAL_SDK_PREFIX } from "./constants.js";
48
49
 
49
50
  vi.mock("./handle-errors", async (importOriginal) => {
50
51
  const actual =
@@ -56,18 +57,22 @@ vi.mock("./handle-errors", async (importOriginal) => {
56
57
  };
57
58
  });
58
59
 
59
- afterEach(() => {
60
+ beforeEach(() => {
61
+ vi.useFakeTimers();
60
62
  vi.clearAllMocks();
61
63
  });
62
64
 
63
65
  describe("MapContextService", () => {
64
66
  describe("#createLayer", () => {
65
67
  let layerModel: MapContextLayer, layer: Layer;
68
+ const eventCallback = vi.fn();
66
69
 
67
70
  describe("XYZ", () => {
68
71
  beforeEach(async () => {
69
72
  layerModel = MAP_CTX_LAYER_XYZ_FIXTURE;
70
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);
71
76
  });
72
77
  it("create a tile layer", () => {
73
78
  expect(layer).toBeTruthy();
@@ -105,13 +110,25 @@ describe("MapContextService", () => {
105
110
  tileLoadFunction(tile, "http://example.com/tile");
106
111
  expect(tileLoadErrorCatchFunction).toHaveBeenCalled();
107
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
+ });
108
123
  });
109
- describe("OGCAPI", () => {
124
+ describe("OGCAPI (as geojson items)", () => {
110
125
  beforeEach(async () => {
111
126
  layerModel = MAP_CTX_LAYER_OGCAPI_FIXTURE;
112
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);
113
130
  });
114
- it("create a vector tile layer", () => {
131
+ it("create a vector layer", () => {
115
132
  expect(layer).toBeTruthy();
116
133
  expect(layer).toBeInstanceOf(VectorLayer);
117
134
  });
@@ -121,7 +138,7 @@ describe("MapContextService", () => {
121
138
  expect(layer.get("label")).toBeUndefined();
122
139
  expect(layer.getSource()?.getAttributions()).toBeNull();
123
140
  });
124
- it("create a OGCVectorTile source", () => {
141
+ it("create a vector source", () => {
125
142
  const source = layer.getSource();
126
143
  expect(source).toBeInstanceOf(VectorSource);
127
144
  });
@@ -132,11 +149,23 @@ describe("MapContextService", () => {
132
149
  "https://demo.ldproxy.net/zoomstack/collections/airports/items?f=json",
133
150
  );
134
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
+ });
135
162
  });
136
163
  describe("WMS", () => {
137
164
  beforeEach(async () => {
138
- (layerModel = MAP_CTX_LAYER_WMS_FIXTURE),
139
- (layer = await createLayer(layerModel));
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);
140
169
  });
141
170
  it("create a tile layer", () => {
142
171
  expect(layer).toBeTruthy();
@@ -186,12 +215,24 @@ describe("MapContextService", () => {
186
215
  tileLoadFunction(tile, "http://example.com/tile");
187
216
  expect(tileLoadErrorCatchFunction).toHaveBeenCalled();
188
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
+ });
189
228
  });
190
229
 
191
230
  describe("WFS", () => {
192
231
  beforeEach(async () => {
193
- (layerModel = MAP_CTX_LAYER_WFS_FIXTURE),
194
- (layer = await createLayer(layerModel));
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);
195
236
  });
196
237
  it("create a vector layer", () => {
197
238
  expect(layer).toBeTruthy();
@@ -221,17 +262,15 @@ describe("MapContextService", () => {
221
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",
222
263
  );
223
264
  });
224
- it("should NOT call handleEndpointError", () => {
225
- expect(handleEndpointError).not.toHaveBeenCalled();
226
- });
227
- });
228
- describe("WFS error", () => {
229
- beforeEach(async () => {
230
- layerModel = { ...MAP_CTX_LAYER_WFS_FIXTURE, url: "https://wfs/error" };
231
- layer = await createLayer(layerModel);
232
- });
233
- it("should call handleEndpointError", () => {
234
- 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
+ });
235
274
  });
236
275
  });
237
276
 
@@ -240,6 +279,11 @@ describe("MapContextService", () => {
240
279
  beforeEach(async () => {
241
280
  layerModel = MAP_CTX_LAYER_GEOJSON_FIXTURE;
242
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);
243
287
  });
244
288
  it("create a VectorLayer", () => {
245
289
  expect(layer).toBeTruthy();
@@ -261,6 +305,16 @@ describe("MapContextService", () => {
261
305
  .data as FeatureCollection;
262
306
  expect(features.length).toBe(data.features.length);
263
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
+ });
264
318
  });
265
319
  describe("with inline data as string", () => {
266
320
  beforeEach(async () => {
@@ -313,6 +367,11 @@ describe("MapContextService", () => {
313
367
  beforeEach(async () => {
314
368
  layerModel = MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE;
315
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);
316
375
  });
317
376
  it("create a VectorLayer", () => {
318
377
  expect(layer).toBeTruthy();
@@ -332,6 +391,16 @@ describe("MapContextService", () => {
332
391
  (layerModel as MapContextLayerGeojson).url,
333
392
  );
334
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
+ });
335
404
  });
336
405
  });
337
406
 
@@ -339,6 +408,8 @@ describe("MapContextService", () => {
339
408
  beforeEach(async () => {
340
409
  layerModel = MAP_CTX_LAYER_WMTS_FIXTURE;
341
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);
342
413
  });
343
414
  it("create a tile layer", () => {
344
415
  expect(layer).toBeTruthy();
@@ -363,20 +434,15 @@ describe("MapContextService", () => {
363
434
  "https://services.geo.sg.ch/wss/service/SG00066_WMTS/guest/tile/1.0.0/SG00066/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}",
364
435
  ]);
365
436
  });
366
- it("should NOT call handleEndpointError", () => {
367
- expect(handleEndpointError).not.toHaveBeenCalled();
368
- });
369
- });
370
- describe("WMTS error", () => {
371
- beforeEach(async () => {
372
- layerModel = {
373
- ...MAP_CTX_LAYER_WMTS_FIXTURE,
374
- url: "https://wmts/error",
375
- };
376
- layer = await createLayer(layerModel);
377
- });
378
- it("should call handleEndpointError", () => {
379
- 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
+ });
380
446
  });
381
447
  });
382
448
  describe("WMTS without default style given", () => {
@@ -386,6 +452,8 @@ describe("MapContextService", () => {
386
452
  url: "https://wmts/no-default-style",
387
453
  };
388
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);
389
457
  });
390
458
  it("uses the first style as default", async () => {
391
459
  const source = layer.getSource() as WMTS;
@@ -397,6 +465,8 @@ describe("MapContextService", () => {
397
465
  beforeEach(async () => {
398
466
  layerModel = MAP_CTX_LAYER_MAPBLIBRE_STYLE_FIXTURE;
399
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);
400
470
  });
401
471
  it("create a tile layer", () => {
402
472
  expect(layer).toBeTruthy();
@@ -411,12 +481,24 @@ describe("MapContextService", () => {
411
481
  const source = layer.getSource();
412
482
  expect(source).toBeInstanceOf(VectorTile);
413
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
+ });
414
494
  });
415
495
 
416
496
  describe("MVT", () => {
417
497
  beforeEach(async () => {
418
498
  layerModel = MAP_CTX_LAYER_MVT_FIXTURE;
419
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);
420
502
  });
421
503
  it("create a VectorTileLayer", () => {
422
504
  expect(layer).toBeTruthy();
@@ -431,6 +513,36 @@ describe("MapContextService", () => {
431
513
  expect(layer.getOpacity()).toBe(1);
432
514
  expect(layer.get("label")).toBeUndefined();
433
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
+ });
526
+ });
527
+
528
+ describe("GeoTIFF", () => {
529
+ beforeEach(async () => {
530
+ layerModel = MAP_CTX_LAYER_GEOTIFF_FIXTURE;
531
+ layer = await createLayer(layerModel);
532
+ });
533
+ it("create a WebGLTile layer", () => {
534
+ expect(layer).toBeTruthy();
535
+ expect(layer).toBeInstanceOf(WebGLTileLayer);
536
+ });
537
+ it("set correct layer properties", () => {
538
+ expect(layer.getVisible()).toBe(true);
539
+ expect(layer.getOpacity()).toBe(1);
540
+ expect(layer.get("label")).toBeUndefined();
541
+ });
542
+ it("create a GeoTIFF source", () => {
543
+ const source = layer.getSource();
544
+ expect(source).toBeInstanceOf(GeoTIFF);
545
+ });
434
546
  });
435
547
  });
436
548
 
@@ -25,29 +25,48 @@ 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";
37
+ import GeoTIFF from "ol/source/GeoTIFF.js";
38
+ import WebGLTileLayer from "ol/layer/WebGLTile.js";
39
+ import proj4 from "proj4";
40
+ import { register } from "ol/proj/proj4.js";
39
41
  import {
40
42
  canDoIncrementalUpdate,
41
43
  updateLayerProperties,
42
44
  } from "./layer-update.js";
43
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
+
54
+ // Register proj4 with OpenLayers so that arbitrary EPSG codes
55
+ // (e.g., UTM zones from GeoTIFF metadata) can be reprojected to the map projection
56
+ register(proj4);
44
57
 
45
58
  const GEOJSON = new GeoJSON();
46
59
  const WFS_MAX_FEATURES = 10000;
47
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
+
48
66
  export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
49
67
  const { type } = layerModel;
50
- let layer: Layer | undefined;
68
+ let layer: Layer;
69
+
51
70
  switch (type) {
52
71
  case "xyz":
53
72
  {
@@ -74,8 +93,10 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
74
93
  });
75
94
  layer.setSource(source);
76
95
  }
96
+ defer().then(() => emitLayerLoadingStatusSuccess(layer));
77
97
  }
78
98
  break;
99
+
79
100
  case "wms":
80
101
  {
81
102
  layer = new TileLayer({});
@@ -96,9 +117,10 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
96
117
  );
97
118
  });
98
119
  layer.setSource(source);
120
+ defer().then(() => emitLayerLoadingStatusSuccess(layer));
99
121
  }
100
-
101
122
  break;
123
+
102
124
  case "wmts": {
103
125
  const olLayer = new TileLayer({});
104
126
  const endpoint = new WmtsEndpoint(layerModel.url);
@@ -130,11 +152,16 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
130
152
  }),
131
153
  );
132
154
  })
155
+ .then(() => emitLayerLoadingStatusSuccess(olLayer))
133
156
  .catch((e) => {
134
- handleEndpointError(olLayer, e);
157
+ const httpStatus =
158
+ e instanceof EndpointError ? e.httpStatus : undefined;
159
+ emitLayerLoadingError(olLayer, e, httpStatus);
135
160
  });
136
- return olLayer;
161
+ layer = olLayer;
162
+ break;
137
163
  }
164
+
138
165
  case "wfs": {
139
166
  const olLayer = new VectorLayer({
140
167
  style: layerModel.style ?? defaultStyle,
@@ -161,28 +188,35 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
161
188
  }),
162
189
  );
163
190
  })
191
+ .then(() => emitLayerLoadingStatusSuccess(olLayer))
164
192
  .catch((e) => {
165
- handleEndpointError(olLayer, e);
193
+ const httpStatus =
194
+ e instanceof EndpointError ? e.httpStatus : undefined;
195
+ emitLayerLoadingError(olLayer, e, httpStatus);
166
196
  });
167
197
  layer = olLayer;
168
198
  break;
169
199
  }
200
+
170
201
  case "maplibre-style": {
171
202
  layer = new MapboxVectorLayer({
172
203
  styleUrl: layerModel.styleUrl,
173
204
  accessToken: layerModel.accessToken,
174
205
  }) as unknown as Layer;
206
+ defer().then(() => emitLayerLoadingStatusSuccess(layer));
175
207
  break;
176
208
  }
209
+
177
210
  case "geojson": {
211
+ layer = new VectorLayer({
212
+ style: layerModel.style ?? defaultStyle,
213
+ });
214
+ let source: VectorSource;
178
215
  if (layerModel.url !== undefined) {
179
- layer = new VectorLayer({
180
- source: new VectorSource({
181
- format: new GeoJSON(),
182
- url: layerModel.url,
183
- attributions: layerModel.attributions,
184
- }),
185
- style: layerModel.style ?? defaultStyle,
216
+ source = new VectorSource({
217
+ format: new GeoJSON(),
218
+ url: layerModel.url,
219
+ attributions: layerModel.attributions,
186
220
  });
187
221
  } else {
188
222
  let geojson = layerModel.data;
@@ -198,16 +232,17 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
198
232
  featureProjection: "EPSG:3857",
199
233
  dataProjection: "EPSG:4326",
200
234
  }) as Feature<Geometry>[];
201
- layer = new VectorLayer({
202
- source: new VectorSource({
203
- features,
204
- attributions: layerModel.attributions,
205
- }),
206
- style: layerModel.style ?? defaultStyle,
235
+ source = new VectorSource({
236
+ features,
237
+ attributions: layerModel.attributions,
207
238
  });
208
239
  }
240
+ layer.setSource(source);
241
+ // FIXME: actually track layer loading and data info
242
+ defer().then(() => emitLayerLoadingStatusSuccess(layer));
209
243
  break;
210
244
  }
245
+
211
246
  case "ogcapi": {
212
247
  const ogcEndpoint = new OgcApiEndpoint(layerModel.url);
213
248
  let layerUrl: string;
@@ -241,35 +276,61 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
241
276
  layerModel.collection,
242
277
  layerModel.options,
243
278
  );
279
+ const source = new VectorSource({
280
+ format: new GeoJSON(),
281
+ url: layerUrl,
282
+ attributions: layerModel.attributions,
283
+ });
244
284
  layer = new VectorLayer({
245
- source: new VectorSource({
246
- format: new GeoJSON(),
247
- url: layerUrl,
248
- attributions: layerModel.attributions,
249
- }),
285
+ source,
250
286
  style: layerModel.style ?? defaultStyle,
251
287
  });
252
288
  }
289
+ // FIXME: actually track layer loading
290
+ defer().then(() => emitLayerLoadingStatusSuccess(layer));
253
291
  break;
254
292
  }
255
- default:
256
- throw new Error(`Unrecognized layer type: ${JSON.stringify(layerModel)}`);
257
- }
258
- if (!layer) {
259
- throw new Error(`Layer could not be created for type: ${layerModel.type}`);
293
+
294
+ case "geotiff": {
295
+ const geoTiffSource = new GeoTIFF({
296
+ sources: [{ url: layerModel.url }],
297
+ convertToRGB: "auto",
298
+ });
299
+ layer = new WebGLTileLayer({
300
+ source: geoTiffSource,
301
+ });
302
+ // FIXME: actually track tile loading
303
+ defer().then(() => emitLayerLoadingStatusSuccess(layer));
304
+ break;
305
+ }
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
+ }
260
321
  }
261
322
 
262
- updateLayerProperties(layerModel, layer);
323
+ updateLayerProperties(layerModel, layer!);
263
324
 
264
- return layer;
325
+ return layer!;
265
326
  }
266
327
 
267
- export function updateLayerInMap(
328
+ export async function updateLayerInMap(
268
329
  map: Map,
269
330
  layerModel: MapContextLayer,
270
331
  layerPosition: number,
271
332
  previousLayerModel: MapContextLayer,
272
- ) {
333
+ ): Promise<void> {
273
334
  const layers = map.getLayers();
274
335
  const updatedLayer = layers.item(layerPosition) as Layer;
275
336
 
@@ -281,8 +342,9 @@ export function updateLayerInMap(
281
342
 
282
343
  // dispose and recreate layer
283
344
  updatedLayer.dispose();
284
- createLayer(layerModel).then((layer) => {
345
+ await createLayer(layerModel).then((layer) => {
285
346
  layers.setAt(layerPosition, layer);
347
+ propagateLayerStateChangeEventToMap(map, layer);
286
348
  });
287
349
  }
288
350
 
@@ -351,6 +413,7 @@ export async function resetMapFromContext(
351
413
  for (const layerModel of context.layers) {
352
414
  const layer = await createLayer(layerModel);
353
415
  map.addLayer(layer);
416
+ propagateLayerStateChangeEventToMap(map, layer);
354
417
  }
355
418
  initHoverLayer(map);
356
419
  return map;
@@ -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);