@geospatial-sdk/openlayers 0.0.5-dev.13 → 0.0.5-dev.15

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.
@@ -1 +1 @@
1
- {"version":3,"file":"apply-context-diff.d.ts","sourceRoot":"","sources":["../../lib/map/apply-context-diff.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,QAAQ,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAGtD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACvC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,cAAc,GAC5B,OAAO,CAAC,GAAG,CAAC,CAmDd"}
1
+ {"version":3,"file":"apply-context-diff.d.ts","sourceRoot":"","sources":["../../lib/map/apply-context-diff.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,QAAQ,CAAC;AACzB,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AAQtD;;;;GAIG;AACH,wBAAsB,qBAAqB,CACzC,GAAG,EAAE,GAAG,EACR,WAAW,EAAE,cAAc,GAC1B,OAAO,CAAC,GAAG,CAAC,CAuFd"}
@@ -8,6 +8,9 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
8
8
  });
9
9
  };
10
10
  import { createLayer } from "./create-map";
11
+ import { fromLonLat, transformExtent } from "ol/proj";
12
+ import GeoJSON from "ol/format/GeoJSON";
13
+ const GEOJSON = new GeoJSON();
11
14
  /**
12
15
  * Apply a context diff to an OpenLayers map
13
16
  * @param map
@@ -48,10 +51,46 @@ export function applyContextDiffToMap(map, contextDiff) {
48
51
  // recreate changed layers
49
52
  for (const layerChanged of contextDiff.layersChanged) {
50
53
  layers.item(layerChanged.position).dispose();
51
- createLayer(layerChanged.layer).then(layer => {
54
+ createLayer(layerChanged.layer).then((layer) => {
52
55
  layers.setAt(layerChanged.position, layer);
53
56
  });
54
57
  }
58
+ if ("viewChanges" in contextDiff) {
59
+ const { viewChanges } = contextDiff;
60
+ const view = map.getView();
61
+ const projection = view.getProjection();
62
+ if (!viewChanges) {
63
+ return map;
64
+ }
65
+ if ("geometry" in viewChanges) {
66
+ const geom = GEOJSON.readGeometry(viewChanges.geometry);
67
+ view.fit(geom, {
68
+ size: map.getSize(),
69
+ });
70
+ }
71
+ else if ("extent" in viewChanges) {
72
+ view.fit(transformExtent(viewChanges.extent, "EPSG:4326", projection), {
73
+ size: map.getSize(),
74
+ });
75
+ }
76
+ else {
77
+ const { center: centerInViewProj, zoom } = viewChanges;
78
+ const center = centerInViewProj
79
+ ? fromLonLat(centerInViewProj, projection)
80
+ : [0, 0];
81
+ view.setCenter(center);
82
+ view.setZoom(zoom);
83
+ if (viewChanges.maxZoom) {
84
+ view.setMaxZoom(viewChanges.maxZoom);
85
+ }
86
+ // TODO: factorize this better
87
+ // if (viewChanges.maxExtent) {
88
+ // map.setView(new View({
89
+ //
90
+ // }))
91
+ // }
92
+ }
93
+ }
55
94
  return map;
56
95
  });
57
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"create-map.d.ts","sourceRoot":"","sources":["../../lib/map/create-map.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,eAAe,EACf,cAAc,EACf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,GAAG,MAAM,QAAQ,CAAC;AACzB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAqBnC,wBAAsB,WAAW,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CA2I7E;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAmBpE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACtC,OAAO,EAAE,UAAU,EACnB,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAC9B,OAAO,CAAC,GAAG,CAAC,CAKd;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CAAC,GAAG,EAAE,GAAG,EAAE,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,GAAG,CAAC,CAQrF"}
1
+ {"version":3,"file":"create-map.d.ts","sourceRoot":"","sources":["../../lib/map/create-map.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,UAAU,EACV,eAAe,EACf,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,GAAG,MAAM,QAAQ,CAAC;AACzB,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,KAAK,MAAM,gBAAgB,CAAC;AAyBnC,wBAAsB,WAAW,CAAC,UAAU,EAAE,eAAe,GAAG,OAAO,CAAC,KAAK,CAAC,CA8K7E;AAED,wBAAgB,UAAU,CAAC,SAAS,EAAE,cAAc,EAAE,GAAG,EAAE,GAAG,GAAG,IAAI,CAyBpE;AAED;;;;GAIG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,UAAU,EACnB,MAAM,CAAC,EAAE,MAAM,GAAG,WAAW,GAC5B,OAAO,CAAC,GAAG,CAAC,CAKd;AAED;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,GAAG,EAAE,GAAG,EACR,OAAO,EAAE,UAAU,GAClB,OAAO,CAAC,GAAG,CAAC,CAQd"}
@@ -7,6 +7,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
+ import { removeSearchParams, } from "@geospatial-sdk/core";
10
11
  import Map from "ol/Map";
11
12
  import View from "ol/View";
12
13
  import TileLayer from "ol/layer/Tile";
@@ -17,13 +18,12 @@ import VectorSource from "ol/source/Vector";
17
18
  import GeoJSON from "ol/format/GeoJSON";
18
19
  import { fromLonLat } from "ol/proj";
19
20
  import { bbox as bboxStrategy } from "ol/loadingstrategy";
20
- import { removeSearchParams } from "@geospatial-sdk/core";
21
21
  import { defaultStyle } from "./styles";
22
22
  import VectorTileLayer from "ol/layer/VectorTile";
23
- import { OGCMapTile, OGCVectorTile } from "ol/source";
23
+ import { OGCMapTile, OGCVectorTile, WMTS } from "ol/source";
24
24
  import { MVT } from "ol/format";
25
- import { OgcApiEndpoint, WfsEndpoint } from "@camptocamp/ogc-client";
26
- const geosjonFormat = new GeoJSON();
25
+ import { OgcApiEndpoint, WfsEndpoint, WmtsEndpoint, } from "@camptocamp/ogc-client";
26
+ const GEOJSON = new GeoJSON();
27
27
  const WFS_MAX_FEATURES = 10000;
28
28
  export function createLayer(layerModel) {
29
29
  var _a;
@@ -50,11 +50,36 @@ export function createLayer(layerModel) {
50
50
  }),
51
51
  });
52
52
  break;
53
- // TODO: implement when ogc-client can handle wmts
54
- // case 'wmts':
55
- // return new TileLayer({
56
- // source: new WMTS(layerModel.options),
57
- // })
53
+ case "wmts": {
54
+ const olLayer = new TileLayer({});
55
+ const endpoint = new WmtsEndpoint(layerModel.url);
56
+ endpoint.isReady().then((endpoint) => __awaiter(this, void 0, void 0, function* () {
57
+ var _b;
58
+ const layerName = (_b = endpoint.getSingleLayerName()) !== null && _b !== void 0 ? _b : layerModel.name;
59
+ const layer = endpoint.getLayerByName(layerName);
60
+ const matrixSet = layer.matrixSets[0];
61
+ const tileGrid = yield endpoint.getOpenLayersTileGrid(layer.name);
62
+ if (tileGrid === null) {
63
+ console.warn("A WMTS tile grid could not be created", layerModel);
64
+ return;
65
+ }
66
+ const resourceUrl = layer.resourceLinks[0];
67
+ const dimensions = endpoint.getDefaultDimensions(layer.name);
68
+ olLayer.setSource(new WMTS({
69
+ layer: layer.name,
70
+ style: layer.defaultStyle,
71
+ matrixSet: matrixSet.identifier,
72
+ format: resourceUrl.format,
73
+ url: resourceUrl.url,
74
+ requestEncoding: resourceUrl.encoding,
75
+ tileGrid,
76
+ projection: matrixSet.crs,
77
+ dimensions,
78
+ attributions: layerModel.attributions,
79
+ }));
80
+ }));
81
+ return olLayer;
82
+ }
58
83
  case "wfs": {
59
84
  const olLayer = new VectorLayer({
60
85
  style,
@@ -102,7 +127,7 @@ export function createLayer(layerModel) {
102
127
  geojson = { type: "FeatureCollection", features: [] };
103
128
  }
104
129
  }
105
- const features = geosjonFormat.readFeatures(geojson, {
130
+ const features = GEOJSON.readFeatures(geojson, {
106
131
  featureProjection: "EPSG:3857",
107
132
  dataProjection: "EPSG:4326",
108
133
  });
@@ -120,7 +145,7 @@ export function createLayer(layerModel) {
120
145
  const ogcEndpoint = yield new OgcApiEndpoint(layerModel.url);
121
146
  let layerUrl;
122
147
  if (layerModel.useTiles) {
123
- if (layerModel.useTiles === 'vector') {
148
+ if (layerModel.useTiles === "vector") {
124
149
  layerUrl = yield ogcEndpoint.getVectorTilesetUrl(layerModel.collection, layerModel.tileMatrixSet);
125
150
  layer = new VectorTileLayer({
126
151
  source: new OGCVectorTile({
@@ -130,7 +155,7 @@ export function createLayer(layerModel) {
130
155
  }),
131
156
  });
132
157
  }
133
- else if (layerModel.useTiles === 'map') {
158
+ else if (layerModel.useTiles === "map") {
134
159
  layerUrl = yield ogcEndpoint.getMapTilesetUrl(layerModel.collection, layerModel.tileMatrixSet);
135
160
  layer = new TileLayer({
136
161
  source: new OGCMapTile({
@@ -154,7 +179,7 @@ export function createLayer(layerModel) {
154
179
  break;
155
180
  }
156
181
  default:
157
- throw new Error(`Unrecognized layer type: ${layerModel.type}`);
182
+ throw new Error(`Unrecognized layer type: ${JSON.stringify(layerModel)}`);
158
183
  }
159
184
  if (!layer) {
160
185
  throw new Error(`Layer could not be created for type: ${layerModel.type}`);
@@ -170,23 +195,26 @@ export function createLayer(layerModel) {
170
195
  });
171
196
  }
172
197
  export function createView(viewModel, map) {
173
- const { center: centerInViewProj, zoom, maxZoom, maxExtent } = viewModel;
174
- const center = centerInViewProj
175
- ? fromLonLat(centerInViewProj, "EPSG:3857")
176
- : [0, 0];
177
- const view = new View({
178
- center,
179
- zoom: zoom !== null && zoom !== void 0 ? zoom : 0,
180
- maxZoom,
181
- extent: maxExtent,
182
- multiWorld: false,
183
- constrainResolution: true,
184
- });
185
- if (viewModel.extent) {
198
+ const view = new View(Object.assign(Object.assign(Object.assign({}, ("maxExtent" in viewModel && { extent: viewModel.maxExtent })), ("maxZoom" in viewModel && { maxZoom: viewModel.maxZoom })), { multiWorld: false, constrainResolution: true }));
199
+ if ("geometry" in viewModel) {
200
+ const geom = GEOJSON.readGeometry(viewModel.geometry);
201
+ view.fit(geom, {
202
+ size: map.getSize(),
203
+ });
204
+ }
205
+ else if ("extent" in viewModel) {
186
206
  view.fit(viewModel.extent, {
187
207
  size: map.getSize(),
188
208
  });
189
209
  }
210
+ else {
211
+ const { center: centerInViewProj, zoom } = viewModel;
212
+ const center = centerInViewProj
213
+ ? fromLonLat(centerInViewProj, "EPSG:3857")
214
+ : [0, 0];
215
+ view.setCenter(center);
216
+ view.setZoom(zoom !== undefined ? zoom : 0);
217
+ }
190
218
  return view;
191
219
  }
192
220
  /**
@@ -18,7 +18,7 @@ import { beforeEach } from "vitest";
18
18
  import BaseLayer from "ol/layer/Base";
19
19
 
20
20
  async function assertEqualsToModel(layer: any, layerModel: MapContextLayer) {
21
- const reference = await createLayer(layerModel) as any;
21
+ const reference = (await createLayer(layerModel)) as any;
22
22
  expect(reference).toBeInstanceOf(layer.constructor);
23
23
  const refSource = reference.getSource() as any;
24
24
  const layerSource = layer.getSource() as any;
@@ -235,7 +235,7 @@ describe("applyContextDiffToMap", () => {
235
235
  describe("combined changes", () => {
236
236
  let changedLayer: MapContextLayer;
237
237
  beforeEach(async () => {
238
- changedLayer = {...SAMPLE_LAYER3, extras: {prop: true}};
238
+ changedLayer = { ...SAMPLE_LAYER3, extras: { prop: true } };
239
239
  context = {
240
240
  ...context,
241
241
  layers: [SAMPLE_LAYER1, SAMPLE_LAYER5, SAMPLE_LAYER3, SAMPLE_LAYER4],
@@ -1,6 +1,11 @@
1
1
  import Map from "ol/Map";
2
2
  import { MapContextDiff } from "@geospatial-sdk/core";
3
3
  import { createLayer } from "./create-map";
4
+ import { fromLonLat, transformExtent } from "ol/proj";
5
+ import GeoJSON from "ol/format/GeoJSON";
6
+ import SimpleGeometry from "ol/geom/SimpleGeometry";
7
+
8
+ const GEOJSON = new GeoJSON();
4
9
 
5
10
  /**
6
11
  * Apply a context diff to an OpenLayers map
@@ -8,15 +13,15 @@ import { createLayer } from "./create-map";
8
13
  * @param contextDiff
9
14
  */
10
15
  export async function applyContextDiffToMap(
11
- map: Map,
12
- contextDiff: MapContextDiff,
16
+ map: Map,
17
+ contextDiff: MapContextDiff,
13
18
  ): Promise<Map> {
14
19
  const layers = map.getLayers();
15
20
 
16
21
  // removed layers (sorted by descending position)
17
22
  if (contextDiff.layersRemoved.length > 0) {
18
23
  const removed = contextDiff.layersRemoved.sort(
19
- (a, b) => b.position - a.position,
24
+ (a, b) => b.position - a.position,
20
25
  );
21
26
  for (const layerRemoved of removed) {
22
27
  layers.item(layerRemoved.position).dispose();
@@ -26,7 +31,7 @@ export async function applyContextDiffToMap(
26
31
 
27
32
  // insert added layers
28
33
  const newLayers = await Promise.all(
29
- contextDiff.layersAdded.map((layerAdded) => createLayer(layerAdded.layer))
34
+ contextDiff.layersAdded.map((layerAdded) => createLayer(layerAdded.layer)),
30
35
  );
31
36
 
32
37
  newLayers.forEach((layer, index) => {
@@ -41,10 +46,10 @@ export async function applyContextDiffToMap(
41
46
  // move reordered layers (sorted by ascending new position)
42
47
  if (contextDiff.layersReordered.length > 0) {
43
48
  const reordered = contextDiff.layersReordered.sort(
44
- (a, b) => a.newPosition - b.newPosition,
49
+ (a, b) => a.newPosition - b.newPosition,
45
50
  );
46
51
  const olLayers = reordered.map((layer) =>
47
- layers.item(layer.previousPosition),
52
+ layers.item(layer.previousPosition),
48
53
  );
49
54
  const layersArray = layers.getArray();
50
55
  for (let i = 0; i < reordered.length; i++) {
@@ -56,9 +61,45 @@ export async function applyContextDiffToMap(
56
61
  // recreate changed layers
57
62
  for (const layerChanged of contextDiff.layersChanged) {
58
63
  layers.item(layerChanged.position).dispose();
59
- createLayer(layerChanged.layer).then(layer => {
64
+ createLayer(layerChanged.layer).then((layer) => {
60
65
  layers.setAt(layerChanged.position, layer);
61
66
  });
62
67
  }
68
+
69
+ if ("viewChanges" in contextDiff) {
70
+ const { viewChanges } = contextDiff;
71
+ const view = map.getView();
72
+ const projection = view.getProjection();
73
+ if (!viewChanges) {
74
+ return map;
75
+ }
76
+ if ("geometry" in viewChanges) {
77
+ const geom = GEOJSON.readGeometry(viewChanges.geometry);
78
+ view.fit(geom as SimpleGeometry, {
79
+ size: map.getSize(),
80
+ });
81
+ } else if ("extent" in viewChanges) {
82
+ view.fit(transformExtent(viewChanges.extent, "EPSG:4326", projection), {
83
+ size: map.getSize(),
84
+ });
85
+ } else {
86
+ const { center: centerInViewProj, zoom } = viewChanges;
87
+ const center = centerInViewProj
88
+ ? fromLonLat(centerInViewProj, projection)
89
+ : [0, 0];
90
+ view.setCenter(center);
91
+ view.setZoom(zoom);
92
+ if (viewChanges.maxZoom) {
93
+ view.setMaxZoom(viewChanges.maxZoom);
94
+ }
95
+ // TODO: factorize this better
96
+ // if (viewChanges.maxExtent) {
97
+ // map.setView(new View({
98
+ //
99
+ // }))
100
+ // }
101
+ }
102
+ }
103
+
63
104
  return map;
64
105
  }
@@ -12,10 +12,11 @@ import {
12
12
  MAP_CTX_FIXTURE,
13
13
  MAP_CTX_LAYER_GEOJSON_FIXTURE,
14
14
  MAP_CTX_LAYER_GEOJSON_REMOTE_FIXTURE,
15
+ MAP_CTX_LAYER_OGCAPI_FIXTURE,
15
16
  MAP_CTX_LAYER_WFS_FIXTURE,
16
17
  MAP_CTX_LAYER_WMS_FIXTURE,
18
+ MAP_CTX_LAYER_WMTS_FIXTURE,
17
19
  MAP_CTX_LAYER_XYZ_FIXTURE,
18
- MAP_CTX_LAYER_OGCAPI_FIXTURE,
19
20
  } from "@geospatial-sdk/core/fixtures/map-context.fixtures";
20
21
  import {
21
22
  MapContext,
@@ -30,6 +31,7 @@ import {
30
31
  createView,
31
32
  resetMapFromContext,
32
33
  } from "./create-map";
34
+ import WMTS from "ol/source/WMTS";
33
35
 
34
36
  describe("MapContextService", () => {
35
37
  describe("#createLayer", () => {
@@ -64,36 +66,36 @@ describe("MapContextService", () => {
64
66
  });
65
67
  });
66
68
  describe("OGCAPI", () => {
67
- beforeEach(async () => {
68
- layerModel = MAP_CTX_LAYER_OGCAPI_FIXTURE;
69
- layer = await createLayer(layerModel);
70
- });
71
- it("create a vector tile layer", () => {
72
- expect(layer).toBeTruthy();
73
- expect(layer).toBeInstanceOf(VectorLayer);
74
- });
75
- it("set correct layer properties", () => {
76
- expect(layer.getVisible()).toBe(true);
77
- expect(layer.getOpacity()).toBe(1);
78
- expect(layer.get("label")).toBeUndefined();
79
- expect(layer.getSource()?.getAttributions()).toBeNull();
80
- });
81
- it("create a OGCVectorTile source", () => {
82
- const source = layer.getSource();
83
- expect(source).toBeInstanceOf(VectorSource);
84
- });
85
- it("set correct url", () => {
86
- const source = layer.getSource() as VectorSource;
87
- const url = source.getUrl();
88
- expect(url).toBe(
89
- "https://demo.ldproxy.net/zoomstack/collections/airports/items?f=json",
90
- );
91
- });
69
+ beforeEach(async () => {
70
+ layerModel = MAP_CTX_LAYER_OGCAPI_FIXTURE;
71
+ layer = await createLayer(layerModel);
72
+ });
73
+ it("create a vector tile layer", () => {
74
+ expect(layer).toBeTruthy();
75
+ expect(layer).toBeInstanceOf(VectorLayer);
76
+ });
77
+ it("set correct layer properties", () => {
78
+ expect(layer.getVisible()).toBe(true);
79
+ expect(layer.getOpacity()).toBe(1);
80
+ expect(layer.get("label")).toBeUndefined();
81
+ expect(layer.getSource()?.getAttributions()).toBeNull();
82
+ });
83
+ it("create a OGCVectorTile source", () => {
84
+ const source = layer.getSource();
85
+ expect(source).toBeInstanceOf(VectorSource);
86
+ });
87
+ it("set correct url", () => {
88
+ const source = layer.getSource() as VectorSource;
89
+ const url = source.getUrl();
90
+ expect(url).toBe(
91
+ "https://demo.ldproxy.net/zoomstack/collections/airports/items?f=json",
92
+ );
93
+ });
92
94
  });
93
95
  describe("WMS", () => {
94
96
  beforeEach(async () => {
95
97
  (layerModel = MAP_CTX_LAYER_WMS_FIXTURE),
96
- (layer = await createLayer(layerModel));
98
+ (layer = await createLayer(layerModel));
97
99
  });
98
100
  it("create a tile layer", () => {
99
101
  expect(layer).toBeTruthy();
@@ -133,7 +135,7 @@ describe("MapContextService", () => {
133
135
  describe("WFS", () => {
134
136
  beforeEach(async () => {
135
137
  (layerModel = MAP_CTX_LAYER_WFS_FIXTURE),
136
- (layer = await createLayer(layerModel));
138
+ (layer = await createLayer(layerModel));
137
139
  });
138
140
  it("create a vector layer", () => {
139
141
  expect(layer).toBeTruthy();
@@ -193,7 +195,7 @@ describe("MapContextService", () => {
193
195
  });
194
196
  describe("with inline data as string", () => {
195
197
  beforeEach(async () => {
196
- layerModel = {...MAP_CTX_LAYER_GEOJSON_FIXTURE};
198
+ layerModel = { ...MAP_CTX_LAYER_GEOJSON_FIXTURE };
197
199
  layerModel.data = JSON.stringify(layerModel.data);
198
200
  layer = await createLayer(layerModel);
199
201
  });
@@ -261,6 +263,34 @@ describe("MapContextService", () => {
261
263
  });
262
264
  });
263
265
  });
266
+
267
+ describe("WMTS", () => {
268
+ beforeEach(async () => {
269
+ (layerModel = MAP_CTX_LAYER_WMTS_FIXTURE),
270
+ (layer = await createLayer(layerModel));
271
+ });
272
+ it("create a tile layer", () => {
273
+ expect(layer).toBeTruthy();
274
+ expect(layer).toBeInstanceOf(TileLayer);
275
+ });
276
+ it("set correct layer properties", () => {
277
+ expect(layer.getVisible()).toBe(true);
278
+ expect(layer.getOpacity()).toBe(1);
279
+ expect(layer.get("label")).toBeUndefined();
280
+ expect(layer.getSource()?.getAttributions()).toBeNull();
281
+ });
282
+ it("create a WMTS source", () => {
283
+ const source = layer.getSource();
284
+ expect(source).toBeInstanceOf(WMTS);
285
+ });
286
+ it("set correct urls", () => {
287
+ const source = layer.getSource() as WMTS;
288
+ const urls = source.getUrls() ?? [];
289
+ expect(urls).toEqual([
290
+ "https://services.geo.sg.ch/wss/service/SG00066_WMTS/guest/tile/1.0.0/SG00066/{Style}/{TileMatrixSet}/{TileMatrix}/{TileRow}/{TileCol}",
291
+ ]);
292
+ });
293
+ });
264
294
  });
265
295
 
266
296
  describe("#createView", () => {
@@ -2,6 +2,7 @@ import {
2
2
  MapContext,
3
3
  MapContextLayer,
4
4
  MapContextView,
5
+ removeSearchParams,
5
6
  } from "@geospatial-sdk/core";
6
7
  import Map from "ol/Map";
7
8
  import View from "ol/View";
@@ -14,16 +15,20 @@ import VectorSource from "ol/source/Vector";
14
15
  import GeoJSON from "ol/format/GeoJSON";
15
16
  import Feature from "ol/Feature";
16
17
  import Geometry from "ol/geom/Geometry";
18
+ import SimpleGeometry from "ol/geom/SimpleGeometry";
17
19
  import { fromLonLat } from "ol/proj";
18
20
  import { bbox as bboxStrategy } from "ol/loadingstrategy";
19
- import { removeSearchParams } from "@geospatial-sdk/core";
20
21
  import { defaultStyle } from "./styles";
21
22
  import VectorTileLayer from "ol/layer/VectorTile";
22
- import {OGCMapTile, OGCVectorTile} from "ol/source";
23
- import {MVT} from "ol/format";
24
- import {OgcApiEndpoint, WfsEndpoint} from "@camptocamp/ogc-client";
23
+ import { OGCMapTile, OGCVectorTile, WMTS } from "ol/source";
24
+ import { MVT } from "ol/format";
25
+ import {
26
+ OgcApiEndpoint,
27
+ WfsEndpoint,
28
+ WmtsEndpoint,
29
+ } from "@camptocamp/ogc-client";
25
30
 
26
- const geosjonFormat = new GeoJSON();
31
+ const GEOJSON = new GeoJSON();
27
32
  const WFS_MAX_FEATURES = 10000;
28
33
 
29
34
  export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
@@ -49,33 +54,59 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
49
54
  }),
50
55
  });
51
56
  break;
52
- // TODO: implement when ogc-client can handle wmts
53
- // case 'wmts':
54
- // return new TileLayer({
55
- // source: new WMTS(layerModel.options),
56
- // })
57
- case "wfs":{
57
+ case "wmts": {
58
+ const olLayer = new TileLayer({});
59
+ const endpoint = new WmtsEndpoint(layerModel.url);
60
+ endpoint.isReady().then(async (endpoint) => {
61
+ const layerName = endpoint.getSingleLayerName() ?? layerModel.name;
62
+ const layer = endpoint.getLayerByName(layerName);
63
+ const matrixSet = layer.matrixSets[0];
64
+ const tileGrid = await endpoint.getOpenLayersTileGrid(layer.name);
65
+ if (tileGrid === null) {
66
+ console.warn("A WMTS tile grid could not be created", layerModel);
67
+ return;
68
+ }
69
+ const resourceUrl = layer.resourceLinks[0];
70
+ const dimensions = endpoint.getDefaultDimensions(layer.name);
71
+ olLayer.setSource(
72
+ new WMTS({
73
+ layer: layer.name,
74
+ style: layer.defaultStyle,
75
+ matrixSet: matrixSet.identifier,
76
+ format: resourceUrl.format,
77
+ url: resourceUrl.url,
78
+ requestEncoding: resourceUrl.encoding,
79
+ tileGrid,
80
+ projection: matrixSet.crs,
81
+ dimensions,
82
+ attributions: layerModel.attributions,
83
+ }),
84
+ );
85
+ });
86
+ return olLayer;
87
+ }
88
+ case "wfs": {
58
89
  const olLayer = new VectorLayer({
59
90
  style,
60
91
  });
61
92
  new WfsEndpoint(layerModel.url).isReady().then((endpoint) => {
62
93
  const featureType =
63
- endpoint.getSingleFeatureTypeName() ?? layerModel.featureType;
94
+ endpoint.getSingleFeatureTypeName() ?? layerModel.featureType;
64
95
  olLayer.setSource(
65
- new VectorSource({
66
- format: new GeoJSON(),
67
- url: function (extent) {
68
- return endpoint.getFeatureUrl(featureType, {
69
- maxFeatures: WFS_MAX_FEATURES,
70
- asJson: true,
71
- outputCrs: "EPSG:3857",
72
- extent: extent as [number, number, number, number],
73
- extentCrs: "EPSG:3857",
74
- });
75
- },
76
- strategy: bboxStrategy,
77
- attributions: layerModel.attributions,
78
- }),
96
+ new VectorSource({
97
+ format: new GeoJSON(),
98
+ url: function (extent) {
99
+ return endpoint.getFeatureUrl(featureType, {
100
+ maxFeatures: WFS_MAX_FEATURES,
101
+ asJson: true,
102
+ outputCrs: "EPSG:3857",
103
+ extent: extent as [number, number, number, number],
104
+ extentCrs: "EPSG:3857",
105
+ });
106
+ },
107
+ strategy: bboxStrategy,
108
+ attributions: layerModel.attributions,
109
+ }),
79
110
  );
80
111
  });
81
112
  layer = olLayer;
@@ -101,7 +132,7 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
101
132
  geojson = { type: "FeatureCollection", features: [] };
102
133
  }
103
134
  }
104
- const features = geosjonFormat.readFeatures(geojson, {
135
+ const features = GEOJSON.readFeatures(geojson, {
105
136
  featureProjection: "EPSG:3857",
106
137
  dataProjection: "EPSG:4326",
107
138
  }) as Feature<Geometry>[];
@@ -119,8 +150,11 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
119
150
  const ogcEndpoint = await new OgcApiEndpoint(layerModel.url);
120
151
  let layerUrl: string;
121
152
  if (layerModel.useTiles) {
122
- if (layerModel.useTiles === 'vector') {
123
- layerUrl = await ogcEndpoint.getVectorTilesetUrl(layerModel.collection, layerModel.tileMatrixSet);
153
+ if (layerModel.useTiles === "vector") {
154
+ layerUrl = await ogcEndpoint.getVectorTilesetUrl(
155
+ layerModel.collection,
156
+ layerModel.tileMatrixSet,
157
+ );
124
158
  layer = new VectorTileLayer({
125
159
  source: new OGCVectorTile({
126
160
  url: layerUrl,
@@ -128,8 +162,11 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
128
162
  attributions: layerModel.attributions,
129
163
  }),
130
164
  });
131
- } else if (layerModel.useTiles === 'map') {
132
- layerUrl = await ogcEndpoint.getMapTilesetUrl(layerModel.collection, layerModel.tileMatrixSet);
165
+ } else if (layerModel.useTiles === "map") {
166
+ layerUrl = await ogcEndpoint.getMapTilesetUrl(
167
+ layerModel.collection,
168
+ layerModel.tileMatrixSet,
169
+ );
133
170
  layer = new TileLayer({
134
171
  source: new OGCMapTile({
135
172
  url: layerUrl,
@@ -138,7 +175,10 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
138
175
  });
139
176
  }
140
177
  } else {
141
- layerUrl = await ogcEndpoint.getCollectionItemsUrl(layerModel.collection, layerModel.options);
178
+ layerUrl = await ogcEndpoint.getCollectionItemsUrl(
179
+ layerModel.collection,
180
+ layerModel.options,
181
+ );
142
182
  layer = new VectorLayer({
143
183
  source: new VectorSource({
144
184
  format: new GeoJSON(),
@@ -151,7 +191,7 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
151
191
  break;
152
192
  }
153
193
  default:
154
- throw new Error(`Unrecognized layer type: ${layerModel.type}`);
194
+ throw new Error(`Unrecognized layer type: ${JSON.stringify(layerModel)}`);
155
195
  }
156
196
  if (!layer) {
157
197
  throw new Error(`Layer could not be created for type: ${layerModel.type}`);
@@ -168,22 +208,28 @@ export async function createLayer(layerModel: MapContextLayer): Promise<Layer> {
168
208
  }
169
209
 
170
210
  export function createView(viewModel: MapContextView, map: Map): View {
171
- const { center: centerInViewProj, zoom, maxZoom, maxExtent } = viewModel;
172
- const center = centerInViewProj
173
- ? fromLonLat(centerInViewProj, "EPSG:3857")
174
- : [0, 0];
175
211
  const view = new View({
176
- center,
177
- zoom: zoom ?? 0,
178
- maxZoom,
179
- extent: maxExtent,
212
+ ...("maxExtent" in viewModel && { extent: viewModel.maxExtent }),
213
+ ...("maxZoom" in viewModel && { maxZoom: viewModel.maxZoom }),
180
214
  multiWorld: false,
181
215
  constrainResolution: true,
182
216
  });
183
- if (viewModel.extent) {
217
+ if ("geometry" in viewModel) {
218
+ const geom = GEOJSON.readGeometry(viewModel.geometry);
219
+ view.fit(geom as SimpleGeometry, {
220
+ size: map.getSize(),
221
+ });
222
+ } else if ("extent" in viewModel) {
184
223
  view.fit(viewModel.extent, {
185
224
  size: map.getSize(),
186
225
  });
226
+ } else {
227
+ const { center: centerInViewProj, zoom } = viewModel;
228
+ const center = centerInViewProj
229
+ ? fromLonLat(centerInViewProj, "EPSG:3857")
230
+ : [0, 0];
231
+ view.setCenter(center);
232
+ view.setZoom(zoom !== undefined ? zoom : 0);
187
233
  }
188
234
  return view;
189
235
  }
@@ -194,8 +240,8 @@ export function createView(viewModel: MapContextView, map: Map): View {
194
240
  * @param target
195
241
  */
196
242
  export async function createMapFromContext(
197
- context: MapContext,
198
- target?: string | HTMLElement,
243
+ context: MapContext,
244
+ target?: string | HTMLElement,
199
245
  ): Promise<Map> {
200
246
  const map = new Map({
201
247
  target,
@@ -208,7 +254,10 @@ export async function createMapFromContext(
208
254
  * @param map
209
255
  * @param context
210
256
  */
211
- export async function resetMapFromContext(map: Map, context: MapContext): Promise<Map> {
257
+ export async function resetMapFromContext(
258
+ map: Map,
259
+ context: MapContext,
260
+ ): Promise<Map> {
212
261
  map.setView(createView(context.view, map));
213
262
  map.getLayers().clear();
214
263
  for (const layerModel of context.layers) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@geospatial-sdk/openlayers",
3
- "version": "0.0.5-dev.13+4cd825d",
3
+ "version": "0.0.5-dev.15+751c3f3",
4
4
  "description": "OpenLayers-related utilities",
5
5
  "keywords": [
6
6
  "ol",
@@ -35,8 +35,8 @@
35
35
  "ol": ">6.x"
36
36
  },
37
37
  "dependencies": {
38
- "@geospatial-sdk/core": "^0.0.5-dev.13+4cd825d",
38
+ "@geospatial-sdk/core": "^0.0.5-dev.15+751c3f3",
39
39
  "chroma-js": "^2.4.2"
40
40
  },
41
- "gitHead": "4cd825d11d70758c227ca0b955bcf571282bfea7"
41
+ "gitHead": "751c3f38588b76394f9d27d6f76f862f0cff52e2"
42
42
  }