@open-pioneer/map 0.1.1 → 0.3.0

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,6 +1,8 @@
1
- import { createLogger } from '@open-pioneer/core';
1
+ import { createLogger, isAbortError } from '@open-pioneer/core';
2
+ import WMSCapabilities from 'ol/format/WMSCapabilities';
2
3
  import ImageLayer from 'ol/layer/Image';
3
4
  import ImageWMS from 'ol/source/ImageWMS';
5
+ import { fetchCapabilities } from '../../util/capabilities-utils.js';
4
6
  import { defer } from '../../util/defer.js';
5
7
  import { AbstractLayer } from '../AbstractLayer.js';
6
8
  import { AbstractLayerBase } from '../AbstractLayerBase.js';
@@ -13,6 +15,9 @@ class WMSLayerImpl extends AbstractLayer {
13
15
  #deferredSublayerUpdate;
14
16
  #layer;
15
17
  #source;
18
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
19
+ #capabilities;
20
+ #abortController = new AbortController();
16
21
  constructor(config) {
17
22
  const layer = new ImageLayer();
18
23
  super({
@@ -27,7 +32,7 @@ class WMSLayerImpl extends AbstractLayer {
27
32
  },
28
33
  // Use http service to load tiles; needed for authentication etc.
29
34
  imageLoadFunction: (wrapper, url) => {
30
- return this.#loadTile(wrapper, url).catch((error) => {
35
+ return this.#loadImage(wrapper, url).catch((error) => {
31
36
  LOG.error(`Failed to load tile at '${url}'`, error);
32
37
  });
33
38
  }
@@ -38,17 +43,55 @@ class WMSLayerImpl extends AbstractLayer {
38
43
  this.#sublayers = new SublayersCollectionImpl(constructSublayers(config.sublayers));
39
44
  this.#updateLayersParam();
40
45
  }
46
+ get legend() {
47
+ return void 0;
48
+ }
41
49
  get url() {
42
50
  return this.#url;
43
51
  }
52
+ get __source() {
53
+ return this.#source;
54
+ }
44
55
  get sublayers() {
45
56
  return this.#sublayers;
46
57
  }
58
+ get capabilities() {
59
+ return this.#capabilities;
60
+ }
47
61
  __attach(map) {
48
62
  super.__attach(map);
49
63
  for (const sublayer of this.#sublayers.getSublayers()) {
50
64
  sublayer.__attach(map, this, this);
51
65
  }
66
+ const layers = [];
67
+ const getNestedSublayer = (sublayers, layers2) => {
68
+ for (const sublayer of sublayers) {
69
+ const nested = sublayer.sublayers.getSublayers();
70
+ if (nested.length) {
71
+ getNestedSublayer(nested, layers2);
72
+ } else {
73
+ if (sublayer.name) {
74
+ layers2.push(sublayer);
75
+ }
76
+ }
77
+ }
78
+ };
79
+ this.#fetchWMSCapabilities().then((result) => {
80
+ const parser = new WMSCapabilities();
81
+ const capabilities = parser.read(result);
82
+ this.#capabilities = capabilities;
83
+ getNestedSublayer(this.#sublayers.getSublayers(), layers);
84
+ for (const layer of layers) {
85
+ const legendUrl = getWMSLegendUrl(capabilities, layer.name);
86
+ layer.legend = legendUrl;
87
+ }
88
+ }).catch((error) => {
89
+ if (isAbortError(error)) {
90
+ LOG.error(`Layer ${this.id} has been destroyed before fetching the data`);
91
+ return;
92
+ }
93
+ LOG.error(`Failed fetching WMS capabilities for Layer ${this.id}`, error);
94
+ });
52
95
  }
53
96
  /** Called by the sublayers when their visibility changed. */
54
97
  __updateSublayerVisibility() {
@@ -90,7 +133,9 @@ class WMSLayerImpl extends AbstractLayer {
90
133
  visitSublayer(nestedSublayer);
91
134
  }
92
135
  } else {
93
- layers.push(sublayer.name);
136
+ if (sublayer.name) {
137
+ layers.push(sublayer.name);
138
+ }
94
139
  }
95
140
  };
96
141
  for (const sublayer of this.sublayers.__getRawSublayers()) {
@@ -98,25 +143,35 @@ class WMSLayerImpl extends AbstractLayer {
98
143
  }
99
144
  return layers;
100
145
  }
101
- async #loadTile(imageWrapper, tileUrl) {
146
+ async #fetchWMSCapabilities() {
102
147
  const httpService = this.map.__sharedDependencies.httpService;
103
- const response = await httpService.fetch(tileUrl);
148
+ const url = `${this.#url}?LANGUAGE=ger&SERVICE=WMS&REQUEST=GetCapabilities`;
149
+ return fetchCapabilities(url, httpService, this.#abortController.signal);
150
+ }
151
+ async #loadImage(imageWrapper, imageUrl) {
152
+ const httpService = this.map.__sharedDependencies.httpService;
153
+ const image = imageWrapper.getImage();
154
+ const response = await httpService.fetch(imageUrl);
104
155
  if (!response.ok) {
105
156
  throw new Error(`Request failed with status ${response.status}.`);
106
157
  }
107
158
  const blob = await response.blob();
108
159
  const objectUrl = URL.createObjectURL(blob);
109
- const image = imageWrapper.getImage();
110
- image.src = objectUrl;
111
- image.onload = () => {
160
+ const finish = () => {
112
161
  URL.revokeObjectURL(objectUrl);
162
+ image.removeEventListener("load", finish);
163
+ image.removeEventListener("error", finish);
113
164
  };
165
+ image.addEventListener("load", finish);
166
+ image.addEventListener("error", finish);
167
+ image.src = objectUrl;
114
168
  }
115
169
  }
116
170
  class WMSSublayerImpl extends AbstractLayerBase {
117
171
  #parent;
118
172
  #parentLayer;
119
173
  #name;
174
+ #legend;
120
175
  #sublayers;
121
176
  #visible;
122
177
  constructor(config) {
@@ -145,6 +200,13 @@ class WMSSublayerImpl extends AbstractLayerBase {
145
200
  }
146
201
  return parentLayer;
147
202
  }
203
+ get legend() {
204
+ return this.#legend;
205
+ }
206
+ set legend(legendUrl) {
207
+ this.#legend = legendUrl;
208
+ this.__emitChangeEvent("changed:legend");
209
+ }
148
210
  /**
149
211
  * Called by the parent layer when it is attached to the map to attach all sublayers.
150
212
  */
@@ -192,6 +254,31 @@ function constructSublayers(sublayerConfigs = []) {
192
254
  throw new Error("Failed to construct sublayers.", { cause: e });
193
255
  }
194
256
  }
257
+ function getWMSLegendUrl(capabilities, layerName) {
258
+ const capabilitiesContent = capabilities?.Capability;
259
+ const rootLayerCapabilities = capabilitiesContent?.Layer;
260
+ let url = void 0;
261
+ const searchNestedLayer = (layer) => {
262
+ for (const currentLayer of layer) {
263
+ if (currentLayer?.Name === layerName) {
264
+ const activeLayer = currentLayer;
265
+ const styles = activeLayer.Style;
266
+ if (!styles || !styles.length) {
267
+ LOG.debug("No style in WMS layer capabilities - giving up.");
268
+ return;
269
+ }
270
+ const activeStyle = styles[0];
271
+ url = activeStyle.LegendURL?.[0]?.OnlineResource;
272
+ } else if (currentLayer.Layer) {
273
+ searchNestedLayer(currentLayer.Layer);
274
+ }
275
+ }
276
+ };
277
+ if (rootLayerCapabilities) {
278
+ searchNestedLayer(rootLayerCapabilities.Layer);
279
+ }
280
+ return url;
281
+ }
195
282
 
196
- export { WMSLayerImpl };
283
+ export { WMSLayerImpl, getWMSLegendUrl };
197
284
  //# sourceMappingURL=WMSLayerImpl.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"WMSLayerImpl.js","sources":["WMSLayerImpl.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger } from \"@open-pioneer/core\";\nimport ImageLayer from \"ol/layer/Image\";\nimport type ImageSource from \"ol/source/Image\";\nimport ImageWMS from \"ol/source/ImageWMS\";\nimport { Sublayer, WMSLayerConfig, WMSLayer, WMSSublayerConfig } from \"../../api\";\nimport { DeferredExecution, defer } from \"../../util/defer\";\nimport { AbstractLayer } from \"../AbstractLayer\";\nimport { AbstractLayerBase } from \"../AbstractLayerBase\";\nimport { MapModelImpl } from \"../MapModelImpl\";\nimport { SublayersCollectionImpl } from \"../SublayersCollectionImpl\";\nimport { ImageWrapper } from \"ol\";\n\nconst LOG = createLogger(\"map:WMSLayer\");\n\nexport class WMSLayerImpl extends AbstractLayer implements WMSLayer {\n #url: string;\n #sublayers: SublayersCollectionImpl<WMSSublayerImpl>;\n #deferredSublayerUpdate: DeferredExecution | undefined;\n #layer: ImageLayer<ImageSource>;\n #source: ImageWMS;\n\n constructor(config: WMSLayerConfig) {\n const layer = new ImageLayer();\n super({\n ...config,\n olLayer: layer\n });\n const source = new ImageWMS({\n ...config.sourceOptions,\n url: config.url,\n params: {\n ...config.sourceOptions?.params\n },\n // Use http service to load tiles; needed for authentication etc.\n imageLoadFunction: (wrapper, url) => {\n return this.#loadTile(wrapper, url).catch((error) => {\n LOG.error(`Failed to load tile at '${url}'`, error);\n });\n }\n });\n this.#url = config.url;\n this.#source = source;\n this.#layer = layer;\n this.#sublayers = new SublayersCollectionImpl(constructSublayers(config.sublayers));\n this.#updateLayersParam();\n }\n\n get url(): string {\n return this.#url;\n }\n\n get sublayers(): SublayersCollectionImpl<WMSSublayerImpl> {\n return this.#sublayers;\n }\n\n __attach(map: MapModelImpl): void {\n super.__attach(map);\n for (const sublayer of this.#sublayers.getSublayers()) {\n sublayer.__attach(map, this, this);\n }\n }\n\n /** Called by the sublayers when their visibility changed. */\n __updateSublayerVisibility() {\n if (this.#deferredSublayerUpdate?.reschedule()) {\n return;\n }\n this.#deferredSublayerUpdate = defer(() => {\n try {\n this.#updateLayersParam();\n this.#deferredSublayerUpdate = undefined;\n } catch (e) {\n LOG.error(`Failed to update sublayer visibility on WMS layer '${this.id}'.`, e);\n }\n });\n }\n\n /**\n * Gathers the visibility of _all_ sublayers and assembles the 'layers' WMS parameter.\n * The parameters are then applied to the WMS source.\n */\n #updateLayersParam() {\n const layers = this.#getVisibleLayerNames();\n this.#source.updateParams({\n \"LAYERS\": layers\n });\n\n // only set source if there are visible sublayers, otherwise\n // we send an invalid http request\n const source = layers.length === 0 ? null : this.#source;\n if (this.#layer.getSource() !== source) {\n this.#layer.setSource(source);\n }\n }\n\n #getVisibleLayerNames() {\n const layers: string[] = [];\n const visitSublayer = (sublayer: WMSSublayerImpl) => {\n if (!sublayer.visible) {\n return;\n }\n\n const nestedSublayers = sublayer.sublayers.__getRawSublayers();\n if (nestedSublayers.length) {\n for (const nestedSublayer of nestedSublayers) {\n visitSublayer(nestedSublayer);\n }\n } else {\n layers.push(sublayer.name);\n }\n };\n\n for (const sublayer of this.sublayers.__getRawSublayers()) {\n visitSublayer(sublayer);\n }\n return layers;\n }\n\n async #loadTile(imageWrapper: ImageWrapper, tileUrl: string): Promise<void> {\n const httpService = this.map.__sharedDependencies.httpService;\n const response = await httpService.fetch(tileUrl);\n if (!response.ok) {\n throw new Error(`Request failed with status ${response.status}.`);\n }\n\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const image = imageWrapper.getImage() as HTMLImageElement;\n image.src = objectUrl;\n image.onload = () => {\n // Cleanup object URL after load to prevent memory leaks.\n // https://stackoverflow.com/questions/62473876/openlayers-6-settileloadfunction-documented-example-uses-url-createobjecturld\n URL.revokeObjectURL(objectUrl);\n };\n }\n}\n\nclass WMSSublayerImpl extends AbstractLayerBase implements Sublayer {\n #parent: WMSSublayerImpl | WMSLayerImpl | undefined;\n #parentLayer: WMSLayerImpl | undefined;\n #name: string;\n #sublayers: SublayersCollectionImpl<WMSSublayerImpl>;\n #visible: boolean;\n\n constructor(config: WMSSublayerConfig) {\n super(config);\n this.#name = config.name;\n this.#visible = config.visible ?? true;\n this.#sublayers = new SublayersCollectionImpl(constructSublayers(config.sublayers));\n }\n\n get name(): string {\n return this.#name;\n }\n\n get sublayers(): SublayersCollectionImpl<WMSSublayerImpl> {\n return this.#sublayers;\n }\n\n get parent(): WMSSublayerImpl | WMSLayerImpl {\n const parent = this.#parent;\n if (!parent) {\n throw new Error(`WMS sublayer ${this.id} has not been attached to its parent yet.`);\n }\n return parent;\n }\n\n get parentLayer(): WMSLayerImpl {\n const parentLayer = this.#parentLayer;\n if (!parentLayer) {\n throw new Error(`WMS sublayer ${this.id} has not been attached to its parent yet.`);\n }\n return parentLayer;\n }\n\n /**\n * Called by the parent layer when it is attached to the map to attach all sublayers.\n */\n __attach(\n map: MapModelImpl,\n parentLayer: WMSLayerImpl,\n parent: WMSLayerImpl | WMSSublayerImpl\n ): void {\n super.__attachToMap(map);\n if (this.#parent) {\n throw new Error(\n `WMS sublayer '${this.id}' has already been attached to parent '${this.#parent.id}'`\n );\n }\n this.#parent = parent;\n if (this.#parentLayer) {\n throw new Error(\n `WMS sublayer '${this.id}' has already been attached to parent layer '${this.#parentLayer.id}'`\n );\n }\n this.#parentLayer = parentLayer;\n\n // Recurse into nested sublayers\n for (const sublayer of this.sublayers.__getRawSublayers()) {\n sublayer.__attach(map, parentLayer, this);\n }\n }\n\n get visible(): boolean {\n return this.#visible;\n }\n\n setVisible(newVisibility: boolean): void {\n if (this.visible !== newVisibility) {\n this.#visible = newVisibility;\n this.#parentLayer?.__updateSublayerVisibility();\n this.__emitChangeEvent(\"changed:visible\");\n }\n }\n}\n\nfunction constructSublayers(sublayerConfigs: WMSSublayerConfig[] = []): WMSSublayerImpl[] {\n const sublayers: WMSSublayerImpl[] = [];\n try {\n for (const sublayerConfig of sublayerConfigs) {\n sublayers.push(new WMSSublayerImpl(sublayerConfig));\n }\n return sublayers;\n } catch (e) {\n // Ensure previous sublayers are destroyed if a single constructor throws\n while (sublayers.length) {\n const layer = sublayers.pop()!;\n layer?.destroy();\n }\n throw new Error(\"Failed to construct sublayers.\", { cause: e });\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"WMSLayerImpl.js","sources":["WMSLayerImpl.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger, isAbortError } from \"@open-pioneer/core\";\nimport { ImageWrapper } from \"ol\";\nimport WMSCapabilities from \"ol/format/WMSCapabilities\";\nimport ImageLayer from \"ol/layer/Image\";\nimport type ImageSource from \"ol/source/Image\";\nimport ImageWMS from \"ol/source/ImageWMS\";\nimport { WMSLayer, WMSLayerConfig, WMSSublayer, WMSSublayerConfig } from \"../../api\";\nimport { fetchCapabilities } from \"../../util/capabilities-utils\";\nimport { DeferredExecution, defer } from \"../../util/defer\";\nimport { AbstractLayer } from \"../AbstractLayer\";\nimport { AbstractLayerBase } from \"../AbstractLayerBase\";\nimport { MapModelImpl } from \"../MapModelImpl\";\nimport { SublayersCollectionImpl } from \"../SublayersCollectionImpl\";\n\nconst LOG = createLogger(\"map:WMSLayer\");\n\nexport class WMSLayerImpl extends AbstractLayer implements WMSLayer {\n #url: string;\n #sublayers: SublayersCollectionImpl<WMSSublayerImpl>;\n #deferredSublayerUpdate: DeferredExecution | undefined;\n #layer: ImageLayer<ImageSource>;\n #source: ImageWMS;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n #capabilities: Record<string, any> | undefined;\n readonly #abortController = new AbortController();\n\n constructor(config: WMSLayerConfig) {\n const layer = new ImageLayer();\n super({\n ...config,\n olLayer: layer\n });\n const source = new ImageWMS({\n ...config.sourceOptions,\n url: config.url,\n params: {\n ...config.sourceOptions?.params\n },\n // Use http service to load tiles; needed for authentication etc.\n imageLoadFunction: (wrapper, url) => {\n return this.#loadImage(wrapper, url).catch((error) => {\n LOG.error(`Failed to load tile at '${url}'`, error);\n });\n }\n });\n this.#url = config.url;\n this.#source = source;\n this.#layer = layer;\n this.#sublayers = new SublayersCollectionImpl(constructSublayers(config.sublayers));\n this.#updateLayersParam();\n }\n\n get legend() {\n return undefined;\n }\n\n get url(): string {\n return this.#url;\n }\n get __source() {\n return this.#source;\n }\n\n get sublayers(): SublayersCollectionImpl<WMSSublayerImpl> {\n return this.#sublayers;\n }\n\n get capabilities() {\n return this.#capabilities;\n }\n\n __attach(map: MapModelImpl): void {\n super.__attach(map);\n for (const sublayer of this.#sublayers.getSublayers()) {\n sublayer.__attach(map, this, this);\n }\n const layers: WMSSublayerImpl[] = [];\n /** identify all leaf nodes representing a layer in the structure */\n const getNestedSublayer = (sublayers: WMSSublayerImpl[], layers: WMSSublayerImpl[]) => {\n for (const sublayer of sublayers) {\n const nested = sublayer.sublayers.getSublayers();\n if (nested.length) {\n getNestedSublayer(nested, layers);\n } else {\n if (sublayer.name) {\n layers.push(sublayer);\n }\n }\n }\n };\n this.#fetchWMSCapabilities()\n .then((result: string) => {\n const parser = new WMSCapabilities();\n const capabilities = parser.read(result);\n this.#capabilities = capabilities;\n getNestedSublayer(this.#sublayers.getSublayers(), layers);\n\n for (const layer of layers) {\n const legendUrl = getWMSLegendUrl(capabilities, layer.name!);\n layer.legend = legendUrl;\n }\n })\n .catch((error) => {\n if (isAbortError(error)) {\n LOG.error(`Layer ${this.id} has been destroyed before fetching the data`);\n return;\n }\n LOG.error(`Failed fetching WMS capabilities for Layer ${this.id}`, error);\n });\n }\n\n /** Called by the sublayers when their visibility changed. */\n __updateSublayerVisibility() {\n if (this.#deferredSublayerUpdate?.reschedule()) {\n return;\n }\n this.#deferredSublayerUpdate = defer(() => {\n try {\n this.#updateLayersParam();\n this.#deferredSublayerUpdate = undefined;\n } catch (e) {\n LOG.error(`Failed to update sublayer visibility on WMS layer '${this.id}'.`, e);\n }\n });\n }\n\n /**\n * Gathers the visibility of _all_ sublayers and assembles the 'layers' WMS parameter.\n * The parameters are then applied to the WMS source.\n */\n #updateLayersParam() {\n const layers = this.#getVisibleLayerNames();\n this.#source.updateParams({\n \"LAYERS\": layers\n });\n\n // only set source if there are visible sublayers, otherwise\n // we send an invalid http request\n const source = layers.length === 0 ? null : this.#source;\n if (this.#layer.getSource() !== source) {\n this.#layer.setSource(source);\n }\n }\n\n #getVisibleLayerNames() {\n const layers: string[] = [];\n const visitSublayer = (sublayer: WMSSublayerImpl) => {\n if (!sublayer.visible) {\n return;\n }\n\n const nestedSublayers = sublayer.sublayers.__getRawSublayers();\n if (nestedSublayers.length) {\n for (const nestedSublayer of nestedSublayers) {\n visitSublayer(nestedSublayer);\n }\n } else {\n /**\n * Push sublayer only, if layer name is not an empty string | undefined | ...\n */\n if (sublayer.name) {\n layers.push(sublayer.name);\n }\n }\n };\n\n for (const sublayer of this.sublayers.__getRawSublayers()) {\n visitSublayer(sublayer);\n }\n return layers;\n }\n\n async #fetchWMSCapabilities(): Promise<string> {\n const httpService = this.map.__sharedDependencies.httpService;\n const url = `${this.#url}?LANGUAGE=ger&SERVICE=WMS&REQUEST=GetCapabilities`;\n return fetchCapabilities(url, httpService, this.#abortController.signal);\n }\n\n async #loadImage(imageWrapper: ImageWrapper, imageUrl: string): Promise<void> {\n const httpService = this.map.__sharedDependencies.httpService;\n const image = imageWrapper.getImage() as HTMLImageElement;\n\n const response = await httpService.fetch(imageUrl);\n if (!response.ok) {\n throw new Error(`Request failed with status ${response.status}.`);\n }\n\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const finish = () => {\n // Cleanup object URL after load to prevent memory leaks.\n // https://stackoverflow.com/questions/62473876/openlayers-6-settileloadfunction-documented-example-uses-url-createobjecturld\n URL.revokeObjectURL(objectUrl);\n image.removeEventListener(\"load\", finish);\n image.removeEventListener(\"error\", finish);\n };\n\n image.addEventListener(\"load\", finish);\n image.addEventListener(\"error\", finish);\n image.src = objectUrl;\n }\n}\n\nclass WMSSublayerImpl extends AbstractLayerBase implements WMSSublayer {\n #parent: WMSSublayerImpl | WMSLayerImpl | undefined;\n #parentLayer: WMSLayerImpl | undefined;\n #name: string | undefined;\n #legend: string | undefined;\n #sublayers: SublayersCollectionImpl<WMSSublayerImpl>;\n #visible: boolean;\n\n constructor(config: WMSSublayerConfig) {\n super(config);\n this.#name = config.name;\n this.#visible = config.visible ?? true;\n this.#sublayers = new SublayersCollectionImpl(constructSublayers(config.sublayers));\n }\n\n get name(): string | undefined {\n return this.#name;\n }\n\n get sublayers(): SublayersCollectionImpl<WMSSublayerImpl> {\n return this.#sublayers;\n }\n\n get parent(): WMSSublayerImpl | WMSLayerImpl {\n const parent = this.#parent;\n if (!parent) {\n throw new Error(`WMS sublayer ${this.id} has not been attached to its parent yet.`);\n }\n return parent;\n }\n\n get parentLayer(): WMSLayerImpl {\n const parentLayer = this.#parentLayer;\n if (!parentLayer) {\n throw new Error(`WMS sublayer ${this.id} has not been attached to its parent yet.`);\n }\n return parentLayer;\n }\n get legend(): string | undefined {\n return this.#legend;\n }\n\n set legend(legendUrl: string | undefined) {\n this.#legend = legendUrl;\n this.__emitChangeEvent(\"changed:legend\");\n }\n\n /**\n * Called by the parent layer when it is attached to the map to attach all sublayers.\n */\n __attach(\n map: MapModelImpl,\n parentLayer: WMSLayerImpl,\n parent: WMSLayerImpl | WMSSublayerImpl\n ): void {\n super.__attachToMap(map);\n if (this.#parent) {\n throw new Error(\n `WMS sublayer '${this.id}' has already been attached to parent '${this.#parent.id}'`\n );\n }\n this.#parent = parent;\n if (this.#parentLayer) {\n throw new Error(\n `WMS sublayer '${this.id}' has already been attached to parent layer '${this.#parentLayer.id}'`\n );\n }\n this.#parentLayer = parentLayer;\n\n // Recurse into nested sublayers\n for (const sublayer of this.sublayers.__getRawSublayers()) {\n sublayer.__attach(map, parentLayer, this);\n }\n }\n\n get visible(): boolean {\n return this.#visible;\n }\n\n setVisible(newVisibility: boolean): void {\n if (this.visible !== newVisibility) {\n this.#visible = newVisibility;\n this.#parentLayer?.__updateSublayerVisibility();\n this.__emitChangeEvent(\"changed:visible\");\n }\n }\n}\n\nfunction constructSublayers(sublayerConfigs: WMSSublayerConfig[] = []): WMSSublayerImpl[] {\n const sublayers: WMSSublayerImpl[] = [];\n try {\n for (const sublayerConfig of sublayerConfigs) {\n sublayers.push(new WMSSublayerImpl(sublayerConfig));\n }\n return sublayers;\n } catch (e) {\n // Ensure previous sublayers are destroyed if a single constructor throws\n while (sublayers.length) {\n const layer = sublayers.pop()!;\n layer?.destroy();\n }\n throw new Error(\"Failed to construct sublayers.\", { cause: e });\n }\n}\n\n/** extract the legend url from the service capabilities */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getWMSLegendUrl(capabilities: Record<string, any>, layerName: string) {\n const capabilitiesContent = capabilities?.Capability;\n const rootLayerCapabilities = capabilitiesContent?.Layer;\n let url: string | undefined = undefined;\n\n /** Recurse search for the currrent layer within the parsed capabilities service*/\n //eslint-disable-next-line @typescript-eslint/no-explicit-any\n const searchNestedLayer = (layer: Record<string, any>[]) => {\n for (const currentLayer of layer) {\n // spec. if, a layer has a <Name>, then it is a map layer\n if (currentLayer?.Name === layerName) {\n const activeLayer = currentLayer;\n const styles = activeLayer.Style;\n if (!styles || !styles.length) {\n LOG.debug(\"No style in WMS layer capabilities - giving up.\");\n return;\n }\n // by parsing of the service capabilities, every child inherits the parent's legend\n // theorfore, extract the legendURL from the first style object in the array (its own legend)\n const activeStyle = styles[0];\n url = activeStyle.LegendURL?.[0]?.OnlineResource;\n } else if (currentLayer.Layer) {\n searchNestedLayer(currentLayer.Layer);\n }\n }\n };\n if (rootLayerCapabilities) {\n searchNestedLayer(rootLayerCapabilities.Layer);\n }\n return url;\n}\n"],"names":["layers"],"mappings":"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"}
@@ -0,0 +1,19 @@
1
+ import TileLayer from "ol/layer/Tile";
2
+ import type TileSourceType from "ol/source/Tile";
3
+ import { WMTSLayer, WMTSLayerConfig } from "../../api";
4
+ import { AbstractLayer } from "../AbstractLayer";
5
+ import { MapModelImpl } from "../MapModelImpl";
6
+ export declare class WMTSLayerImpl extends AbstractLayer implements WMTSLayer {
7
+ #private;
8
+ constructor(config: WMTSLayerConfig);
9
+ destroy(): void;
10
+ get legend(): string | undefined;
11
+ __attach(map: MapModelImpl): void;
12
+ get layer(): TileLayer<TileSourceType>;
13
+ get url(): string;
14
+ get name(): string;
15
+ get matrixSet(): string;
16
+ get attributions(): string | undefined;
17
+ get sublayers(): undefined;
18
+ }
19
+ export declare function getWMTSLegendUrl(capabilities: Record<string, any>, activeLayerId: string | undefined, activeStyleId: string | undefined): string | undefined;
@@ -0,0 +1,154 @@
1
+ import { createLogger, isAbortError } from '@open-pioneer/core';
2
+ import TileState from 'ol/TileState';
3
+ import WMTSCapabilities from 'ol/format/WMTSCapabilities';
4
+ import TileLayer from 'ol/layer/Tile';
5
+ import WMTS, { optionsFromCapabilities } from 'ol/source/WMTS';
6
+ import { fetchCapabilities } from '../../util/capabilities-utils.js';
7
+ import { AbstractLayer } from '../AbstractLayer.js';
8
+ import { ImageTile } from 'ol';
9
+
10
+ const LOG = createLogger("map:WMTSLayer");
11
+ class WMTSLayerImpl extends AbstractLayer {
12
+ #url;
13
+ #name;
14
+ #matrixSet;
15
+ #attributions;
16
+ #layer;
17
+ #source;
18
+ #legend;
19
+ #abortController = new AbortController();
20
+ constructor(config) {
21
+ const layer = new TileLayer();
22
+ super({
23
+ ...config,
24
+ olLayer: layer
25
+ });
26
+ this.#url = config.url;
27
+ this.#name = config.name;
28
+ this.#layer = layer;
29
+ this.#matrixSet = config.matrixSet;
30
+ }
31
+ destroy() {
32
+ super.destroy();
33
+ this.#abortController.abort();
34
+ }
35
+ get legend() {
36
+ return this.#legend;
37
+ }
38
+ __attach(map) {
39
+ super.__attach(map);
40
+ this.#fetchWMTSCapabilities().then((result) => {
41
+ const parser = new WMTSCapabilities();
42
+ const capabilities = parser.read(result);
43
+ const options = optionsFromCapabilities(capabilities, {
44
+ layer: this.#name,
45
+ matrixSet: this.#matrixSet
46
+ });
47
+ if (!options) {
48
+ throw new Error("Layer was not found in capabilities");
49
+ }
50
+ const source = new WMTS({
51
+ ...options,
52
+ tileLoadFunction: (tile, tileUrl) => {
53
+ this.#loadTile(tile, tileUrl);
54
+ }
55
+ });
56
+ this.#source = source;
57
+ this.#layer.setSource(this.#source);
58
+ const activeStyleId = source.getStyle();
59
+ const legendUrl = getWMTSLegendUrl(capabilities, this.name, activeStyleId);
60
+ this.#legend = legendUrl;
61
+ this.__emitChangeEvent("changed:legend");
62
+ }).catch((error) => {
63
+ if (isAbortError(error)) {
64
+ LOG.error(`Layer ${this.name} has been destroyed before fetching the data`);
65
+ return;
66
+ }
67
+ LOG.error(`Failed fetching WMTS capabilities for Layer ${this.name}`, error);
68
+ });
69
+ }
70
+ get layer() {
71
+ return this.#layer;
72
+ }
73
+ get url() {
74
+ return this.#url;
75
+ }
76
+ get name() {
77
+ return this.#name;
78
+ }
79
+ get matrixSet() {
80
+ return this.#matrixSet;
81
+ }
82
+ get attributions() {
83
+ return this.#attributions;
84
+ }
85
+ get sublayers() {
86
+ return void 0;
87
+ }
88
+ async #fetchWMTSCapabilities() {
89
+ const httpService = this.map.__sharedDependencies.httpService;
90
+ return fetchCapabilities(this.#url, httpService, this.#abortController.signal);
91
+ }
92
+ async #loadTile(tile, tileUrl) {
93
+ const httpService = this.map.__sharedDependencies.httpService;
94
+ try {
95
+ if (!(tile instanceof ImageTile)) {
96
+ throw new Error("Only 'ImageTile' is supported for now.");
97
+ }
98
+ const image = tile.getImage();
99
+ if (!isHtmlImage(image)) {
100
+ throw new Error("Only <img> tags are supported as tiles for now.");
101
+ }
102
+ const response = await httpService.fetch(tileUrl);
103
+ if (!response.ok) {
104
+ throw new Error(`Tile request failed with status ${response.status}.`);
105
+ }
106
+ const blob = await response.blob();
107
+ const objectUrl = URL.createObjectURL(blob);
108
+ const finish = () => {
109
+ URL.revokeObjectURL(objectUrl);
110
+ image.removeEventListener("load", finish);
111
+ image.removeEventListener("error", finish);
112
+ };
113
+ image.addEventListener("load", finish);
114
+ image.addEventListener("error", finish);
115
+ image.src = objectUrl;
116
+ } catch (e) {
117
+ tile.setState(TileState.ERROR);
118
+ if (!isAbortError(e)) {
119
+ LOG.error("Failed to load tile", e);
120
+ }
121
+ }
122
+ }
123
+ }
124
+ function isHtmlImage(htmlElement) {
125
+ return htmlElement.tagName === "IMG";
126
+ }
127
+ function getWMTSLegendUrl(capabilities, activeLayerId, activeStyleId) {
128
+ const content = capabilities?.Contents;
129
+ const layers = content?.Layer;
130
+ let activeLayer = layers?.find((layer) => layer?.Identifier === activeLayerId);
131
+ if (!activeLayer) {
132
+ LOG.debug("Failed to find the active layer in WMTS layer capabilities.");
133
+ activeLayer = layers?.[0];
134
+ if (!activeLayer) {
135
+ LOG.debug("No layer in WMTS capabilities - giving up.");
136
+ return void 0;
137
+ }
138
+ }
139
+ const styles = activeLayer.Style;
140
+ let activeStyle = styles?.find((style) => style?.Identifier === activeStyleId);
141
+ if (!activeStyle) {
142
+ LOG.debug("Failed to find active style in WMTS layer.");
143
+ activeStyle = styles?.[0];
144
+ if (!activeStyle) {
145
+ LOG.debug("No style in WMTS layer capabilities - giving up.");
146
+ return void 0;
147
+ }
148
+ }
149
+ const legendUrl = activeStyle.LegendURL?.[0]?.href;
150
+ return legendUrl;
151
+ }
152
+
153
+ export { WMTSLayerImpl, getWMTSLegendUrl };
154
+ //# sourceMappingURL=WMTSLayerImpl.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"WMTSLayerImpl.js","sources":["WMTSLayerImpl.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger, isAbortError } from \"@open-pioneer/core\";\nimport Tile from \"ol/Tile\";\nimport TileState from \"ol/TileState\";\nimport WMTSCapabilities from \"ol/format/WMTSCapabilities\";\nimport TileLayer from \"ol/layer/Tile\";\nimport type TileSourceType from \"ol/source/Tile\";\nimport WMTS, { optionsFromCapabilities } from \"ol/source/WMTS\";\nimport { WMTSLayer, WMTSLayerConfig } from \"../../api\";\nimport { fetchCapabilities } from \"../../util/capabilities-utils\";\nimport { AbstractLayer } from \"../AbstractLayer\";\nimport { MapModelImpl } from \"../MapModelImpl\";\nimport { ImageTile } from \"ol\";\n\nconst LOG = createLogger(\"map:WMTSLayer\");\n\nexport class WMTSLayerImpl extends AbstractLayer implements WMTSLayer {\n #url: string;\n #name: string;\n #matrixSet: string;\n #attributions?: string | undefined;\n #layer: TileLayer<TileSourceType>;\n #source: WMTS | undefined;\n #legend: string | undefined;\n readonly #abortController = new AbortController();\n\n constructor(config: WMTSLayerConfig) {\n const layer = new TileLayer();\n super({\n ...config,\n olLayer: layer\n });\n this.#url = config.url;\n this.#name = config.name;\n this.#layer = layer;\n this.#matrixSet = config.matrixSet;\n }\n\n destroy(): void {\n super.destroy();\n this.#abortController.abort();\n }\n\n get legend(): string | undefined {\n return this.#legend;\n }\n\n __attach(map: MapModelImpl): void {\n super.__attach(map);\n this.#fetchWMTSCapabilities()\n .then((result: string) => {\n const parser = new WMTSCapabilities();\n const capabilities = parser.read(result);\n const options = optionsFromCapabilities(capabilities, {\n layer: this.#name,\n matrixSet: this.#matrixSet\n });\n if (!options) {\n throw new Error(\"Layer was not found in capabilities\");\n }\n const source = new WMTS({\n ...options,\n tileLoadFunction: (tile, tileUrl) => {\n this.#loadTile(tile, tileUrl);\n }\n });\n this.#source = source;\n this.#layer.setSource(this.#source);\n const activeStyleId = source.getStyle();\n const legendUrl = getWMTSLegendUrl(capabilities, this.name, activeStyleId);\n this.#legend = legendUrl;\n this.__emitChangeEvent(\"changed:legend\");\n })\n .catch((error) => {\n if (isAbortError(error)) {\n LOG.error(`Layer ${this.name} has been destroyed before fetching the data`);\n return;\n }\n LOG.error(`Failed fetching WMTS capabilities for Layer ${this.name}`, error);\n });\n }\n\n get layer() {\n return this.#layer;\n }\n\n get url() {\n return this.#url;\n }\n\n get name() {\n return this.#name;\n }\n\n get matrixSet() {\n return this.#matrixSet;\n }\n\n get attributions() {\n return this.#attributions;\n }\n\n get sublayers(): undefined {\n return undefined;\n }\n\n async #fetchWMTSCapabilities(): Promise<string> {\n const httpService = this.map.__sharedDependencies.httpService;\n return fetchCapabilities(this.#url, httpService, this.#abortController.signal);\n }\n\n async #loadTile(tile: Tile, tileUrl: string): Promise<void> {\n const httpService = this.map.__sharedDependencies.httpService;\n try {\n if (!(tile instanceof ImageTile)) {\n throw new Error(\"Only 'ImageTile' is supported for now.\");\n }\n\n const image = tile.getImage();\n if (!isHtmlImage(image)) {\n // Could also be canvas or video\n throw new Error(\"Only <img> tags are supported as tiles for now.\");\n }\n\n const response = await httpService.fetch(tileUrl);\n if (!response.ok) {\n throw new Error(`Tile request failed with status ${response.status}.`);\n }\n\n const blob = await response.blob();\n const objectUrl = URL.createObjectURL(blob);\n const finish = () => {\n // Cleanup object URL after load to prevent memory leaks.\n // https://stackoverflow.com/questions/62473876/openlayers-6-settileloadfunction-documented-example-uses-url-createobjecturld\n URL.revokeObjectURL(objectUrl);\n image.removeEventListener(\"load\", finish);\n image.removeEventListener(\"error\", finish);\n };\n image.addEventListener(\"load\", finish);\n image.addEventListener(\"error\", finish);\n image.src = objectUrl;\n } catch (e) {\n tile.setState(TileState.ERROR);\n if (!isAbortError(e)) {\n LOG.error(\"Failed to load tile\", e);\n }\n }\n }\n}\n\nfunction isHtmlImage(htmlElement: HTMLElement): htmlElement is HTMLImageElement {\n return htmlElement.tagName === \"IMG\";\n}\n/* eslint-disable @typescript-eslint/no-explicit-any */\nexport function getWMTSLegendUrl(\n capabilities: Record<string, any>,\n activeLayerId: string | undefined,\n activeStyleId: string | undefined\n): string | undefined {\n const content = capabilities?.Contents;\n const layers = content?.Layer;\n\n let activeLayer = layers?.find((layer: any) => layer?.Identifier === activeLayerId);\n if (!activeLayer) {\n LOG.debug(\"Failed to find the active layer in WMTS layer capabilities.\");\n activeLayer = layers?.[0];\n if (!activeLayer) {\n LOG.debug(\"No layer in WMTS capabilities - giving up.\");\n return undefined;\n }\n }\n\n const styles = activeLayer.Style;\n let activeStyle = styles?.find((style: any) => style?.Identifier === activeStyleId);\n if (!activeStyle) {\n LOG.debug(\"Failed to find active style in WMTS layer.\");\n activeStyle = styles?.[0];\n if (!activeStyle) {\n LOG.debug(\"No style in WMTS layer capabilities - giving up.\");\n return undefined;\n }\n }\n\n const legendUrl = activeStyle.LegendURL?.[0]?.href;\n return legendUrl as string | undefined;\n}\n"],"names":[],"mappings":";;;;;;;;;AAeA,MAAM,GAAA,GAAM,aAAa,eAAe,CAAA,CAAA;AAEjC,MAAM,sBAAsB,aAAmC,CAAA;AAAA,EAClE,IAAA,CAAA;AAAA,EACA,KAAA,CAAA;AAAA,EACA,UAAA,CAAA;AAAA,EACA,aAAA,CAAA;AAAA,EACA,MAAA,CAAA;AAAA,EACA,OAAA,CAAA;AAAA,EACA,OAAA,CAAA;AAAA,EACS,gBAAA,GAAmB,IAAI,eAAgB,EAAA,CAAA;AAAA,EAEhD,YAAY,MAAyB,EAAA;AACjC,IAAM,MAAA,KAAA,GAAQ,IAAI,SAAU,EAAA,CAAA;AAC5B,IAAM,KAAA,CAAA;AAAA,MACF,GAAG,MAAA;AAAA,MACH,OAAS,EAAA,KAAA;AAAA,KACZ,CAAA,CAAA;AACD,IAAA,IAAA,CAAK,OAAO,MAAO,CAAA,GAAA,CAAA;AACnB,IAAA,IAAA,CAAK,QAAQ,MAAO,CAAA,IAAA,CAAA;AACpB,IAAA,IAAA,CAAK,MAAS,GAAA,KAAA,CAAA;AACd,IAAA,IAAA,CAAK,aAAa,MAAO,CAAA,SAAA,CAAA;AAAA,GAC7B;AAAA,EAEA,OAAgB,GAAA;AACZ,IAAA,KAAA,CAAM,OAAQ,EAAA,CAAA;AACd,IAAA,IAAA,CAAK,iBAAiB,KAAM,EAAA,CAAA;AAAA,GAChC;AAAA,EAEA,IAAI,MAA6B,GAAA;AAC7B,IAAA,OAAO,IAAK,CAAA,OAAA,CAAA;AAAA,GAChB;AAAA,EAEA,SAAS,GAAyB,EAAA;AAC9B,IAAA,KAAA,CAAM,SAAS,GAAG,CAAA,CAAA;AAClB,IAAA,IAAA,CAAK,sBAAuB,EAAA,CACvB,IAAK,CAAA,CAAC,MAAmB,KAAA;AACtB,MAAM,MAAA,MAAA,GAAS,IAAI,gBAAiB,EAAA,CAAA;AACpC,MAAM,MAAA,YAAA,GAAe,MAAO,CAAA,IAAA,CAAK,MAAM,CAAA,CAAA;AACvC,MAAM,MAAA,OAAA,GAAU,wBAAwB,YAAc,EAAA;AAAA,QAClD,OAAO,IAAK,CAAA,KAAA;AAAA,QACZ,WAAW,IAAK,CAAA,UAAA;AAAA,OACnB,CAAA,CAAA;AACD,MAAA,IAAI,CAAC,OAAS,EAAA;AACV,QAAM,MAAA,IAAI,MAAM,qCAAqC,CAAA,CAAA;AAAA,OACzD;AACA,MAAM,MAAA,MAAA,GAAS,IAAI,IAAK,CAAA;AAAA,QACpB,GAAG,OAAA;AAAA,QACH,gBAAA,EAAkB,CAAC,IAAA,EAAM,OAAY,KAAA;AACjC,UAAK,IAAA,CAAA,SAAA,CAAU,MAAM,OAAO,CAAA,CAAA;AAAA,SAChC;AAAA,OACH,CAAA,CAAA;AACD,MAAA,IAAA,CAAK,OAAU,GAAA,MAAA,CAAA;AACf,MAAK,IAAA,CAAA,MAAA,CAAO,SAAU,CAAA,IAAA,CAAK,OAAO,CAAA,CAAA;AAClC,MAAM,MAAA,aAAA,GAAgB,OAAO,QAAS,EAAA,CAAA;AACtC,MAAA,MAAM,SAAY,GAAA,gBAAA,CAAiB,YAAc,EAAA,IAAA,CAAK,MAAM,aAAa,CAAA,CAAA;AACzE,MAAA,IAAA,CAAK,OAAU,GAAA,SAAA,CAAA;AACf,MAAA,IAAA,CAAK,kBAAkB,gBAAgB,CAAA,CAAA;AAAA,KAC1C,CAAA,CACA,KAAM,CAAA,CAAC,KAAU,KAAA;AACd,MAAI,IAAA,YAAA,CAAa,KAAK,CAAG,EAAA;AACrB,QAAA,GAAA,CAAI,KAAM,CAAA,CAAA,MAAA,EAAS,IAAK,CAAA,IAAI,CAA8C,4CAAA,CAAA,CAAA,CAAA;AAC1E,QAAA,OAAA;AAAA,OACJ;AACA,MAAA,GAAA,CAAI,KAAM,CAAA,CAAA,4CAAA,EAA+C,IAAK,CAAA,IAAI,IAAI,KAAK,CAAA,CAAA;AAAA,KAC9E,CAAA,CAAA;AAAA,GACT;AAAA,EAEA,IAAI,KAAQ,GAAA;AACR,IAAA,OAAO,IAAK,CAAA,MAAA,CAAA;AAAA,GAChB;AAAA,EAEA,IAAI,GAAM,GAAA;AACN,IAAA,OAAO,IAAK,CAAA,IAAA,CAAA;AAAA,GAChB;AAAA,EAEA,IAAI,IAAO,GAAA;AACP,IAAA,OAAO,IAAK,CAAA,KAAA,CAAA;AAAA,GAChB;AAAA,EAEA,IAAI,SAAY,GAAA;AACZ,IAAA,OAAO,IAAK,CAAA,UAAA,CAAA;AAAA,GAChB;AAAA,EAEA,IAAI,YAAe,GAAA;AACf,IAAA,OAAO,IAAK,CAAA,aAAA,CAAA;AAAA,GAChB;AAAA,EAEA,IAAI,SAAuB,GAAA;AACvB,IAAO,OAAA,KAAA,CAAA,CAAA;AAAA,GACX;AAAA,EAEA,MAAM,sBAA0C,GAAA;AAC5C,IAAM,MAAA,WAAA,GAAc,IAAK,CAAA,GAAA,CAAI,oBAAqB,CAAA,WAAA,CAAA;AAClD,IAAA,OAAO,kBAAkB,IAAK,CAAA,IAAA,EAAM,WAAa,EAAA,IAAA,CAAK,iBAAiB,MAAM,CAAA,CAAA;AAAA,GACjF;AAAA,EAEA,MAAM,SAAU,CAAA,IAAA,EAAY,OAAgC,EAAA;AACxD,IAAM,MAAA,WAAA,GAAc,IAAK,CAAA,GAAA,CAAI,oBAAqB,CAAA,WAAA,CAAA;AAClD,IAAI,IAAA;AACA,MAAI,IAAA,EAAE,gBAAgB,SAAY,CAAA,EAAA;AAC9B,QAAM,MAAA,IAAI,MAAM,wCAAwC,CAAA,CAAA;AAAA,OAC5D;AAEA,MAAM,MAAA,KAAA,GAAQ,KAAK,QAAS,EAAA,CAAA;AAC5B,MAAI,IAAA,CAAC,WAAY,CAAA,KAAK,CAAG,EAAA;AAErB,QAAM,MAAA,IAAI,MAAM,iDAAiD,CAAA,CAAA;AAAA,OACrE;AAEA,MAAA,MAAM,QAAW,GAAA,MAAM,WAAY,CAAA,KAAA,CAAM,OAAO,CAAA,CAAA;AAChD,MAAI,IAAA,CAAC,SAAS,EAAI,EAAA;AACd,QAAA,MAAM,IAAI,KAAA,CAAM,CAAmC,gCAAA,EAAA,QAAA,CAAS,MAAM,CAAG,CAAA,CAAA,CAAA,CAAA;AAAA,OACzE;AAEA,MAAM,MAAA,IAAA,GAAO,MAAM,QAAA,CAAS,IAAK,EAAA,CAAA;AACjC,MAAM,MAAA,SAAA,GAAY,GAAI,CAAA,eAAA,CAAgB,IAAI,CAAA,CAAA;AAC1C,MAAA,MAAM,SAAS,MAAM;AAGjB,QAAA,GAAA,CAAI,gBAAgB,SAAS,CAAA,CAAA;AAC7B,QAAM,KAAA,CAAA,mBAAA,CAAoB,QAAQ,MAAM,CAAA,CAAA;AACxC,QAAM,KAAA,CAAA,mBAAA,CAAoB,SAAS,MAAM,CAAA,CAAA;AAAA,OAC7C,CAAA;AACA,MAAM,KAAA,CAAA,gBAAA,CAAiB,QAAQ,MAAM,CAAA,CAAA;AACrC,MAAM,KAAA,CAAA,gBAAA,CAAiB,SAAS,MAAM,CAAA,CAAA;AACtC,MAAA,KAAA,CAAM,GAAM,GAAA,SAAA,CAAA;AAAA,aACP,CAAG,EAAA;AACR,MAAK,IAAA,CAAA,QAAA,CAAS,UAAU,KAAK,CAAA,CAAA;AAC7B,MAAI,IAAA,CAAC,YAAa,CAAA,CAAC,CAAG,EAAA;AAClB,QAAI,GAAA,CAAA,KAAA,CAAM,uBAAuB,CAAC,CAAA,CAAA;AAAA,OACtC;AAAA,KACJ;AAAA,GACJ;AACJ,CAAA;AAEA,SAAS,YAAY,WAA2D,EAAA;AAC5E,EAAA,OAAO,YAAY,OAAY,KAAA,KAAA,CAAA;AACnC,CAAA;AAEgB,SAAA,gBAAA,CACZ,YACA,EAAA,aAAA,EACA,aACkB,EAAA;AAClB,EAAA,MAAM,UAAU,YAAc,EAAA,QAAA,CAAA;AAC9B,EAAA,MAAM,SAAS,OAAS,EAAA,KAAA,CAAA;AAExB,EAAA,IAAI,cAAc,MAAQ,EAAA,IAAA,CAAK,CAAC,KAAe,KAAA,KAAA,EAAO,eAAe,aAAa,CAAA,CAAA;AAClF,EAAA,IAAI,CAAC,WAAa,EAAA;AACd,IAAA,GAAA,CAAI,MAAM,6DAA6D,CAAA,CAAA;AACvE,IAAA,WAAA,GAAc,SAAS,CAAC,CAAA,CAAA;AACxB,IAAA,IAAI,CAAC,WAAa,EAAA;AACd,MAAA,GAAA,CAAI,MAAM,4CAA4C,CAAA,CAAA;AACtD,MAAO,OAAA,KAAA,CAAA,CAAA;AAAA,KACX;AAAA,GACJ;AAEA,EAAA,MAAM,SAAS,WAAY,CAAA,KAAA,CAAA;AAC3B,EAAA,IAAI,cAAc,MAAQ,EAAA,IAAA,CAAK,CAAC,KAAe,KAAA,KAAA,EAAO,eAAe,aAAa,CAAA,CAAA;AAClF,EAAA,IAAI,CAAC,WAAa,EAAA;AACd,IAAA,GAAA,CAAI,MAAM,4CAA4C,CAAA,CAAA;AACtD,IAAA,WAAA,GAAc,SAAS,CAAC,CAAA,CAAA;AACxB,IAAA,IAAI,CAAC,WAAa,EAAA;AACd,MAAA,GAAA,CAAI,MAAM,kDAAkD,CAAA,CAAA;AAC5D,MAAO,OAAA,KAAA,CAAA,CAAA;AAAA,KACX;AAAA,GACJ;AAEA,EAAA,MAAM,SAAY,GAAA,WAAA,CAAY,SAAY,GAAA,CAAC,CAAG,EAAA,IAAA,CAAA;AAC9C,EAAO,OAAA,SAAA,CAAA;AACX;;;;"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@open-pioneer/map",
4
- "version": "0.1.1",
4
+ "version": "0.3.0",
5
5
  "license": "Apache-2.0",
6
6
  "dependencies": {
7
7
  "uuid": "^9.0.1"
@@ -9,15 +9,15 @@
9
9
  "peerDependencies": {
10
10
  "@open-pioneer/chakra-integration": "^1.1.1",
11
11
  "@open-pioneer/core": "^1.2.1",
12
- "@open-pioneer/http": "^2.1.1",
13
- "@open-pioneer/runtime": "^2.0.2",
12
+ "@open-pioneer/http": "^2.1.2",
13
+ "@open-pioneer/runtime": "^2.1.0",
14
14
  "@types/proj4": "^2.5.2",
15
15
  "ol": "^8.2.0",
16
16
  "proj4": "^2.9.0",
17
17
  "react": "^18.2.0",
18
18
  "react-dom": "^18.2.0",
19
19
  "react-use": "^17.4.2",
20
- "@open-pioneer/react-utils": "^0.1.0"
20
+ "@open-pioneer/react-utils": "^0.2.1"
21
21
  },
22
22
  "exports": {
23
23
  "./package.json": "./package.json",
package/ui/MapAnchor.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { StyleProps } from "@open-pioneer/chakra-integration";
2
2
  import { CommonComponentProps } from "@open-pioneer/react-utils";
3
3
  import { ReactNode } from "react";
4
- import { MapPadding } from "./MapContainer";
4
+ import { MapPadding } from "../api";
5
5
  export type MapAnchorPosition = "top-left" | "top-right" | "bottom-left" | "bottom-right";
6
6
  export interface MapAnchorProps extends CommonComponentProps {
7
7
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"MapAnchor.js","sources":["MapAnchor.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { Box, BoxProps, StyleProps } from \"@open-pioneer/chakra-integration\";\nimport { CommonComponentProps, useCommonComponentProps } from \"@open-pioneer/react-utils\";\nimport { BaseSyntheticEvent, ReactNode, useMemo } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { MapPadding } from \"./MapContainer\";\nimport { useMapContext } from \"./MapContext\";\n\nexport type MapAnchorPosition = \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n\nconst defaultPosition: MapAnchorPosition = \"top-right\";\n\nexport interface MapAnchorProps extends CommonComponentProps {\n /**\n * The position of the anchor container above the map.\n * @default \"top-right\"\n */\n position?: MapAnchorPosition;\n\n /**\n * Horizontal gap in pixel applied to anchor container.\n *\n * Applied:\n * - left, if position `*-left`\n * - right, if position `*-right`\n *\n * @default 0\n */\n horizontalGap?: number;\n\n /**\n * Vertical gap in pixel applied to anchor container.\n *\n * Applied:\n * - top, if position `top-*`\n * - bottom, if position `bottom-*`\n *\n * @default 0 (If position `bottom-*`, default verticalGap == `30`)\n */\n verticalGap?: number;\n\n /**\n * Prevent some events from the map anchor's children from bubbling towards the map, effectively hiding them from map interactions.\n * Defaults to `true`.\n *\n * If this value is enabled, events such as `pointer-down` are hidden from the map when they occur\n * within the map anchor.\n * This is essential when the user wants to select text, or open the browser context menu within the anchor.\n * If that is not required, set `stopEvents` to `false` instead.\n */\n stopEvents?: boolean;\n\n children?: ReactNode;\n}\n\nexport function MapAnchor(props: MapAnchorProps): JSX.Element {\n const {\n position = defaultPosition,\n stopEvents = true,\n children,\n horizontalGap,\n verticalGap\n } = props;\n const { containerProps } = useCommonComponentProps(\"map-anchor\", props);\n const { padding, mapAnchorsHost } = useMapContext();\n\n const eventHandlers: Partial<BoxProps> = useMemo(() => {\n const stopHandler = stopEvents ? stopPropagation : undefined;\n return {\n onPointerDown: stopHandler,\n onPointerUp: stopHandler,\n onContextMenu: stopHandler\n };\n }, [stopEvents]);\n\n return createPortal(\n <Box\n {...containerProps}\n /* Overlay container uses pointer-events: none, this restores interactivity */\n pointerEvents=\"auto\"\n /* Restore user-select: none set by ol-viewport parent */\n userSelect=\"text\"\n /** Hide pointer up/down and context menu events from the map parent. */\n {...eventHandlers}\n {...computePositionStyles(position, padding, horizontalGap, verticalGap)}\n >\n {children}\n </Box>,\n mapAnchorsHost\n );\n}\n\nexport function computeAttributionGap(verticalGap?: number): {\n gap: number;\n space: number;\n} {\n /**\n * height of the ol attribution component\n * improvement: Get height directly from `Attribution` HTMLDivElement\n */\n const height = 20;\n\n /**\n * additional space between attribution and map anchor container\n */\n const space = 10;\n\n return {\n gap: verticalGap === undefined ? height + space : 0,\n space\n };\n}\n\nexport function computePositionStyles(\n position: MapAnchorPosition,\n padding: Required<MapPadding>,\n horizontalGap?: number | undefined,\n verticalGap?: number | undefined\n): StyleProps {\n const props: StyleProps = {\n position: \"absolute\",\n transitionProperty: \"left, right, top, bottom\",\n transitionDuration: \"200ms\",\n transitionTimingFunction: \"ease-out\"\n };\n\n const defaultHorizontalGap = 0;\n const horizontal = horizontalGap ?? defaultHorizontalGap;\n\n const defaultVerticalGap = 0;\n const vertical = verticalGap ?? defaultVerticalGap;\n\n const attribution = computeAttributionGap(verticalGap);\n const gap = (n: number) => `${n}px`;\n\n switch (position) {\n case \"top-left\":\n props.left = gap(padding.left + horizontal);\n props.top = gap(padding.top + vertical);\n break;\n case \"top-right\":\n props.right = gap(padding.right + horizontal);\n props.top = gap(padding.top + vertical);\n break;\n case \"bottom-left\":\n props.left = gap(padding.left + horizontal);\n props.bottom = gap(padding.bottom + vertical + attribution.gap);\n break;\n case \"bottom-right\":\n props.right = gap(padding.right + horizontal);\n props.bottom = gap(padding.bottom + vertical + attribution.gap);\n break;\n }\n\n /**\n * Apply max-height and max-width to MapAnchor to avoid content overflow\n */\n props.maxH = `calc((100%) - ${props.top ?? \"0px\"} - ${\n props.bottom ?? attribution.gap + \"px\"\n } - ${vertical + \"px\"} - ${attribution.space + \"px\"})`;\n\n props.maxW = `calc((100%) - ${props.left ?? \"0px\"} - ${props.right ?? \"0px\"} - ${\n horizontal + \"px\"\n })`;\n props.overflow = \"hidden\";\n\n return props;\n}\n\nfunction stopPropagation(e: BaseSyntheticEvent) {\n e.stopPropagation();\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MapAnchor.js","sources":["MapAnchor.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { Box, BoxProps, StyleProps } from \"@open-pioneer/chakra-integration\";\nimport { CommonComponentProps, useCommonComponentProps } from \"@open-pioneer/react-utils\";\nimport { BaseSyntheticEvent, ReactNode, useMemo } from \"react\";\nimport { createPortal } from \"react-dom\";\nimport { MapPadding } from \"../api\";\nimport { useMapContext } from \"./MapContext\";\n\nexport type MapAnchorPosition = \"top-left\" | \"top-right\" | \"bottom-left\" | \"bottom-right\";\n\nconst defaultPosition: MapAnchorPosition = \"top-right\";\n\nexport interface MapAnchorProps extends CommonComponentProps {\n /**\n * The position of the anchor container above the map.\n * @default \"top-right\"\n */\n position?: MapAnchorPosition;\n\n /**\n * Horizontal gap in pixel applied to anchor container.\n *\n * Applied:\n * - left, if position `*-left`\n * - right, if position `*-right`\n *\n * @default 0\n */\n horizontalGap?: number;\n\n /**\n * Vertical gap in pixel applied to anchor container.\n *\n * Applied:\n * - top, if position `top-*`\n * - bottom, if position `bottom-*`\n *\n * @default 0 (If position `bottom-*`, default verticalGap == `30`)\n */\n verticalGap?: number;\n\n /**\n * Prevent some events from the map anchor's children from bubbling towards the map, effectively hiding them from map interactions.\n * Defaults to `true`.\n *\n * If this value is enabled, events such as `pointer-down` are hidden from the map when they occur\n * within the map anchor.\n * This is essential when the user wants to select text, or open the browser context menu within the anchor.\n * If that is not required, set `stopEvents` to `false` instead.\n */\n stopEvents?: boolean;\n\n children?: ReactNode;\n}\n\nexport function MapAnchor(props: MapAnchorProps): JSX.Element {\n const {\n position = defaultPosition,\n stopEvents = true,\n children,\n horizontalGap,\n verticalGap\n } = props;\n const { containerProps } = useCommonComponentProps(\"map-anchor\", props);\n const { padding, mapAnchorsHost } = useMapContext();\n\n const eventHandlers: Partial<BoxProps> = useMemo(() => {\n const stopHandler = stopEvents ? stopPropagation : undefined;\n return {\n onPointerDown: stopHandler,\n onPointerUp: stopHandler,\n onContextMenu: stopHandler\n };\n }, [stopEvents]);\n\n return createPortal(\n <Box\n {...containerProps}\n /* Overlay container uses pointer-events: none, this restores interactivity */\n pointerEvents=\"auto\"\n /* Restore user-select: none set by ol-viewport parent */\n userSelect=\"text\"\n /** Hide pointer up/down and context menu events from the map parent. */\n {...eventHandlers}\n {...computePositionStyles(position, padding, horizontalGap, verticalGap)}\n >\n {children}\n </Box>,\n mapAnchorsHost\n );\n}\n\nexport function computeAttributionGap(verticalGap?: number): {\n gap: number;\n space: number;\n} {\n /**\n * height of the ol attribution component\n * improvement: Get height directly from `Attribution` HTMLDivElement\n */\n const height = 20;\n\n /**\n * additional space between attribution and map anchor container\n */\n const space = 10;\n\n return {\n gap: verticalGap === undefined ? height + space : 0,\n space\n };\n}\n\nexport function computePositionStyles(\n position: MapAnchorPosition,\n padding: Required<MapPadding>,\n horizontalGap?: number | undefined,\n verticalGap?: number | undefined\n): StyleProps {\n const props: StyleProps = {\n position: \"absolute\",\n transitionProperty: \"left, right, top, bottom\",\n transitionDuration: \"200ms\",\n transitionTimingFunction: \"ease-out\"\n };\n\n const defaultHorizontalGap = 0;\n const horizontal = horizontalGap ?? defaultHorizontalGap;\n\n const defaultVerticalGap = 0;\n const vertical = verticalGap ?? defaultVerticalGap;\n\n const attribution = computeAttributionGap(verticalGap);\n const gap = (n: number) => `${n}px`;\n\n switch (position) {\n case \"top-left\":\n props.left = gap(padding.left + horizontal);\n props.top = gap(padding.top + vertical);\n break;\n case \"top-right\":\n props.right = gap(padding.right + horizontal);\n props.top = gap(padding.top + vertical);\n break;\n case \"bottom-left\":\n props.left = gap(padding.left + horizontal);\n props.bottom = gap(padding.bottom + vertical + attribution.gap);\n break;\n case \"bottom-right\":\n props.right = gap(padding.right + horizontal);\n props.bottom = gap(padding.bottom + vertical + attribution.gap);\n break;\n }\n\n /**\n * Apply max-height and max-width to MapAnchor to avoid content overflow\n */\n props.maxH = `calc((100%) - ${props.top ?? \"0px\"} - ${\n props.bottom ?? attribution.gap + \"px\"\n } - ${vertical + \"px\"} - ${attribution.space + \"px\"})`;\n\n props.maxW = `calc((100%) - ${props.left ?? \"0px\"} - ${props.right ?? \"0px\"} - ${\n horizontal + \"px\"\n })`;\n props.overflow = \"hidden\";\n\n return props;\n}\n\nfunction stopPropagation(e: BaseSyntheticEvent) {\n e.stopPropagation();\n}\n"],"names":[],"mappings":"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"}
@@ -1,16 +1,6 @@
1
1
  import { CommonComponentProps } from "@open-pioneer/react-utils";
2
2
  import { ReactNode } from "react";
3
- /**
4
- * Map padding, all values are pixels.
5
- *
6
- * See https://openlayers.org/en/latest/apidoc/module-ol_View-View.html#padding
7
- */
8
- export interface MapPadding {
9
- left?: number;
10
- right?: number;
11
- top?: number;
12
- bottom?: number;
13
- }
3
+ import { MapPadding } from "../api";
14
4
  export interface MapContainerProps extends CommonComponentProps {
15
5
  /** The id of the map to display. */
16
6
  mapId: string;