@vcmap/ui 6.1.0-rc.7 → 6.1.1

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 (106) hide show
  1. package/build/bundle.js +3 -3
  2. package/config/base.config.json +7 -3
  3. package/config/cluster.config.json +1 -1
  4. package/config/dev.config.json +172 -56
  5. package/config/projects.config.json +2 -1
  6. package/config/vectorTile.config.json +42 -1
  7. package/dist/assets/cesium.js +1 -1
  8. package/dist/assets/{core-52c2ef11.js → core-5ae90f6d.js} +7515 -5451
  9. package/dist/assets/core.js +1 -1
  10. package/dist/assets/ol.js +1 -1
  11. package/dist/assets/{ui-dccb9009.css → ui-e659989f.css} +1 -1
  12. package/dist/assets/{ui-dccb9009.js → ui-e659989f.js} +21736 -20926
  13. package/dist/assets/ui.js +1 -1
  14. package/dist/assets/vue.js +1 -1
  15. package/dist/assets/{vuetify-43a20e18.css → vuetify-cc6a8213.css} +2 -2
  16. package/dist/assets/{vuetify-43a20e18.js → vuetify-cc6a8213.js} +6694 -6593
  17. package/dist/assets/vuetify.js +1 -1
  18. package/index.d.ts +14 -6
  19. package/index.js +14 -5
  20. package/package.json +18 -9
  21. package/plugins/@vcmap-show-case/vector-properties-example/src/LayerSettings.vue +39 -0
  22. package/plugins/@vcmap-show-case/vector-properties-example/src/VectorPropertiesExample.vue +3 -0
  23. package/plugins/@vcmap-show-case/vector-properties-example/src/lib.js +13 -0
  24. package/plugins/@vcmap-show-case/window-tester/src/WindowExample.vue +9 -0
  25. package/plugins/package.json +9 -5
  26. package/src/actions/actionHelper.d.ts +6 -0
  27. package/src/actions/actionHelper.js +30 -2
  28. package/src/actions/deepPickingAction.d.ts +23 -0
  29. package/src/actions/deepPickingAction.js +404 -0
  30. package/src/actions/extentActions.js +20 -6
  31. package/src/actions/flightActions.js +5 -1
  32. package/src/application/VcsApp.vue +33 -21
  33. package/src/application/VcsApp.vue.d.ts +4 -0
  34. package/src/application/VcsAttributionsFooter.vue +1 -0
  35. package/src/application/VcsContainer.vue +1 -0
  36. package/src/application/VcsContainer.vue.d.ts +4 -0
  37. package/src/application/VcsMobileMenuList.vue +34 -41
  38. package/src/application/VcsNavbar.vue +3 -0
  39. package/src/application/VcsNavbarMobile.vue +6 -18
  40. package/src/application/VcsNavbarMobile.vue.d.ts +0 -1
  41. package/src/application/VcsPositionDisplay.vue +1 -0
  42. package/src/callback/activateLayersCallback.js +9 -1
  43. package/src/callback/addModuleCallback.js +2 -1
  44. package/src/components/buttons/VcsActionButtonList.vue +1 -0
  45. package/src/components/form-inputs-controls/VcsSelect.vue +8 -6
  46. package/src/components/icons/+all.d.ts +5 -0
  47. package/src/components/icons/+all.js +14 -0
  48. package/src/components/lists/VcsActionList.vue +1 -0
  49. package/src/components/lists/VcsGroupedList.vue +2 -1
  50. package/src/components/lists/VcsListItemComponent.vue +1 -0
  51. package/src/components/lists/VcsTreeNode.vue +5 -1
  52. package/src/components/lists/VcsTreeview.vue +14 -2
  53. package/src/components/style/{MenuWrapper.vue → StyleMenuWrapper.vue} +2 -1
  54. package/src/components/style/VcsFillMenu.vue +4 -4
  55. package/src/components/style/VcsImageMenu.vue +4 -4
  56. package/src/components/style/VcsStrokeMenu.vue +4 -4
  57. package/src/components/style/VcsTextMenu.vue +4 -4
  58. package/src/contentTree/LayerTree.vue +8 -46
  59. package/src/contentTree/LayerTree.vue.d.ts +1 -3
  60. package/src/contentTree/contentTreeCollection.d.ts +7 -0
  61. package/src/contentTree/contentTreeCollection.js +30 -10
  62. package/src/contentTree/contentTreeItem.d.ts +4 -4
  63. package/src/contentTree/contentTreeItem.js +2 -2
  64. package/src/contentTree/groupContentTreeItem.d.ts +5 -0
  65. package/src/contentTree/groupContentTreeItem.js +1 -1
  66. package/src/contentTree/layerContentTreeItem.js +1 -1
  67. package/src/contentTree/layerGroupContentTreeItem.js +11 -1
  68. package/src/contentTree/nodeContentTreeItem.d.ts +21 -0
  69. package/src/contentTree/nodeContentTreeItem.js +31 -2
  70. package/src/contentTree/wmsChildContentTreeItem.d.ts +56 -0
  71. package/src/contentTree/wmsChildContentTreeItem.js +159 -0
  72. package/src/contentTree/wmsGroupContentTreeItem.d.ts +171 -0
  73. package/src/contentTree/wmsGroupContentTreeItem.js +620 -0
  74. package/src/featureInfo/ClusterFeatureComponent.vue +47 -11
  75. package/src/featureInfo/ClusterFeatureComponent.vue.d.ts +1 -0
  76. package/src/featureInfo/balloonFeatureInfoView.d.ts +3 -0
  77. package/src/featureInfo/balloonFeatureInfoView.js +78 -11
  78. package/src/featureInfo/balloonHelper.js +8 -12
  79. package/src/featureInfo/featureInfo.d.ts +32 -7
  80. package/src/featureInfo/featureInfo.js +193 -91
  81. package/src/i18n/de.d.ts +22 -16
  82. package/src/i18n/de.js +4 -0
  83. package/src/i18n/en.d.ts +22 -16
  84. package/src/i18n/en.js +4 -0
  85. package/src/legend/legendHelper.d.ts +15 -0
  86. package/src/legend/legendHelper.js +28 -3
  87. package/src/manager/toolbox/GroupToolboxComponent.vue +1 -0
  88. package/src/manager/toolbox/SelectToolboxComponent.vue +2 -0
  89. package/src/manager/toolbox/ToolboxManagerComponent.vue +1 -0
  90. package/src/manager/window/windowManager.d.ts +2 -2
  91. package/src/manager/window/windowManager.js +17 -16
  92. package/src/navigation/MapNavigation.vue +3 -1
  93. package/src/navigation/overviewMap.js +1 -1
  94. package/src/notifier/NotifierComponent.vue +1 -0
  95. package/src/search/ResultsComponent.vue +10 -1
  96. package/src/search/SearchComponent.vue +11 -6
  97. package/src/search/search.js +3 -16
  98. package/src/state.d.ts +2 -1
  99. package/src/state.js +2 -1
  100. package/src/uiConfig.d.ts +9 -0
  101. package/src/uiConfig.js +1 -0
  102. package/src/vcsUiApp.js +7 -1
  103. /package/dist/assets/{cesium-6c6aa853.js → cesium-be8a1422.js} +0 -0
  104. /package/dist/assets/{ol-b0589b0c.js → ol-d5f8aba6.js} +0 -0
  105. /package/dist/assets/{vue-f7a0b088.js → vue-3435e55b.js} +0 -0
  106. /package/src/components/style/{MenuWrapper.vue.d.ts → StyleMenuWrapper.vue.d.ts} +0 -0
@@ -0,0 +1,404 @@
1
+ import {
2
+ cartesianToMercator,
3
+ FeatureProviderInteraction,
4
+ getFeatureFromPickObject,
5
+ isProvidedClusterFeature,
6
+ isProvidedFeature,
7
+ markVolatile,
8
+ maxZIndex,
9
+ mercatorProjection,
10
+ mercatorToCartesian,
11
+ moduleIdSymbol,
12
+ originalFeatureSymbol,
13
+ vcsLayerName,
14
+ vectorClusterGroupName,
15
+ VectorLayer,
16
+ VectorStyleItem,
17
+ volatileModuleId,
18
+ } from '@vcmap/core';
19
+ import { Feature } from 'ol';
20
+ import { Point, LineString } from 'ol/geom.js';
21
+ import { Icon } from 'ol/style.js';
22
+ import { Cartesian3, Ray } from '@vcmap-cesium/engine';
23
+ import { watch } from 'vue';
24
+ import { getLogger } from '@vcsuite/logger';
25
+ import { getColorByKey } from '../vuePlugins/vuetify.js';
26
+ import ClusterFeatureComponent from '../featureInfo/ClusterFeatureComponent.vue';
27
+ import { WindowSlot } from '../manager/window/windowManager.js';
28
+ import { vcsAppSymbol } from '../pluginHelper.js';
29
+ import {
30
+ featureInfoViewSymbol,
31
+ getFeatureInfoViewForFeature,
32
+ getGroupedFeatureList,
33
+ } from '../featureInfo/featureInfo.js';
34
+ import { getColoredMapIcon } from '../components/icons/+all.js';
35
+
36
+ const scratchCartesian = new Cartesian3();
37
+ const pickingZOffset = 10.0;
38
+ const iconScale = 1.5;
39
+
40
+ export const deepPickingWindowId = 'deep-picking-window';
41
+
42
+ /**
43
+ * @param {import("@vcmap/ui").VcsUiApp} app
44
+ * @returns {{ layer: import("@vcmap/core").VectorLayer, destroy: () => void }}
45
+ */
46
+ export function setupDeepPickingLayer(app) {
47
+ const layer = new VectorLayer({
48
+ zIndex: maxZIndex,
49
+ projection: mercatorProjection.toJSON(),
50
+ vectorProperties: {
51
+ altitudeMode: 'absolute',
52
+ eyeOffset: [0, 0, -200],
53
+ },
54
+ allowPicking: false,
55
+ });
56
+ markVolatile(layer);
57
+ app.layers.add(layer);
58
+ layer.activate().catch(() => {
59
+ getLogger('deepPickingAction').error(
60
+ 'Could not activate deep picking layer',
61
+ );
62
+ });
63
+
64
+ const style = new VectorStyleItem({
65
+ image: {
66
+ ...getColoredMapIcon(getColorByKey(app, 'primary')),
67
+ scale: iconScale,
68
+ },
69
+ fill: {
70
+ color: 'rgba(237, 237, 237, 0.1)',
71
+ },
72
+ stroke: {
73
+ color: getColorByKey(app, 'primary'),
74
+ width: 5,
75
+ },
76
+ });
77
+ layer.setStyle(style);
78
+
79
+ function setIconColor() {
80
+ const color = getColorByKey(app, 'primary');
81
+ style.stroke?.setColor(color);
82
+ style.image = new Icon({ ...getColoredMapIcon(color), scale: iconScale });
83
+ layer.forceRedraw();
84
+ }
85
+
86
+ const themChangedListener = app.themeChanged.addEventListener(setIconColor);
87
+
88
+ const destroy = () => {
89
+ layer.deactivate();
90
+ app.layers.remove(layer);
91
+ layer.destroy();
92
+ themChangedListener();
93
+ };
94
+
95
+ return { layer, destroy };
96
+ }
97
+
98
+ /**
99
+ * Retrieves features from an OpenLayers map at a given pixel.
100
+ * @param {import("ol/Map.js").OLMap} map
101
+ * @param {[number, number]} pixel
102
+ * @param {number} hitTolerance
103
+ * @param {number} drill
104
+ * @returns {import("ol").Feature[]}
105
+ */
106
+ function getFeaturesFromOlMap(map, pixel, hitTolerance, drill) {
107
+ const features = [];
108
+ let i = 0;
109
+ map.forEachFeatureAtPixel(
110
+ pixel,
111
+ (feat) => {
112
+ if (
113
+ feat &&
114
+ (feat.get('olcs_allowPicking') == null ||
115
+ feat.get('olcs_allowPicking') === true)
116
+ ) {
117
+ const feature = feat[originalFeatureSymbol] || feat;
118
+ if (!feature[vectorClusterGroupName]) {
119
+ features.push(feature);
120
+ }
121
+ }
122
+ i += 1;
123
+ return i >= drill;
124
+ },
125
+ { hitTolerance },
126
+ );
127
+
128
+ return features;
129
+ }
130
+
131
+ /**
132
+ * Retrieves features from a Cesium scene at a given window position.
133
+ * @param {import("@vcmap-cesium/engine").Scene} scene
134
+ * @param {import("@vcmap-cesium/engine").Ray} ray
135
+ * @param {number} hitTolerance
136
+ * @param {number} drill
137
+ * @returns {Promise<{ features: import("@vcmap/core").EventFeature[], minZ: number }>}
138
+ */
139
+ async function getFeaturesFromScene(scene, ray, hitTolerance, drill) {
140
+ const { depthTestAgainstTerrain } = scene.globe;
141
+ scene.globe.depthTestAgainstTerrain = false;
142
+
143
+ let minZ = 0;
144
+ const objects = await scene.drillPickFromRay(
145
+ ray,
146
+ drill,
147
+ undefined,
148
+ hitTolerance,
149
+ );
150
+
151
+ scene.globe.depthTestAgainstTerrain = depthTestAgainstTerrain;
152
+
153
+ const features = objects
154
+ .map(({ object, position }) => {
155
+ let feature;
156
+ if (object) {
157
+ if (position) {
158
+ const z = cartesianToMercator(position)[2];
159
+ minZ = Math.min(minZ, z);
160
+ }
161
+ feature = getFeatureFromPickObject(object);
162
+ if (feature?.[vectorClusterGroupName]) {
163
+ const clusterFeatures = feature.get('features');
164
+ return [...clusterFeatures];
165
+ }
166
+ }
167
+ return feature;
168
+ })
169
+ .filter((f, i, a) => !!f && a.indexOf(f) === i);
170
+ return { features, minZ };
171
+ }
172
+
173
+ /**
174
+ * @param {import("@vcmap/core").InteractionEvent} event
175
+ * @param {number} [hitTolerance=10]
176
+ * @param {number} [drillLimit]
177
+ * @returns {Promise<{ features: import("@vcmap/core").EventFeature[], minZ: number }>}
178
+ */
179
+ async function getDeepPickingFeatures(
180
+ event,
181
+ hitTolerance = 10,
182
+ drillLimit = undefined,
183
+ ) {
184
+ let features = [];
185
+ let minZ = 0;
186
+ if (
187
+ event.map.className === 'OpenlayersMap' ||
188
+ event.map.className === 'ObliqueMap'
189
+ ) {
190
+ features = getFeaturesFromOlMap(
191
+ event.map.olMap,
192
+ [event.windowPosition.x, event.windowPosition.y],
193
+ hitTolerance,
194
+ drillLimit,
195
+ );
196
+ } else if (event.map.className === 'CesiumMap') {
197
+ const cesiumMap = event.map;
198
+ const scene = cesiumMap.getScene();
199
+
200
+ if (!scene) {
201
+ return features;
202
+ }
203
+
204
+ const [x, y, z] = event.position;
205
+ const origin = mercatorToCartesian([
206
+ x,
207
+ y,
208
+ Number.isNaN(z) ? pickingZOffset : z + pickingZOffset,
209
+ ]);
210
+ const direction = Cartesian3.normalize(
211
+ Cartesian3.negate(origin, scratchCartesian),
212
+ scratchCartesian,
213
+ );
214
+ const ray = new Ray(origin, direction);
215
+
216
+ ({ features, minZ } = await getFeaturesFromScene(
217
+ scene,
218
+ ray,
219
+ hitTolerance,
220
+ drillLimit,
221
+ ));
222
+ }
223
+
224
+ if (
225
+ event.feature &&
226
+ !event.feature[isProvidedFeature] && // provided features will be provided again.
227
+ !features.includes(event.feature)
228
+ ) {
229
+ features.unshift(event.feature);
230
+ }
231
+
232
+ return { features, minZ };
233
+ }
234
+
235
+ /**
236
+ * @param {import("@vcmap/core").AbstractInteraction} interaction
237
+ * @param {import("@vcmap/core").InteractionEvent} event
238
+ * @returns {Promise<import("@vcmap/core").EventFeature[]>}
239
+ */
240
+ async function getInteractionFeatures(interaction, event) {
241
+ const pipedEvent = await interaction.pipe({
242
+ ...event,
243
+ feature: undefined,
244
+ });
245
+ const { feature } = pipedEvent;
246
+ if (feature) {
247
+ if (feature[isProvidedClusterFeature]) {
248
+ return feature.get('features');
249
+ }
250
+ return [feature];
251
+ }
252
+ return [];
253
+ }
254
+
255
+ /**
256
+ * @returns {{ collectFeatures: (event: import("@vcmap/core").InteractionEvent) => Promise<{ features: import("@vcmap/core").EventFeature[], minZ: number }>, destroy: () => void}}
257
+ */
258
+ function createCollectFeatures() {
259
+ const featureProviderInteraction = new FeatureProviderInteraction();
260
+ return {
261
+ async collectFeatures(event) {
262
+ const { features: deepPickingFeatures, minZ } =
263
+ await getDeepPickingFeatures(event);
264
+ return {
265
+ features: [
266
+ ...deepPickingFeatures,
267
+ ...(await getInteractionFeatures(featureProviderInteraction, event)),
268
+ ],
269
+ minZ,
270
+ };
271
+ },
272
+ destroy() {
273
+ featureProviderInteraction.destroy();
274
+ },
275
+ };
276
+ }
277
+
278
+ /**
279
+ * @param {import("../vcsUiApp.js").default} app
280
+ * @param {import("ol/Feature").default} f
281
+ * @returns {boolean}
282
+ */
283
+ function featurePredicate(app, f) {
284
+ if (f[featureInfoViewSymbol]) {
285
+ return true;
286
+ }
287
+
288
+ const l = app.layers.getByKey(f[vcsLayerName]);
289
+ if (l?.[moduleIdSymbol] === volatileModuleId) {
290
+ return !!getFeatureInfoViewForFeature(app, f);
291
+ }
292
+
293
+ return !!l;
294
+ }
295
+
296
+ /**
297
+ * @param {import("../vcsUiApp.js").default} app
298
+ * @param {import("@vcmap/core").VectorLayer} layer
299
+ * @param {(event: import("@vcmap/core").InteractionEvent) => Promise<import("@vcmap/core").EventFeature[]>} collectFeatures
300
+ * @param {import("@vcmap/core").InteractionEvent} event
301
+ * @returns {import("./actionHelper.js").VcsAction}
302
+ */
303
+ export function createDeepPickingAction(app, layer, collectFeatures, event) {
304
+ return {
305
+ name: 'featureInfo.deepPicking.title',
306
+ icon: '$vcsInfo',
307
+ async callback() {
308
+ const { features, minZ } = await collectFeatures(event);
309
+ const { items, groups } = getGroupedFeatureList(
310
+ app,
311
+ features.filter((f) => featurePredicate(app, f)),
312
+ event.position,
313
+ );
314
+
315
+ if (app.windowManager.has(deepPickingWindowId)) {
316
+ app.windowManager.remove(deepPickingWindowId);
317
+ }
318
+
319
+ app.windowManager.add(
320
+ {
321
+ id: deepPickingWindowId,
322
+ component: ClusterFeatureComponent,
323
+ props: {
324
+ items,
325
+ groups,
326
+ },
327
+ state: {
328
+ headerTitle: 'featureInfo.deepPicking.headerTitle',
329
+ headerIcon: '$vcsInfo',
330
+ },
331
+ slot: WindowSlot.DYNAMIC_LEFT,
332
+ },
333
+ vcsAppSymbol,
334
+ );
335
+
336
+ const windowListener = app.windowManager.removed.addEventListener(
337
+ ({ id: windowId }) => {
338
+ if (windowId === deepPickingWindowId) {
339
+ layer.removeAllFeatures();
340
+ windowListener();
341
+ }
342
+ },
343
+ );
344
+
345
+ const [x, y, z] = event.position;
346
+ const coordinate = [
347
+ x,
348
+ y,
349
+ Number.isNaN(z) ? pickingZOffset : z + pickingZOffset,
350
+ ];
351
+ const pickFeature = new Feature({
352
+ geometry: new Point(coordinate),
353
+ });
354
+ const rayFeature = new Feature({
355
+ geometry: new LineString([coordinate, [x, y, minZ]]),
356
+ });
357
+ layer.addFeatures([pickFeature, rayFeature]);
358
+ },
359
+ };
360
+ }
361
+
362
+ /**
363
+ * This adds deep picking action to the context menu, if not disabled in uiConfig
364
+ * @param {import("../vcsUiApp.js").default} app
365
+ * @returns {() => void}
366
+ */
367
+ export function setupDeepPicking(app) {
368
+ const { layer, destroy: destroyLayer } = setupDeepPickingLayer(app);
369
+ const { collectFeatures, destroy: destroyCollectFeatures } =
370
+ createCollectFeatures();
371
+
372
+ const handler = async (event) => {
373
+ if (event.windowPosition) {
374
+ return [createDeepPickingAction(app, layer, collectFeatures, event)];
375
+ }
376
+ return [];
377
+ };
378
+
379
+ if (app.uiConfig.enableDeepPicking !== false) {
380
+ app.contextMenuManager.addEventHandler(handler, vcsAppSymbol);
381
+ }
382
+
383
+ const stopWatching = watch(
384
+ () => app.uiConfig.config.enableDeepPicking,
385
+ (value, oldValue) => {
386
+ if (value !== false) {
387
+ if (!(oldValue !== false)) {
388
+ app.contextMenuManager.addEventHandler(handler, vcsAppSymbol);
389
+ }
390
+ } else {
391
+ app.contextMenuManager.removeHandler(handler);
392
+ }
393
+ },
394
+ );
395
+
396
+ return () => {
397
+ stopWatching();
398
+ destroyLayer();
399
+ destroyCollectFeatures();
400
+ layer.deactivate();
401
+ layer.destroy();
402
+ app.layers.remove(layer);
403
+ };
404
+ }
@@ -20,6 +20,7 @@ import { fromExtent } from 'ol/geom/Polygon.js';
20
20
  import { createOrUpdateFromCoordinates } from 'ol/extent.js';
21
21
  import { Polygon } from 'ol/geom.js';
22
22
  import { unByKey } from 'ol/Observable.js';
23
+ import { getLogger } from '@vcsuite/logger';
23
24
 
24
25
  /**
25
26
  * @param {import("@src/vcsUiApp.js").default} app
@@ -52,9 +53,12 @@ export function createLayerToggleAction(layer, disabled) {
52
53
  icon: '$vcsEye',
53
54
  active: false,
54
55
  disabled,
55
- async callback() {
56
+ callback() {
56
57
  if (!this.active) {
57
- await layer.activate();
58
+ layer.activate().catch(() => {
59
+ getLogger('extentActions').warn('Failed to activate layer');
60
+ this.title = 'components.extent.show';
61
+ });
58
62
  this.title = 'components.extent.hide';
59
63
  } else {
60
64
  layer.deactivate();
@@ -96,7 +100,11 @@ export function createExtentFeatureAction(
96
100
  callback() {
97
101
  if (!this.active) {
98
102
  this.active = true;
99
- layer.activate();
103
+ layer.activate().catch(() => {
104
+ getLogger('extentActions').warn('Failed to activate layer');
105
+ session?.stop();
106
+ this.active = false;
107
+ });
100
108
  const feature = layer.getFeatureById(featureId);
101
109
  layer.removeFeaturesById([featureId]);
102
110
  session = startCreateFeatureSession(app, layer, GeometryType.BBox);
@@ -176,7 +184,7 @@ function setupTranslateAction(
176
184
  title: 'components.extent.translate',
177
185
  icon: 'mdi-axis-arrow',
178
186
  active: false,
179
- async callback() {
187
+ callback() {
180
188
  if (session) {
181
189
  session.stop();
182
190
  } else {
@@ -189,7 +197,10 @@ function setupTranslateAction(
189
197
  suspendFeatureUpdate.value = false;
190
198
  });
191
199
  });
192
- await layer.activate();
200
+ layer.activate().catch(() => {
201
+ getLogger('extentActions').warn('Failed to activate layer');
202
+ session?.stop();
203
+ });
193
204
  session = startEditFeaturesSession(app, layer);
194
205
  session.stopped.addEventListener(() => {
195
206
  action.active = false;
@@ -246,7 +257,10 @@ function setupVertexAction(
246
257
  suspendFeatureUpdate.value = false;
247
258
  });
248
259
  });
249
- await layer.activate();
260
+ layer.activate().catch(() => {
261
+ getLogger('extentActions').warn('Failed to activate layer');
262
+ session?.stop();
263
+ });
250
264
  session = startEditGeometrySession(app, layer);
251
265
  session.stopped.addEventListener(() => {
252
266
  action.active = false;
@@ -1,5 +1,6 @@
1
1
  import { reactive } from 'vue';
2
2
  import { check, maybe, ofEnum } from '@vcsuite/check';
3
+ import { getLogger } from '@vcsuite/logger';
3
4
  import {
4
5
  createFlightVisualization,
5
6
  exportFlightAsGeoJson,
@@ -378,7 +379,10 @@ export async function createFlightVisualizationAction(
378
379
  if (this.active) {
379
380
  flightVis.deactivate();
380
381
  } else {
381
- await flightVis.activate();
382
+ flightVis.activate().catch(() => {
383
+ getLogger('flightActions').warn('Failed to activate layer');
384
+ this.active = false;
385
+ });
382
386
  }
383
387
  this.active = !this.active;
384
388
  },
@@ -79,6 +79,7 @@
79
79
  createMapButtonAction,
80
80
  createToggleAction,
81
81
  } from '../actions/actionHelper.js';
82
+ import { setupDeepPicking } from '../actions/deepPickingAction.js';
82
83
  import VcsSettings from './VcsSettings.vue';
83
84
  import { WindowSlot } from '../manager/window/windowManager.js';
84
85
  import CollectionManager from '../manager/collectionManager/CollectionManager.vue';
@@ -89,7 +90,11 @@
89
90
  isMobileLandscape,
90
91
  } from '../vuePlugins/vuetify.js';
91
92
  import VcsLegend from '../legend/VcsLegend.vue';
92
- import { getLegendEntries } from '../legend/legendHelper.js';
93
+ import {
94
+ getLayerLegend,
95
+ getLegendEntries,
96
+ getStyleLegend,
97
+ } from '../legend/legendHelper.js';
93
98
  import VcsAttributionsFooter from './VcsAttributionsFooter.vue';
94
99
  import VcsObliqueFooter from './VcsObliqueFooter.vue';
95
100
  import VcsTextPageFooter from './VcsTextPageFooter.vue';
@@ -279,9 +284,32 @@
279
284
  );
280
285
  }
281
286
  };
287
+
288
+ /**
289
+ * Handles legend button and window.
290
+ * Adds a button, if legend definitions are available or removes legend otherwise.
291
+ */
292
+ const handleLegend = () => {
293
+ const layersWithLegend = [...app.layers].filter((layer) =>
294
+ getLayerLegend(layer),
295
+ );
296
+ const stylesWithLegend = [...app.styles].filter((style) =>
297
+ getStyleLegend(style),
298
+ );
299
+ if (layersWithLegend < 1 && stylesWithLegend < 1) {
300
+ app.navbarManager.remove(legendComponentId);
301
+ app.windowManager.remove(legendComponentId);
302
+ } else {
303
+ addLegend();
304
+ }
305
+ };
306
+
282
307
  const { xs } = useDisplay();
283
308
  let currentEntryLength = entries.length;
284
309
  const watchEntries = watch(entries, (newValue) => {
310
+ if (newValue.length > currentEntryLength) {
311
+ handleLegend();
312
+ }
285
313
  if (
286
314
  app.uiConfig.config.openLegendOnAdd &&
287
315
  newValue.length > currentEntryLength &&
@@ -308,34 +336,16 @@
308
336
  currentEntryLength = newValue.length;
309
337
  });
310
338
 
311
- /**
312
- * Handles legend button and window.
313
- * Adds a button, if legend definitions are available or removes legend otherwise.
314
- */
315
- const handleLegend = () => {
316
- const layersWithLegend = [...app.layers].filter(
317
- (layer) => layer.style?.properties?.legend ?? layer.properties?.legend,
318
- );
319
- const stylesWithLegend = [...app.styles].filter(
320
- (style) => style?.properties?.legend,
321
- );
322
- if (layersWithLegend < 1 && stylesWithLegend < 1) {
323
- app.navbarManager.remove(legendComponentId);
324
- app.windowManager.remove(legendComponentId);
325
- } else {
326
- addLegend();
327
- }
328
- };
329
339
  handleLegend();
330
340
 
331
341
  const listeners = [
332
342
  app.layers.added.addEventListener((layer) => {
333
- if (layer.style?.properties?.legend ?? layer.properties?.legend) {
343
+ if (getLayerLegend(layer)) {
334
344
  addLegend();
335
345
  }
336
346
  }),
337
347
  app.styles.added.addEventListener((style) => {
338
- if (style?.properties?.legend) {
348
+ if (getStyleLegend(style)) {
339
349
  addLegend();
340
350
  }
341
351
  }),
@@ -814,6 +824,7 @@
814
824
  const destroyDisplayQualityListener = setupUiConfigDisplayQuality(app);
815
825
  const { attributionEntries, attributionAction, destroyAttributions } =
816
826
  setupAttributions(app);
827
+ const destroyDeepPicking = setupDeepPicking(app);
817
828
 
818
829
  let pluginMountedListener;
819
830
  onMounted(() => {
@@ -853,6 +864,7 @@
853
864
  destroyThemingListener();
854
865
  destroyDisplayQualityListener();
855
866
  destroyAttributions();
867
+ destroyDeepPicking();
856
868
  });
857
869
 
858
870
  const { smAndUp, xs } = useDisplay();
@@ -112,6 +112,10 @@ declare const _default: import("vue").DefineComponent<{
112
112
  * - an optional flag whether to activate feature info on startup (default active)
113
113
  */
114
114
  readonly startingFeatureInfo?: boolean | undefined;
115
+ /**
116
+ * - an optional flag whether to enable deep picking via right click context menu (default active)
117
+ */
118
+ readonly enableDeepPicking?: boolean | undefined;
115
119
  /**
116
120
  * - mouse event, when position display is updated. Either 'click' (default) or 'move'.
117
121
  */
@@ -15,6 +15,7 @@
15
15
  </div>
16
16
  <VcsButton
17
17
  class="flex-shrink-0"
18
+ :data-action-name="attributionAction.name"
18
19
  :key="attributionAction.name"
19
20
  :tooltip="attributionAction.title"
20
21
  :icon="attributionAction.icon"
@@ -22,6 +22,7 @@
22
22
  </template>
23
23
  <VcsButton
24
24
  v-if="!smAndUp"
25
+ :data-action-name="attributionAction.name"
25
26
  :key="attributionAction.name"
26
27
  :tooltip="attributionAction.title"
27
28
  icon="mdi-copyright"
@@ -26,6 +26,10 @@ declare const _default: import("vue").DefineComponent<{
26
26
  * - an optional flag whether to activate feature info on startup (default active)
27
27
  */
28
28
  readonly startingFeatureInfo?: boolean | undefined;
29
+ /**
30
+ * - an optional flag whether to enable deep picking via right click context menu (default active)
31
+ */
32
+ readonly enableDeepPicking?: boolean | undefined;
29
33
  /**
30
34
  * - mouse event, when position display is updated. Either 'click' (default) or 'move'.
31
35
  */