@vcmap/ui 5.0.0-rc.10 → 5.0.0-rc.11

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 (116) hide show
  1. package/README.md +11 -4
  2. package/build/build.js +0 -3
  3. package/build/buildHelpers.js +0 -1
  4. package/build/buildPreview.js +7 -0
  5. package/config/aerowest.config.json +13 -3
  6. package/config/base.config.json +89 -64
  7. package/config/codes.config.json +397 -0
  8. package/config/dev.config.json +169 -0
  9. package/config/graphFeatureInfo.config.json +100 -0
  10. package/config/www.config.json +1232 -0
  11. package/dist/assets/{cesium.eb5667.js → cesium.e67536.js} +0 -0
  12. package/dist/assets/cesium.js +1 -1
  13. package/dist/assets/core.ebf665.js +4 -0
  14. package/dist/assets/core.js +1 -1
  15. package/dist/assets/{index.4ccd4433.js → index.9b213929.js} +1 -1
  16. package/dist/assets/{ol.ef03b1.js → ol.8bbd50.js} +0 -0
  17. package/dist/assets/ol.js +1 -1
  18. package/dist/assets/ui.fdfe0d.css +1 -0
  19. package/dist/assets/ui.fdfe0d.js +68 -0
  20. package/dist/assets/ui.js +1 -1
  21. package/dist/assets/vue.0bb7c6.js +9 -0
  22. package/dist/assets/vue.js +2 -1
  23. package/dist/assets/{vuetify.401a29.css → vuetify.53300f.css} +1 -1
  24. package/dist/assets/{vuetify.401a29.js → vuetify.53300f.js} +71 -71
  25. package/dist/assets/vuetify.js +2 -2
  26. package/dist/index.html +1 -1
  27. package/index.js +36 -5
  28. package/lib/vue.js +1 -0
  29. package/map.config.json +15 -6
  30. package/package.json +6 -7
  31. package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
  32. package/plugins/@vcmap/create-link/index.js +83 -0
  33. package/plugins/@vcmap/create-link/package.json +6 -0
  34. package/plugins/@vcmap/pluginExample/index.js +1 -1
  35. package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
  36. package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
  37. package/plugins/@vcmap/project-selector/index.js +1 -1
  38. package/plugins/@vcmap/project-selector/package.json +1 -2
  39. package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
  40. package/plugins/@vcmap/theme-changer/index.js +1 -1
  41. package/plugins/@vcmap/theme-changer/package.json +1 -2
  42. package/plugins/categoryTest/Categories.vue +89 -1
  43. package/plugins/categoryTest/Category.vue +1 -1
  44. package/plugins/simple-graph/README.md +51 -0
  45. package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
  46. package/plugins/simple-graph/index.js +17 -0
  47. package/plugins/simple-graph/package.json +11 -0
  48. package/plugins/simple-graph/simpleGraphView.js +76 -0
  49. package/plugins/test/editor.vue +1 -1
  50. package/plugins/test/index.js +63 -2
  51. package/plugins/test/windowManagerExample.vue +1 -1
  52. package/src/actions/stateRefAction.js +2 -2
  53. package/src/actions/styleSelector.vue +1 -1
  54. package/src/application/Navbar.vue +13 -2
  55. package/src/application/VcsApp.vue +201 -92
  56. package/src/application/VcsMap.vue +1 -1
  57. package/src/application/VcsSettings.vue +1 -1
  58. package/src/application/vcsAppWrapper.vue +1 -0
  59. package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
  60. package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
  61. package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
  62. package/src/components/form-output/VcsFormattedNumber.vue +1 -1
  63. package/src/components/lists/VcsActionList.vue +13 -4
  64. package/src/components/lists/VcsTreeview.vue +4 -4
  65. package/src/components/lists/VcsTreeviewLeaf.vue +9 -2
  66. package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
  67. package/src/components/tables/VcsTable.vue +245 -0
  68. package/src/contentTree/LayerTree.vue +1 -1
  69. package/src/contentTree/contentTreeCollection.js +4 -4
  70. package/src/contentTree/contentTreeItem.js +9 -9
  71. package/src/contentTree/groupContentTreeItem.js +1 -1
  72. package/src/contentTree/layerContentTreeItem.js +15 -1
  73. package/src/contentTree/layerGroupContentTreeItem.js +21 -1
  74. package/src/contentTree/nodeContentTreeItem.js +1 -1
  75. package/src/featureInfo/AddressBalloonComponent.vue +47 -0
  76. package/src/featureInfo/BalloonComponent.vue +138 -0
  77. package/src/featureInfo/abstractFeatureInfoView.js +313 -0
  78. package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
  79. package/src/featureInfo/balloonFeatureInfoView.js +151 -0
  80. package/src/featureInfo/balloonHelper.js +132 -0
  81. package/src/featureInfo/featureInfo.js +455 -0
  82. package/src/featureInfo/featureInfoInteraction.js +42 -0
  83. package/src/featureInfo/iframeFeatureInfoView.js +95 -0
  84. package/src/featureInfo/tableFeatureInfoView.js +106 -0
  85. package/src/i18n/de.js +16 -0
  86. package/src/i18n/en.js +16 -0
  87. package/src/i18n/i18nCollection.js +17 -0
  88. package/src/manager/buttonManager.js +5 -5
  89. package/src/manager/categoryManager/ComponentsManager.vue +30 -0
  90. package/src/manager/categoryManager/categoryManager.js +500 -0
  91. package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
  92. package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
  93. package/src/manager/contextMenu/contextMenuManager.js +197 -0
  94. package/src/manager/navbarManager.js +8 -8
  95. package/src/manager/toolbox/ToolboxManager.vue +2 -2
  96. package/src/manager/toolbox/toolboxManager.js +7 -3
  97. package/src/manager/window/WindowComponent.vue +1 -1
  98. package/src/manager/window/WindowManager.vue +5 -3
  99. package/src/manager/window/windowManager.js +118 -14
  100. package/src/navigation/mapNavigation.vue +3 -5
  101. package/src/navigation/overviewMap.js +28 -5
  102. package/src/navigation/vcsCompass.vue +1 -1
  103. package/src/setup.js +0 -2
  104. package/src/state.js +256 -0
  105. package/src/styles/_theming.scss +0 -5
  106. package/src/uiConfig.js +79 -0
  107. package/src/vcsUiApp.js +210 -20
  108. package/src/vuePlugins/vuetify.js +14 -4
  109. package/config/berlin.config.json +0 -510
  110. package/dist/assets/core.216494.js +0 -4
  111. package/dist/assets/ui.99a1a7.css +0 -1
  112. package/dist/assets/ui.99a1a7.js +0 -70
  113. package/dist/assets/vue-composition-api.c5aca1.js +0 -14
  114. package/dist/assets/vue-composition-api.js +0 -2
  115. package/dist/assets/vue.762edd.js +0 -9
  116. package/lib/vue-composition-api.js +0 -2
@@ -0,0 +1,132 @@
1
+ import { Cartesian2, Cartographic, SceneTransforms } from '@vcmap/cesium';
2
+ import { CesiumMap, ObliqueMap, OpenlayersMap, Projection, transformToImage } from '@vcmap/core';
3
+ import { unByKey } from 'ol/Observable.js';
4
+ import {
5
+ getWindowPositionOptionsFromMapEvent,
6
+ WindowAlignment,
7
+ } from '../manager/window/windowManager.js';
8
+
9
+ /**
10
+ * balloon offset from location or click position in pixel
11
+ * @type {{x: number, y: number}}
12
+ */
13
+ export const balloonOffset = { x: 55, y: 25 };
14
+
15
+ /**
16
+ * @param {import("@vcmap/cesium").Scene} scene
17
+ * @param {import("@vcmap/cesium").Cartesian3} cartesian
18
+ * @returns {undefined|import("@vcmap/cesium").Cartesian2}
19
+ */
20
+ function getBalloonPositionCesium(scene, cartesian) {
21
+ return SceneTransforms.wgs84ToWindowCoordinates(scene, cartesian);
22
+ }
23
+
24
+ /**
25
+ * @param {import("@vcmap/core").OpenlayersMap} olMap
26
+ * @param {import("ol/coordinate").Coordinate} position
27
+ * @returns {undefined|import("@vcmap/cesium").Cartesian2}
28
+ */
29
+ function getBalloonPositionOL(olMap, position) {
30
+ const pixel = olMap.getPixelFromCoordinate(position);
31
+ if (pixel) {
32
+ return new Cartesian2(...pixel);
33
+ }
34
+ return undefined;
35
+ }
36
+
37
+ /**
38
+ * returns the windowPosition of a balloon from a map position
39
+ * @param {VcsApp} app
40
+ * @param {import("ol/coordinate").Coordinate} position - position in mercator
41
+ * @returns {Promise<undefined|Cartesian2>}
42
+ */
43
+ export async function getBalloonPosition(app, position) {
44
+ const map = app.maps.activeMap;
45
+ if (map instanceof CesiumMap) {
46
+ const wgs84Position = Projection.mercatorToWgs84(position);
47
+ const cartesian = Cartographic.toCartesian(Cartographic.fromDegrees(...wgs84Position));
48
+ return getBalloonPositionCesium(map.getScene(), cartesian);
49
+ } else if (map instanceof OpenlayersMap) {
50
+ return getBalloonPositionOL(map.olMap, position);
51
+ } else if (map instanceof ObliqueMap) {
52
+ const { coords } = await transformToImage(
53
+ map.currentImage,
54
+ position,
55
+ );
56
+ return getBalloonPositionOL(map.olMap, coords);
57
+ }
58
+ return undefined;
59
+ }
60
+
61
+ /**
62
+ * sets the windowPosition of a balloon
63
+ * @param {WindowManager} windowManager
64
+ * @param {string} id - windowId of balloon
65
+ * @param {import("@vcmap/cesium").Cartesian2|undefined} windowPosition
66
+ */
67
+ export function setBalloonPosition(windowManager, id, windowPosition) {
68
+ if (!windowPosition) {
69
+ return;
70
+ }
71
+ windowManager.setWindowPositionOptions(
72
+ id,
73
+ getWindowPositionOptionsFromMapEvent(
74
+ new Cartesian2(windowPosition.x - balloonOffset.x, windowPosition.y - balloonOffset.y),
75
+ WindowAlignment.BOTTOM_LEFT,
76
+ ),
77
+ );
78
+ }
79
+
80
+ /**
81
+ * @param {VcsApp} vcsApp
82
+ * @param {string} windowId
83
+ * @param {import("ol/coordinate").Coordinate} clickedPosition - position in mercator
84
+ * @returns {Promise<(() => void)>}
85
+ */
86
+ export async function setupBalloonPositionListener(vcsApp, windowId, clickedPosition) {
87
+ const listeners = [];
88
+
89
+ const destroy = () => {
90
+ listeners.forEach(cb => cb());
91
+ };
92
+
93
+ const setup = async (app, id, position) => {
94
+ destroy();
95
+
96
+ listeners.push(app.maps.mapActivated.addEventListener(
97
+ setup.bind(null, app, id, position),
98
+ ));
99
+
100
+ const map = app.maps.activeMap;
101
+ if (map instanceof CesiumMap) {
102
+ const wgs84Position = Projection.mercatorToWgs84(position);
103
+ const cartesian = Cartographic.toCartesian(Cartographic.fromDegrees(...wgs84Position));
104
+ listeners.push(map.getScene().postRender.addEventListener((scene) => {
105
+ setBalloonPosition(app.windowManager, windowId, getBalloonPositionCesium(scene, cartesian));
106
+ }));
107
+ } else if (map instanceof OpenlayersMap) {
108
+ const handler = () => setBalloonPosition(app.windowManager, windowId, getBalloonPositionOL(map.olMap, position));
109
+ const key = map.olMap.on(
110
+ 'postrender',
111
+ handler,
112
+ );
113
+ listeners.push(() => unByKey(key));
114
+ } else if (map instanceof ObliqueMap) {
115
+ const { coords } = await transformToImage(
116
+ map.currentImage,
117
+ position,
118
+ );
119
+ listeners.push(map.imageChanged.addEventListener(setup.bind(null, app, windowId, position)));
120
+ const handler = () => setBalloonPosition(app.windowManager, windowId, getBalloonPositionOL(map.olMap, coords));
121
+ const key = map.olMap.on(
122
+ 'postrender',
123
+ handler,
124
+ );
125
+ listeners.push(() => unByKey(key));
126
+ }
127
+ };
128
+
129
+ await setup(vcsApp, windowId, clickedPosition);
130
+
131
+ return destroy;
132
+ }
@@ -0,0 +1,455 @@
1
+ import {
2
+ EventType,
3
+ makeOverrideCollection,
4
+ Collection,
5
+ getObjectFromClassRegistry,
6
+ VcsEvent,
7
+ vcsLayerName,
8
+ OverrideClassRegistry,
9
+ ClassRegistry,
10
+ fromCesiumColor,
11
+ VectorLayer,
12
+ isProvidedFeature,
13
+ mercatorProjection,
14
+ getDefaultVectorStyleItemOptions,
15
+ VectorStyleItem,
16
+ markVolatile,
17
+ maxZIndex,
18
+ } from '@vcmap/core';
19
+ import { getLogger as getLoggerByName } from '@vcsuite/logger';
20
+ import {
21
+ Cesium3DTileFeature,
22
+ Cesium3DTilePointFeature,
23
+ Color,
24
+ Entity,
25
+ } from '@vcmap/cesium';
26
+ import { Feature } from 'ol';
27
+ import { check, checkMaybe } from '@vcsuite/check';
28
+ import { v4 as uuidv4 } from 'uuid';
29
+
30
+ import { vcsAppSymbol } from '../pluginHelper.js';
31
+ import FeatureInfoInteraction from './featureInfoInteraction.js';
32
+ import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
33
+ import TableFeatureInfoView from './tableFeatureInfoView.js';
34
+ import IframeFeatureInfoView from './iframeFeatureInfoView.js';
35
+ import AddressBalloonFeatureInfoView from './addressBalloonFeatureInfoView.js';
36
+ import BalloonFeatureInfoView from './balloonFeatureInfoView.js';
37
+ import { defaultPrimaryColor } from '../vuePlugins/vuetify.js';
38
+
39
+ /** @typedef {import("ol").Feature<import("ol/geom/Geometry").default>|import("@vcmap/cesium").Cesium3DTileFeature|import("@vcmap/cesium").Cesium3DTilePointFeature|import("@vcmap/cesium").Entity} FeatureType */
40
+
41
+ /**
42
+ * @typedef {Object} FeatureInfoEvent
43
+ * @property {FeatureType} feature
44
+ * @property {import("ol/coordinate").Coordinate} [position] - potential position to place the balloon at
45
+ * @property {import("ol/coordinate").Coordinate} [windowPosition] - potential window position to initially place the balloon at
46
+ */
47
+
48
+ /**
49
+ * @returns {Logger}
50
+ */
51
+ function getLogger() {
52
+ return getLoggerByName('featureInfo');
53
+ }
54
+
55
+ /**
56
+ * @param {FeatureType} feature
57
+ * @param {import("@vcmap/core").Layer} layer
58
+ * @param {string} defaultFillColor
59
+ * @returns {import("ol/style/Style").default|import("@vcmap/core").VectorStyleItem}
60
+ */
61
+ function getHighlightStyle(feature, layer, defaultFillColor) {
62
+ if (layer && layer.highlightStyle) {
63
+ return layer.highlightStyle;
64
+ }
65
+
66
+ const fillColor = Color.fromCssColorString(defaultFillColor).withAlpha(0.8);
67
+ if (feature instanceof Feature) {
68
+ let style = feature.getStyle() ?? layer?.style?.style;
69
+ if (typeof style === 'function') {
70
+ style = style(feature, 1);
71
+ }
72
+ style = style?.clone?.() ?? new VectorStyleItem(getDefaultVectorStyleItemOptions()).style;
73
+ if (style.getText()) {
74
+ if (style.getText().getFill()) {
75
+ style.getText().getFill().setColor(fillColor.toCssColorString());
76
+ }
77
+ style.getText().setScale((style.getText().getScale() ?? 1) * 2);
78
+ }
79
+ if (style.getImage()) {
80
+ style.getImage().setScale(style.getImage().getScale() * 2);
81
+ }
82
+ if (style.getStroke()) {
83
+ style.getStroke().setColor(fillColor.toCssColorString());
84
+ style.getStroke().setWidth(style.getStroke().getWidth() * 2);
85
+ }
86
+ if (style.getFill()) {
87
+ const color = fillColor.toBytes();
88
+ color[3] /= 255;
89
+ style.getFill().setColor(color);
90
+ }
91
+ return style;
92
+ }
93
+ return fromCesiumColor(fillColor);
94
+ }
95
+
96
+ /**
97
+ * @param {VcsUiApp} app
98
+ * @returns {FeatureInfoSession}
99
+ */
100
+ export function createFeatureInfoSession(app) {
101
+ const { eventHandler } = app.maps;
102
+ /** @type {function():void} */
103
+ let stop;
104
+ const interaction = new FeatureInfoInteraction(app.featureInfo);
105
+ const listener = eventHandler.addExclusiveInteraction(
106
+ interaction,
107
+ () => { stop?.(); },
108
+ );
109
+ const currentFeatureInteractionEvent = eventHandler.featureInteraction.active;
110
+ eventHandler.featureInteraction.setActive(EventType.CLICK);
111
+
112
+ const stopped = new VcsEvent();
113
+ stop = () => {
114
+ listener();
115
+ interaction.destroy();
116
+ eventHandler.featureInteraction.setActive(currentFeatureInteractionEvent);
117
+ stopped.raiseEvent();
118
+ stopped.destroy();
119
+ };
120
+
121
+ return {
122
+ stopped,
123
+ stop,
124
+ };
125
+ }
126
+
127
+ /**
128
+ * @param {VcsUiApp} app
129
+ */
130
+ function setupFeatureInfoTool(app) {
131
+ /** @type {FeatureInfoSession|null} */
132
+ let session = null;
133
+
134
+ const action = {
135
+ name: 'featureInfoToggle',
136
+ title: 'featureInfo.activateToolTitle',
137
+ icon: '$vcsInfo',
138
+ active: false,
139
+ callback() {
140
+ if (session) {
141
+ session.stop();
142
+ this.title = 'featureInfo.activateToolTitle';
143
+ } else {
144
+ session = createFeatureInfoSession(app);
145
+ session.stopped.addEventListener(() => {
146
+ this.active = false;
147
+ session = null;
148
+ app.featureInfo.clear();
149
+ });
150
+ this.active = true;
151
+ this.title = 'featureInfo.deactivateToolTitle';
152
+ }
153
+ },
154
+ };
155
+
156
+ app.toolboxManager.requestGroup('featureInfo').buttonManager.add(
157
+ {
158
+ id: 'featureInfoTool',
159
+ action,
160
+ },
161
+ vcsAppSymbol,
162
+ );
163
+ }
164
+
165
+ /**
166
+ * @typedef {Object} FeatureInfoSession
167
+ * @property {VcsEvent<void>} stopped
168
+ * @property {function():void} stop
169
+ */
170
+
171
+ /**
172
+ * @type {ClassRegistry<AbstractFeatureInfoView>}
173
+ */
174
+ export const featureInfoClassRegistry = new ClassRegistry();
175
+
176
+ /**
177
+ * @class FeatureInfo
178
+ * @description Provides registration of featureInfoClasses and stores featureInfoView instances in its collection.
179
+ */
180
+ class FeatureInfo {
181
+ /**
182
+ * @param {VcsUiApp} app
183
+ */
184
+ constructor(app) {
185
+ /**
186
+ * @type {VcsUiApp}
187
+ * @private
188
+ */
189
+ this._app = app;
190
+ /**
191
+ * @type {import("@vcmap/core").OverrideCollection<AbstractFeatureInfoView>}
192
+ * @private
193
+ */
194
+ this._collection = makeOverrideCollection(
195
+ new Collection(),
196
+ () => this._app.dynamicContextId,
197
+ null,
198
+ config => getObjectFromClassRegistry(this._featureInfoClassRegistry, config),
199
+ AbstractFeatureInfoView,
200
+ );
201
+ /**
202
+ * @type {OverrideClassRegistry<AbstractFeatureInfoView>}
203
+ * @private
204
+ */
205
+ this._featureInfoClassRegistry = new OverrideClassRegistry(featureInfoClassRegistry);
206
+ /**
207
+ * @type {function():void|null}
208
+ * @private
209
+ */
210
+ this._clearHighlightingCb = null;
211
+ /**
212
+ * @type {string|null}
213
+ * @private
214
+ */
215
+ this._windowId = null;
216
+ /**
217
+ * @type {VcsEvent<FeatureType>}
218
+ * @private
219
+ */
220
+ this._featureChanged = new VcsEvent();
221
+ /**
222
+ * @type {FeatureType|null}
223
+ * @private
224
+ */
225
+ this._selectedFeature = null;
226
+ /**
227
+ * @type {Array<function():void>}
228
+ * @private
229
+ */
230
+ this._listeners = [
231
+ this._app.maps.mapActivated.addEventListener((map) => {
232
+ if (
233
+ this._windowId &&
234
+ this._app.windowManager.has(this._windowId)
235
+ ) {
236
+ const { layerName } = this._app.windowManager.get(this._windowId).props;
237
+ const layer = this._app.layers.getByKey(layerName);
238
+ if (layer && !layer.isSupported(map)) {
239
+ this._app.windowManager.remove(this._windowId);
240
+ }
241
+ }
242
+ }),
243
+ this._app.layers.stateChanged.addEventListener((layer) => {
244
+ if (
245
+ this._windowId &&
246
+ this._app.windowManager.has(this._windowId) &&
247
+ this._app.windowManager.get(this._windowId).props.layerName === layer.name
248
+ ) {
249
+ this._app.windowManager.remove(this._windowId);
250
+ }
251
+ }),
252
+ this._app.windowManager.removed.addEventListener(({ id }) => {
253
+ if (id === this._windowId) {
254
+ this.clear();
255
+ }
256
+ }),
257
+ this._app.contextAdded.addEventListener(() => this.clear()),
258
+ this._app.contextRemoved.addEventListener(() => this.clear()),
259
+ ];
260
+ /**
261
+ * A vector layer to render provided features on
262
+ * @type {VectorLayer|null}
263
+ * @private
264
+ */
265
+ this._scratchLayer = null;
266
+
267
+ setupFeatureInfoTool(this._app);
268
+ }
269
+
270
+ /**
271
+ * @type {import("@vcmap/core").OverrideCollection<AbstractFeatureInfoView>}
272
+ * @readonly
273
+ */
274
+ get collection() { return this._collection; }
275
+
276
+ /**
277
+ * @type {OverrideClassRegistry<AbstractFeatureInfoView>}
278
+ * @readonly
279
+ */
280
+ get classRegistry() { return this._featureInfoClassRegistry; }
281
+
282
+ /**
283
+ * @type {VcsEvent<null|FeatureType>}
284
+ * @readonly
285
+ */
286
+ get featureChanged() { return this._featureChanged; }
287
+
288
+ /**
289
+ * @type {null|FeatureType}
290
+ * @readonly
291
+ */
292
+ get selectedFeature() { return this._selectedFeature; }
293
+
294
+ /**
295
+ * The window id of the current features FeatureInfoView window
296
+ * @type {string|null}
297
+ * @readonly
298
+ */
299
+ get windowId() {
300
+ return this._windowId;
301
+ }
302
+
303
+ /**
304
+ * @private
305
+ */
306
+ _ensureScratchLayer() {
307
+ if (!this._scratchLayer) {
308
+ this._scratchLayer = new VectorLayer({
309
+ zIndex: maxZIndex,
310
+ projection: mercatorProjection.toJSON(),
311
+ });
312
+ markVolatile(this._scratchLayer);
313
+ this._app.layers.add(this._scratchLayer);
314
+ this._scratchLayer.activate();
315
+ }
316
+ }
317
+
318
+ /**
319
+ * @param {FeatureType} feature
320
+ * @returns {null|AbstractFeatureInfoView}
321
+ * @private
322
+ */
323
+ _getFeatureInfoViewForFeature(feature) {
324
+ const layer = this._app.layers.getByKey(feature[vcsLayerName]);
325
+ const name = layer?.properties?.featureInfo;
326
+ if (!name) {
327
+ getLogger().debug(`No view has been configured for layer '${layer?.name}'.`);
328
+ return null;
329
+ }
330
+ if (!this._collection.hasKey(name)) {
331
+ getLogger().warning(`No view with name '${name}' has been registered.`);
332
+ return null;
333
+ }
334
+ return /** @type {AbstractFeatureInfoView} */ this._collection.getByKey(name);
335
+ }
336
+
337
+ /**
338
+ * Selecting a feature highlights said feature and opens a FeatureInfoView, if configured on the layer. For a successful selection,
339
+ * the feature must meet the following criteria: a) the feature must be part of a layer, b) said layer must be managed in
340
+ * the same VcsApp as provided to the FeatureInfo on construction. if not providing a feature info view, then c) said layer must have a featureInfo property set on
341
+ * its properties bag and d) said featureInfo property must provide the name of a FeatureInfoView present on this FeatureInfos
342
+ * collection.
343
+ * If passing a feature create by a FeatureProvider, the feature will be highlighted on an internal scratch layer.
344
+ * @param {FeatureType} feature
345
+ * @param {import("ol/coordinate").Coordinate=} [position] - optional clicked position. If not given feature's center point is used to place balloons
346
+ * @param {import("ol/coordinate").Coordinate=} [windowPosition] - optional clicked window position. If not given derived from position for balloons
347
+ * @param {AbstractFeatureInfoView=} featureInfoView
348
+ * @returns {Promise<void>}
349
+ */
350
+ async selectFeature(feature, position, windowPosition, featureInfoView) {
351
+ check(feature, [Feature, Entity, Cesium3DTileFeature, Cesium3DTilePointFeature]);
352
+ checkMaybe(position, [Number]);
353
+ checkMaybe(windowPosition, [Number]);
354
+ checkMaybe(featureInfoView, AbstractFeatureInfoView);
355
+
356
+ const usedFeatureInfoView = featureInfoView ?? this._getFeatureInfoViewForFeature(feature);
357
+ const layer = this._app.layers.getByKey(feature[vcsLayerName]);
358
+
359
+ if (usedFeatureInfoView && layer) {
360
+ this._clearInternal();
361
+ if (feature[isProvidedFeature]) {
362
+ this._ensureScratchLayer();
363
+ this._scratchLayer.addFeatures([feature]);
364
+ const featureId = feature.getId(); // make sure to grab ID after adding it to the layer
365
+ this._scratchLayer.featureVisibility.highlight({
366
+ [featureId]: getHighlightStyle(
367
+ feature,
368
+ layer,
369
+ this._app.uiConfig.config.value.primaryColor ?? defaultPrimaryColor,
370
+ ),
371
+ });
372
+ this._clearHighlightingCb = () => this._scratchLayer.featureVisibility.unHighlight([featureId]);
373
+ } else if (layer.featureVisibility) {
374
+ const featureId = feature.getId();
375
+ layer.featureVisibility.highlight({
376
+ [featureId]: getHighlightStyle(
377
+ feature,
378
+ layer,
379
+ this._app.uiConfig.config.value.primaryColor ?? defaultPrimaryColor,
380
+ ),
381
+ });
382
+ this._clearHighlightingCb = () => layer.featureVisibility.unHighlight([featureId]);
383
+ }
384
+ this._windowId = uuidv4(); // we need to create a uuid, otherwise the window manager gets confused if we recreate a window in the same thread with the same id
385
+ this._app.windowManager.add(
386
+ {
387
+ id: this._windowId,
388
+ ...usedFeatureInfoView.getWindowComponentOptions(
389
+ { feature, position, windowPosition },
390
+ layer,
391
+ ),
392
+ },
393
+ vcsAppSymbol,
394
+ );
395
+
396
+ this._selectedFeature = feature;
397
+ this._featureChanged.raiseEvent(this._selectedFeature);
398
+ } else {
399
+ this.clear();
400
+ }
401
+ }
402
+
403
+ /**
404
+ * Clears the current feature. remove window, highlighting and provided feature.
405
+ * @private
406
+ */
407
+ _clearInternal() {
408
+ if (this._clearHighlightingCb) {
409
+ this._clearHighlightingCb();
410
+ this._clearHighlightingCb = null;
411
+ }
412
+ if (this._windowId) {
413
+ this._app.windowManager.remove(this._windowId);
414
+ this._windowId = null;
415
+ }
416
+
417
+ if (this._scratchLayer) {
418
+ this._scratchLayer.removeAllFeatures();
419
+ }
420
+ }
421
+
422
+ /**
423
+ * Deselecting feature clears highlighting and closes FeatureInfoView. fires feature changed with null
424
+ */
425
+ clear() {
426
+ this._clearInternal();
427
+ if (this._selectedFeature) {
428
+ this._selectedFeature = null;
429
+ this._featureChanged.raiseEvent(this._selectedFeature);
430
+ }
431
+ }
432
+
433
+ /**
434
+ * Destroys the feature info and all its events & listeners
435
+ */
436
+ destroy() {
437
+ this._clearInternal();
438
+ this._featureChanged.destroy();
439
+ this._app.toolboxManager.requestGroup('featureInfo').buttonManager.remove('featureInfoTool');
440
+ if (this._scratchLayer) {
441
+ this._app.layers.remove(this._scratchLayer);
442
+ this._scratchLayer.destroy();
443
+ }
444
+ this._listeners.forEach(cb => cb());
445
+ this._listeners.splice(0);
446
+ this._collection.destroy();
447
+ this._featureInfoClassRegistry.destroy();
448
+ }
449
+ }
450
+
451
+ export default FeatureInfo;
452
+ featureInfoClassRegistry.registerClass(TableFeatureInfoView.className, TableFeatureInfoView);
453
+ featureInfoClassRegistry.registerClass(IframeFeatureInfoView.className, IframeFeatureInfoView);
454
+ featureInfoClassRegistry.registerClass(BalloonFeatureInfoView.className, BalloonFeatureInfoView);
455
+ featureInfoClassRegistry.registerClass(AddressBalloonFeatureInfoView.className, AddressBalloonFeatureInfoView);
@@ -0,0 +1,42 @@
1
+ import { AbstractInteraction, EventType, ModificationKeyType } from '@vcmap/core';
2
+
3
+ /**
4
+ * @class
5
+ * @extends {import("@vcmap/core").AbstractInteraction}
6
+ */
7
+ class FeatureInfoInteraction extends AbstractInteraction {
8
+ /**
9
+ * @param {FeatureInfo} featureInfo
10
+ */
11
+ constructor(featureInfo) {
12
+ super(EventType.CLICK, ModificationKeyType.NONE);
13
+ /**
14
+ * @type {FeatureInfo}
15
+ * @private
16
+ */
17
+ this._featureInfo = featureInfo;
18
+ this.setActive();
19
+ }
20
+
21
+ /**
22
+ * @param {import("@vcmap/core").InteractionEvent} event
23
+ * @returns {Promise<import("@vcmap/core").InteractionEvent>}
24
+ */
25
+ async pipe(event) {
26
+ if (event.feature) {
27
+ if (!this._featureInfo.selectedFeature || event.feature.getId() !== this._featureInfo.selectedFeature.getId()) {
28
+ event.stopPropagation = true;
29
+ await this._featureInfo.selectFeature(
30
+ event.feature,
31
+ event.position,
32
+ [event.windowPosition.x, event.windowPosition.y],
33
+ );
34
+ }
35
+ } else {
36
+ await this._featureInfo.clear();
37
+ }
38
+ return event;
39
+ }
40
+ }
41
+
42
+ export default FeatureInfoInteraction;