@vcmap/ui 6.2.1 → 6.2.2

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 (47) hide show
  1. package/build/postInstall.js +1 -1
  2. package/dist/assets/{cesium-607748ca.js → cesium-b021f072.js} +1513 -1313
  3. package/dist/assets/cesium.js +1 -1
  4. package/dist/assets/{core-ea8a27bd.js → core-4cd2e30d.js} +1554 -1551
  5. package/dist/assets/core-workers/panoramaImageWorker.js +1 -1
  6. package/dist/assets/core.js +1 -1
  7. package/dist/assets/{ol-8fe80c53.js → ol-c927b883.js} +1 -1
  8. package/dist/assets/ol.js +1 -1
  9. package/dist/assets/ui-b13e28a1.css +1 -0
  10. package/dist/assets/{ui-ffa735dc.js → ui-b13e28a1.js} +5542 -5402
  11. package/dist/assets/ui.js +1 -1
  12. package/dist/assets/vue.js +1 -1
  13. package/dist/assets/{vuetify-def19d80.js → vuetify-a1930526.js} +1 -1
  14. package/dist/assets/vuetify.js +1 -1
  15. package/index.d.ts +1 -1
  16. package/index.js +1 -0
  17. package/package.json +4 -3
  18. package/plugins/@vcmap-show-case/category-tester/src/CollectionComponentOptions.vue +41 -2
  19. package/plugins/@vcmap-show-case/category-tester/src/index.js +2 -0
  20. package/src/application/VcsSplashScreen.vue +9 -1
  21. package/src/components/lists/VcsTreeNode.vue +2 -1
  22. package/src/components/lists/VcsTreeNode.vue.d.ts +4 -0
  23. package/src/contentTree/LayerSwap.vue +9 -0
  24. package/src/contentTree/LayerSwap.vue.d.ts +14 -0
  25. package/src/featureInfo/balloonFeatureInfoView.d.ts +15 -2
  26. package/src/featureInfo/balloonFeatureInfoView.js +8 -103
  27. package/src/featureInfo/balloonHelper.d.ts +13 -0
  28. package/src/featureInfo/balloonHelper.js +77 -0
  29. package/src/featureInfo/featureInfo.d.ts +2 -1
  30. package/src/featureInfo/featureInfo.js +11 -3
  31. package/src/featureInfo/featureInfoInteraction.js +4 -1
  32. package/src/featureInfo/iframeWmsFeatureInfoView.js +12 -2
  33. package/src/i18n/de.d.ts +3 -0
  34. package/src/i18n/de.js +3 -0
  35. package/src/i18n/en.d.ts +3 -0
  36. package/src/i18n/en.js +3 -0
  37. package/src/legend/legendHelper.js +5 -0
  38. package/src/manager/collectionManager/CollectionComponentContent.vue +67 -3
  39. package/src/manager/collectionManager/CollectionComponentContent.vue.d.ts +3 -0
  40. package/src/manager/collectionManager/collectionComponentClass.d.ts +44 -1
  41. package/src/manager/collectionManager/collectionComponentClass.js +152 -2
  42. package/src/manager/panel/PanelComponent.vue +9 -1
  43. package/src/state.js +19 -6
  44. package/dist/assets/ui-ffa735dc.css +0 -1
  45. /package/dist/assets/core-workers/{panoramaImageWorker.js-a3ad230c.js → panoramaImageWorker.js-90c60e81.js} +0 -0
  46. /package/dist/assets/{vue-09a72820.js → vue-6a295c0b.js} +0 -0
  47. /package/dist/assets/{vuetify-def19d80.css → vuetify-a1930526.css} +0 -0
package/dist/assets/ui.js CHANGED
@@ -1 +1 @@
1
- export * from "./ui-ffa735dc.js";
1
+ export * from "./ui-b13e28a1.js";
@@ -1 +1 @@
1
- export * from "./vue-09a72820.js";
1
+ export * from "./vue-6a295c0b.js";
@@ -10,7 +10,7 @@ function loadCss(href) {
10
10
  elem.onerror = reject;
11
11
  document.head.appendChild(elem);
12
12
  });
13
- } await loadCss('./assets/vuetify-def19d80.css');import { watch as Q, onScopeDispose as Ze, effectScope as Zl, shallowRef as K, Fragment as ie, reactive as it, computed as b, watchEffect as Fe, toRefs as Yt, capitalize as Nn, isVNode as Rc, Comment as Nc, unref as ot, warn as ja, getCurrentInstance as Hc, ref as j, provide as De, inject as ye, defineComponent as zc, camelize as Ir, h as Gt, toRaw as Be, createVNode as r, mergeProps as O, onBeforeUnmount as Qe, readonly as Ql, onDeactivated as _r, onActivated as Wc, onMounted as Ye, nextTick as we, TransitionGroup as Jl, Transition as jt, isRef as Tn, toRef as $, onBeforeMount as ra, withDirectives as $e, resolveDirective as gt, vShow as Ct, onUpdated as jc, Text as Uc, resolveDynamicComponent as Yc, markRaw as Gc, Teleport as Kc, cloneVNode as qc, createTextVNode as Tt, onUnmounted as Tr, onBeforeUpdate as Xc, withModifiers as Tl, toDisplayString as Zc, vModelText as Qc, resolveComponent as Jc, render as Ar } from "./vue-09a72820.js";
13
+ } await loadCss('./assets/vuetify-a1930526.css');import { watch as Q, onScopeDispose as Ze, effectScope as Zl, shallowRef as K, Fragment as ie, reactive as it, computed as b, watchEffect as Fe, toRefs as Yt, capitalize as Nn, isVNode as Rc, Comment as Nc, unref as ot, warn as ja, getCurrentInstance as Hc, ref as j, provide as De, inject as ye, defineComponent as zc, camelize as Ir, h as Gt, toRaw as Be, createVNode as r, mergeProps as O, onBeforeUnmount as Qe, readonly as Ql, onDeactivated as _r, onActivated as Wc, onMounted as Ye, nextTick as we, TransitionGroup as Jl, Transition as jt, isRef as Tn, toRef as $, onBeforeMount as ra, withDirectives as $e, resolveDirective as gt, vShow as Ct, onUpdated as jc, Text as Uc, resolveDynamicComponent as Yc, markRaw as Gc, Teleport as Kc, cloneVNode as qc, createTextVNode as Tt, onUnmounted as Tr, onBeforeUpdate as Xc, withModifiers as Tl, toDisplayString as Zc, vModelText as Qc, resolveComponent as Jc, render as Ar } from "./vue-6a295c0b.js";
14
14
  function rt(e, n) {
15
15
  let t;
16
16
  function a() {
@@ -1 +1 @@
1
- export * from "./vuetify-def19d80.js";
1
+ export * from "./vuetify-a1930526.js";
package/index.d.ts CHANGED
@@ -325,7 +325,7 @@ export { applyKeyMapping, applyValueMapping, default as AbstractFeatureInfoView
325
325
  export type * from "./src/featureInfo/abstractFeatureInfoView.d.ts";
326
326
  export { extractNestedKey, default as BalloonFeatureInfoView } from "./src/featureInfo/balloonFeatureInfoView.js";
327
327
  export type * from "./src/featureInfo/balloonFeatureInfoView.d.ts";
328
- export { getBalloonPosition, setBalloonPosition, setupBalloonPositionListener } from "./src/featureInfo/balloonHelper.js";
328
+ export { getBalloonPosition, setBalloonPosition, setupBalloonPositionListener, getBalloonPositionFromFeature } from "./src/featureInfo/balloonHelper.js";
329
329
  export type * from "./src/featureInfo/balloonHelper.d.ts";
330
330
  export { getHighlightStyleFromStyle, getHighlightStyle, getClusterHighlightStyle, featureInfoViewSymbol } from "./src/featureInfo/featureInfo.js";
331
331
  export type * from "./src/featureInfo/featureInfo.d.ts";
package/index.js CHANGED
@@ -191,6 +191,7 @@ export {
191
191
  getBalloonPosition,
192
192
  setBalloonPosition,
193
193
  setupBalloonPositionListener,
194
+ getBalloonPositionFromFeature,
194
195
  } from './src/featureInfo/balloonHelper.js';
195
196
  export { default as BalloonComponent } from './src/featureInfo/BalloonComponent.vue';
196
197
  export { default as AddressBalloonComponent } from './src/featureInfo/AddressBalloonComponent.vue';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vcmap/ui",
3
- "version": "6.2.1",
3
+ "version": "6.2.2",
4
4
  "author": "Virtual City Systems",
5
5
  "license": "MIT",
6
6
  "scripts": {
@@ -58,13 +58,14 @@
58
58
  },
59
59
  "peerDependencies": {
60
60
  "@vcmap-cesium/engine": "^11.0.3",
61
- "@vcmap/core": "^6.2.2",
61
+ "@vcmap/core": "^6.2.3",
62
62
  "ol": "^10.4.0",
63
63
  "vue": "~3.4.38",
64
64
  "vuetify": "~3.7.14"
65
65
  },
66
66
  "overrides": {
67
- "esbuild": "^0.25.0"
67
+ "esbuild": "^0.25.0",
68
+ "@zip.js/zip.js": "2.7.68"
68
69
  },
69
70
  "devDependencies": {
70
71
  "@histoire/plugin-vue": "^0.17.17",
@@ -24,13 +24,22 @@
24
24
  />
25
25
  </v-col>
26
26
  </v-row>
27
+ <v-row no-gutters>
28
+ <v-col>
29
+ <VcsLabel>{{ $st(`pagination`) }}</VcsLabel>
30
+ </v-col>
31
+ <v-col>
32
+ <VcsCheckbox v-model="pagination" />
33
+ </v-col>
34
+ </v-row>
27
35
  </v-container>
28
36
  </template>
29
37
 
30
38
  <script>
31
- import { inject, isRef, reactive } from 'vue';
39
+ import { inject, isRef, reactive, watch, ref } from 'vue';
32
40
  import { VContainer, VRow, VCol } from 'vuetify/components';
33
41
  import { VcsCheckbox, VcsLabel, VcsTextField } from '@vcmap/ui';
42
+ import { Collection } from '@vcmap/core';
34
43
 
35
44
  /**
36
45
  * Shows CollectionComponentOptions of a provided collectionComponent
@@ -45,7 +54,9 @@
45
54
  VRow,
46
55
  VCol,
47
56
  },
57
+ props: {},
48
58
  setup() {
59
+ /** @type {import("@vcmap/ui").CollectionComponentClass} */
49
60
  const collectionComponent = inject('collectionComponent');
50
61
  const localOptions = reactive({
51
62
  draggable: collectionComponent.draggable,
@@ -54,7 +65,34 @@
54
65
  renamable: collectionComponent.renamable,
55
66
  removable: collectionComponent.removable,
56
67
  });
57
-
68
+ // clone is for demonstration purposes only: caches the original items while pagination is active.
69
+ const collectionClone = new Collection();
70
+ const pagination = ref(!!collectionComponent.pagination.value);
71
+ watch(pagination, (value) => {
72
+ if (value) {
73
+ collectionClone.clear();
74
+ [...collectionComponent.collection].forEach((item) => {
75
+ collectionClone.add(item);
76
+ });
77
+ collectionComponent.setPagination({
78
+ // just for demonstration purposes -> get Items should fetch items from server.
79
+ getItems(startIndex, count) {
80
+ return {
81
+ items: [...collectionClone].slice(
82
+ startIndex,
83
+ count + startIndex,
84
+ ),
85
+ total: collectionClone.size,
86
+ };
87
+ },
88
+ });
89
+ } else {
90
+ collectionComponent.setPagination(undefined);
91
+ [...collectionClone].forEach((item) => {
92
+ collectionComponent.collection.add(item);
93
+ });
94
+ }
95
+ });
58
96
  return {
59
97
  title: collectionComponent.title,
60
98
  keys: Object.keys(localOptions),
@@ -67,6 +105,7 @@
67
105
  collectionComponent[key] = value;
68
106
  }
69
107
  },
108
+ pagination,
70
109
  };
71
110
  },
72
111
  };
@@ -54,6 +54,7 @@ export default async function categoryTest() {
54
54
  singleSelect: 'Einfachauswahl',
55
55
  renamable: 'umbenennbar',
56
56
  removable: 'löschbar',
57
+ pagination: 'Pagination',
57
58
  fooEditor: {
58
59
  name: 'Name',
59
60
  title: 'Titel',
@@ -70,6 +71,7 @@ export default async function categoryTest() {
70
71
  singleSelect: 'single select',
71
72
  renamable: 'renamable',
72
73
  removable: 'removable',
74
+ pagination: 'Pagination',
73
75
  fooEditor: {
74
76
  name: 'Name',
75
77
  title: 'Title',
@@ -12,6 +12,9 @@
12
12
  <v-card>
13
13
  <v-card-text class="pb-0 overflow-y-auto">
14
14
  <VcsMarkdown :content="$st(options.content)" />
15
+ </v-card-text>
16
+
17
+ <div class="vcs-splash-screen-checkboxes px-8">
15
18
  <VcsCheckbox v-if="options.acceptInput" v-model="checkBox">
16
19
  <template #label>
17
20
  <VcsMarkdown
@@ -32,7 +35,7 @@
32
35
  </div>
33
36
  </template>
34
37
  </VcsCheckbox>
35
- </v-card-text>
38
+ </div>
36
39
 
37
40
  <v-card-actions class="vcs-splash-screen-actions">
38
41
  <div class="d-flex gc-2 w-100 justify-end">
@@ -197,4 +200,9 @@
197
200
  position: sticky !important;
198
201
  bottom: 0;
199
202
  }
203
+ .vcs-splash-screen-checkboxes {
204
+ background-color: rgb(var(--v-theme-surface));
205
+ position: sticky !important;
206
+ bottom: 0;
207
+ }
200
208
  </style>
@@ -53,7 +53,7 @@
53
53
  :disabled="item.disabled"
54
54
  right
55
55
  tooltip-position="right"
56
- block-overflow
56
+ :block-overflow="item.blockOverflow ?? true"
57
57
  class="col-4 pa-0 ml-auto pr-4"
58
58
  />
59
59
  </slot>
@@ -133,6 +133,7 @@
133
133
  * @property {Array<VcsTreeNodeItem>} [children] - An optional array of children. Can be binded to another key, using the `item-children` attributes of the VcsTreeview component.
134
134
  * @property {string|HTMLCanvasElement|HTMLImageElement|undefined} [icon] - An optional icon to display with this item. Can be a URL or HTMLElement.
135
135
  * @property {function(string):void} [clicked] - A callback called when the item is clicked.
136
+ * @property {boolean} [blockOverflow=true] - Forwards the blockOverflow setting to the ActionButtonList, if true will reserve some space for an overflow.
136
137
  */
137
138
 
138
139
  /**
@@ -28,4 +28,8 @@ export type VcsTreeNodeItem = {
28
28
  * - A callback called when the item is clicked.
29
29
  */
30
30
  clicked?: ((arg0: string) => void) | undefined;
31
+ /**
32
+ * - Forwards the blockOverflow setting to the ActionButtonList, if true will reserve some space for an overflow.
33
+ */
34
+ blockOverflow?: boolean | undefined;
31
35
  };
@@ -89,6 +89,15 @@
89
89
  name: l.name,
90
90
  title: l.properties?.title || l.name,
91
91
  index: app.layers.indexOf(l),
92
+ actions: [
93
+ {
94
+ name: 'layer-swap.delete',
95
+ icon: 'mdi-delete',
96
+ title: 'components.layerSwap.deleteButton',
97
+ callback: () => l.deactivate(),
98
+ },
99
+ ],
100
+ blockOverflow: false,
92
101
  };
93
102
  if (wmsGroupItemsMap[l.name]) {
94
103
  return {
@@ -4,6 +4,13 @@ declare const _default: import("vue").DefineComponent<{}, {
4
4
  name: any;
5
5
  title: any;
6
6
  index: any;
7
+ actions: {
8
+ name: string;
9
+ icon: string;
10
+ title: string;
11
+ callback: () => any;
12
+ }[];
13
+ blockOverflow: boolean;
7
14
  } | {
8
15
  children: (Partial<import("./contentTreeItem.js", { with: { "resolution-mode": "import" } }).TreeViewItem> & {
9
16
  index: number;
@@ -13,6 +20,13 @@ declare const _default: import("vue").DefineComponent<{}, {
13
20
  name: any;
14
21
  title: any;
15
22
  index: any;
23
+ actions: {
24
+ name: string;
25
+ icon: string;
26
+ title: string;
27
+ callback: () => any;
28
+ }[];
29
+ blockOverflow: boolean;
16
30
  })[]>;
17
31
  dropTargetZones(item: any, targetItem: any): false | {
18
32
  into: boolean;
@@ -15,9 +15,23 @@ export type BalloonFeatureInfoViewProps = import("./abstractFeatureInfoView.js")
15
15
  balloonTitle: string;
16
16
  balloonSubtitle: string;
17
17
  position: import("ol/coordinate.js").Coordinate;
18
- heightReference: HeightReference;
18
+ heightReference: import("@vcmap-cesium/engine").HeightReference;
19
19
  heightOffset: number;
20
20
  };
21
+ /**
22
+ * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoViewOptions & { balloonTitle?: string, balloonSubtitle?: string }} BalloonFeatureInfoViewOptions
23
+ * @property {string} [balloonTitle] - optional title to overwrite default (layerName). Can be attribute key (nested key using '.'), i18n key or text
24
+ * @property {string} [balloonSubtitle] - optional window title to overwrite default (featureId). Can be attribute key (nested key using '.'), i18n key or text
25
+ */
26
+ /**
27
+ * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoProps & {
28
+ * balloonTitle: string,
29
+ * balloonSubtitle: string,
30
+ * position: import("ol/coordinate.js").Coordinate
31
+ * heightReference: import("@vcmap-cesium/engine").HeightReference,
32
+ * heightOffset: number
33
+ * }} BalloonFeatureInfoViewProps
34
+ */
21
35
  /**
22
36
  * @class
23
37
  * @description An balloon view.
@@ -48,5 +62,4 @@ declare class BalloonFeatureInfoView extends AbstractFeatureInfoView {
48
62
  */
49
63
  toJSON(): BalloonFeatureInfoViewOptions;
50
64
  }
51
- import { HeightReference } from '@vcmap-cesium/engine';
52
65
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
@@ -1,24 +1,8 @@
1
- import { Feature } from 'ol';
2
- import { getCenter } from 'ol/extent.js';
3
- import Point from 'ol/geom/Point.js';
4
- import {
5
- Cartographic,
6
- Entity,
7
- HeightReference,
8
- Math as CesiumMath,
9
- } from '@vcmap-cesium/engine';
10
- import {
11
- getGeometryHeight,
12
- getHeightInfo,
13
- isAbsoluteHeightReference,
14
- isRelativeHeightReference,
15
- Projection,
16
- VectorProperties,
17
- } from '@vcmap/core';
18
1
  import { check } from '@vcsuite/check';
19
2
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
20
3
  import { WindowSlot } from '../manager/window/windowManager.js';
21
4
  import BalloonComponent from './BalloonComponent.vue';
5
+ import { getBalloonPositionFromFeature } from './balloonHelper.js';
22
6
 
23
7
  /**
24
8
  * derive value from attributes
@@ -47,91 +31,11 @@ export function extractNestedKey(key, attrs, defaultValue = null) {
47
31
  * balloonTitle: string,
48
32
  * balloonSubtitle: string,
49
33
  * position: import("ol/coordinate.js").Coordinate
50
- * heightReference: HeightReference,
34
+ * heightReference: import("@vcmap-cesium/engine").HeightReference,
51
35
  * heightOffset: number
52
36
  * }} BalloonFeatureInfoViewProps
53
37
  */
54
38
 
55
- /**
56
- * @param {import("@vcmap/core").Cartesian3} cartesian
57
- * @returns {import("ol/coordinate.js").Coordinate}
58
- */
59
- function cartesian3ToCoordinate(cartesian) {
60
- const cartographic = Cartographic.fromCartesian(cartesian);
61
- const wgs84position = [
62
- CesiumMath.toDegrees(cartographic.longitude),
63
- CesiumMath.toDegrees(cartographic.latitude),
64
- cartographic.height,
65
- ];
66
- return Projection.wgs84ToMercator(wgs84position);
67
- }
68
-
69
- /**
70
- * @param {FeatureType} feature
71
- * @param {import("@vcmap/core").Layer} layer
72
- * @param {import("ol/coordinate.js").Coordinate|undefinded} clickedPosition
73
- * @returns {{position?: import("ol/coordinate.js").Coordinate, heightReference: HeightReference, heightOffset: number}}
74
- */
75
- function getPositionFromFeature(feature, layer, clickedPosition) {
76
- let heightReference = HeightReference.NONE;
77
- let heightOffset = 0;
78
- let position = null;
79
- if (feature instanceof Feature && feature.getGeometry() instanceof Point) {
80
- const point = feature.getGeometry();
81
- const vectorProperties =
82
- layer.vectorProperties ??
83
- layer.featureProvider?.vectorProperties ??
84
- new VectorProperties();
85
- const renderAs = vectorProperties.renderAs(feature);
86
- if (renderAs === 'geometry') {
87
- // special case where we do not want to use the clickedPosition but the exact Position of the Point
88
- const heightInfo = getHeightInfo(feature, point, vectorProperties);
89
- ({ heightReference } = heightInfo);
90
- let height = clickedPosition?.[2] ?? 0;
91
- position = point.getCoordinates();
92
- // if clamped, do nothing
93
- if (isRelativeHeightReference(heightReference)) {
94
- height = getGeometryHeight(point, heightInfo);
95
- if (heightInfo.groundLevel != null) {
96
- // we have a groundLevel, so no need to clamp the point
97
- heightReference = HeightReference.NONE;
98
- }
99
- if (heightInfo.heightAboveGround != null) {
100
- heightOffset += heightInfo.heightAboveGround;
101
- }
102
- const extrudedHeight = heightInfo.storeyHeightsAboveGround.reduce(
103
- (acc, storeyHeight) => acc + storeyHeight,
104
- 0,
105
- );
106
- heightOffset += extrudedHeight;
107
- height += extrudedHeight;
108
- position = [position[0], position[1], height];
109
- } else if (isAbsoluteHeightReference(heightReference)) {
110
- const extrudedHeight = heightInfo.storeyHeightsAboveGround.reduce(
111
- (acc, storeyHeight) => acc + storeyHeight,
112
- 0,
113
- );
114
- position = [
115
- position[0],
116
- position[1],
117
- heightInfo.groundLevelOrMinHeight + extrudedHeight,
118
- ];
119
- }
120
- return { position, heightOffset, heightReference };
121
- }
122
- }
123
- if (clickedPosition) {
124
- position = clickedPosition.slice();
125
- } else if (feature instanceof Feature && feature.getGeometry()) {
126
- position = getCenter(feature.getGeometry().getExtent());
127
- } else if (feature instanceof Entity) {
128
- position = cartesian3ToCoordinate(feature.position);
129
- } else if (feature?.primitive?.boundingSphere?.center) {
130
- position = cartesian3ToCoordinate(feature.primitive.boundingSphere.center);
131
- }
132
- return { position, heightReference, heightOffset };
133
- }
134
-
135
39
  /**
136
40
  * @class
137
41
  * @description An balloon view.
@@ -170,11 +74,12 @@ class BalloonFeatureInfoView extends AbstractFeatureInfoView {
170
74
  */
171
75
  getProperties(featureInfo, layer) {
172
76
  const properties = super.getProperties(featureInfo, layer);
173
- const { position, heightReference, heightOffset } = getPositionFromFeature(
174
- featureInfo.feature,
175
- layer,
176
- featureInfo.position,
177
- );
77
+ const { position, heightReference, heightOffset } =
78
+ getBalloonPositionFromFeature(
79
+ featureInfo.feature,
80
+ layer,
81
+ featureInfo.position,
82
+ );
178
83
  return {
179
84
  ...properties,
180
85
  position,
@@ -20,6 +20,17 @@ export function setBalloonPosition(windowManager: import("../manager/window/wind
20
20
  * @returns {Promise<(() => void)>}
21
21
  */
22
22
  export function setupBalloonPositionListener(vcsApp: import("@vcmap/core").VcsApp, windowId: string, clickedPosition: import("ol/coordinate.js").Coordinate): Promise<(() => void)>;
23
+ /**
24
+ * @param {Feature} feature
25
+ * @param {import("@vcmap/core").Layer} layer
26
+ * @param {import("ol/coordinate.js").Coordinate|undefined} clickedPosition
27
+ * @returns {{position?: import("ol/coordinate.js").Coordinate, heightReference: HeightReference, heightOffset: number}}
28
+ */
29
+ export function getBalloonPositionFromFeature(feature: Feature, layer: import("@vcmap/core").Layer, clickedPosition: import("ol/coordinate.js").Coordinate | undefined): {
30
+ position?: import("ol/coordinate.js").Coordinate;
31
+ heightReference: HeightReference;
32
+ heightOffset: number;
33
+ };
23
34
  /**
24
35
  * balloon offset from location or click position in pixel
25
36
  * @type {{x: number, y: number}}
@@ -29,3 +40,5 @@ export const balloonOffset: {
29
40
  y: number;
30
41
  };
31
42
  import { Cartesian2 } from '@vcmap-cesium/engine';
43
+ import Feature from 'ol/Feature.js';
44
+ import { HeightReference } from '@vcmap-cesium/engine';
@@ -2,15 +2,26 @@ import {
2
2
  Cartesian2,
3
3
  Cartographic,
4
4
  SceneTransforms,
5
+ HeightReference,
6
+ Entity,
5
7
  } from '@vcmap-cesium/engine';
6
8
  import {
9
+ cartesianToMercator,
7
10
  CesiumMap,
11
+ getGeometryHeight,
12
+ getHeightInfo,
13
+ isAbsoluteHeightReference,
14
+ isRelativeHeightReference,
8
15
  ObliqueMap,
9
16
  OpenlayersMap,
10
17
  Projection,
11
18
  transformToImage,
19
+ VectorProperties,
12
20
  } from '@vcmap/core';
13
21
  import { unByKey } from 'ol/Observable.js';
22
+ import Feature from 'ol/Feature.js';
23
+ import { Point } from 'ol/geom.js';
24
+ import { getCenter } from 'ol/extent.js';
14
25
  import {
15
26
  getWindowPositionOptionsFromMapEvent,
16
27
  WindowAlignment,
@@ -173,3 +184,69 @@ export async function setupBalloonPositionListener(
173
184
 
174
185
  return destroy;
175
186
  }
187
+
188
+ /**
189
+ * @param {Feature} feature
190
+ * @param {import("@vcmap/core").Layer} layer
191
+ * @param {import("ol/coordinate.js").Coordinate|undefined} clickedPosition
192
+ * @returns {{position?: import("ol/coordinate.js").Coordinate, heightReference: HeightReference, heightOffset: number}}
193
+ */
194
+ export function getBalloonPositionFromFeature(feature, layer, clickedPosition) {
195
+ let heightReference = HeightReference.NONE;
196
+ let heightOffset = 0;
197
+ let position = null;
198
+ if (feature instanceof Feature && feature.getGeometry() instanceof Point) {
199
+ const point = feature.getGeometry();
200
+ const vectorProperties =
201
+ layer.vectorProperties ??
202
+ layer.featureProvider?.vectorProperties ??
203
+ new VectorProperties();
204
+ const renderAs = vectorProperties.renderAs(feature);
205
+ if (renderAs === 'geometry') {
206
+ // special case where we do not want to use the clickedPosition but the exact Position of the Point
207
+ const heightInfo = getHeightInfo(feature, point, vectorProperties);
208
+ ({ heightReference } = heightInfo);
209
+ let height = clickedPosition?.[2] ?? 0;
210
+ position = point.getCoordinates();
211
+ // if clamped, do nothing
212
+ if (isRelativeHeightReference(heightReference)) {
213
+ height = getGeometryHeight(point, heightInfo);
214
+ if (heightInfo.groundLevel != null) {
215
+ // we have a groundLevel, so no need to clamp the point
216
+ heightReference = HeightReference.NONE;
217
+ }
218
+ if (heightInfo.heightAboveGround != null) {
219
+ heightOffset += heightInfo.heightAboveGround;
220
+ }
221
+ const extrudedHeight = heightInfo.storeyHeightsAboveGround.reduce(
222
+ (acc, storeyHeight) => acc + storeyHeight,
223
+ 0,
224
+ );
225
+ heightOffset += extrudedHeight;
226
+ height += extrudedHeight;
227
+ position = [position[0], position[1], height];
228
+ } else if (isAbsoluteHeightReference(heightReference)) {
229
+ const extrudedHeight = heightInfo.storeyHeightsAboveGround.reduce(
230
+ (acc, storeyHeight) => acc + storeyHeight,
231
+ 0,
232
+ );
233
+ position = [
234
+ position[0],
235
+ position[1],
236
+ heightInfo.groundLevelOrMinHeight + extrudedHeight,
237
+ ];
238
+ }
239
+ return { position, heightOffset, heightReference };
240
+ }
241
+ }
242
+ if (clickedPosition) {
243
+ position = clickedPosition.slice();
244
+ } else if (feature instanceof Feature && feature.getGeometry()) {
245
+ position = getCenter(feature.getGeometry().getExtent());
246
+ } else if (feature instanceof Entity) {
247
+ position = cartesianToMercator(feature.position);
248
+ } else if (feature?.primitive?.boundingSphere?.center) {
249
+ position = cartesianToMercator(feature.primitive.boundingSphere.center);
250
+ }
251
+ return { position, heightReference, heightOffset };
252
+ }
@@ -225,9 +225,10 @@ declare class FeatureInfo extends Collection<AbstractFeatureInfoView> {
225
225
  * The cluster feature will be cloned, highlighted and added on an internal scratch layer to ensure availability until deselection.
226
226
  * The original cluster feature will be hidden until deselection.
227
227
  * @param {import("ol").Feature} clusterFeature
228
+ * @param {import("ol/coordinate.js").Coordinate} position
228
229
  * @returns {Promise<void>}
229
230
  */
230
- selectClusterFeature(clusterFeature: import("ol").Feature): Promise<void>;
231
+ selectClusterFeature(clusterFeature: import("ol").Feature, position: import("ol/coordinate.js").Coordinate): Promise<void>;
231
232
  /**
232
233
  * Clears the current feature. remove window, highlighting and provided feature.
233
234
  * @private
@@ -296,7 +296,10 @@ function setupFeatureInfoTool(app) {
296
296
  });
297
297
 
298
298
  function addFeatureInfoButton() {
299
- if (app.uiConfig.getByKey('startingFeatureInfo')?.value !== false) {
299
+ if (
300
+ app.uiConfig.getByKey('startingFeatureInfo')?.value !== false &&
301
+ !action.active
302
+ ) {
300
303
  action.callback();
301
304
  }
302
305
  if (!app.toolboxManager.has('featureInfo')) {
@@ -754,9 +757,10 @@ class FeatureInfo extends Collection {
754
757
  * The cluster feature will be cloned, highlighted and added on an internal scratch layer to ensure availability until deselection.
755
758
  * The original cluster feature will be hidden until deselection.
756
759
  * @param {import("ol").Feature} clusterFeature
760
+ * @param {import("ol/coordinate.js").Coordinate} position
757
761
  * @returns {Promise<void>}
758
762
  */
759
- async selectClusterFeature(clusterFeature) {
763
+ async selectClusterFeature(clusterFeature, position) {
760
764
  this.clearFeature();
761
765
  this._clearClusterInternal();
762
766
  const id = `cluster-at-${clusterFeature.getGeometry().getCoordinates().join('-')}`;
@@ -800,7 +804,11 @@ class FeatureInfo extends Collection {
800
804
  this._scratchLayer.addFeatures([clonedFeature]);
801
805
 
802
806
  const features = clusterFeature.get('features');
803
- const { items, groups } = getGroupedFeatureList(this._app, features);
807
+ const { items, groups } = getGroupedFeatureList(
808
+ this._app,
809
+ features,
810
+ position,
811
+ );
804
812
 
805
813
  this._clusterWindowId = id;
806
814
  this._app.windowManager.add(
@@ -41,7 +41,10 @@ class FeatureInfoInteraction extends AbstractInteraction {
41
41
  featureId !== this._featureInfo.selectedClusterFeatureId
42
42
  ) {
43
43
  event.stopPropagation = true;
44
- await this._featureInfo.selectClusterFeature(event.feature);
44
+ await this._featureInfo.selectClusterFeature(
45
+ event.feature,
46
+ event.position,
47
+ );
45
48
  }
46
49
  } else if (
47
50
  !this._featureInfo.selectedFeature ||
@@ -1,6 +1,7 @@
1
1
  import { get as getOlProj } from 'ol/proj.js';
2
2
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
3
3
  import IframeComponent from './IframeComponent.vue';
4
+ import { getBalloonPositionFromFeature } from './balloonHelper.js';
4
5
 
5
6
  /**
6
7
  * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoViewOptions & { infoFormat: string, title?: string, sandbox?:string, disableSandbox?: boolean }} IframeWmsFeatureInfoViewOptions
@@ -54,17 +55,22 @@ class IframeWmsFeatureInfoView extends AbstractFeatureInfoView {
54
55
  * @returns {import("../manager/window/windowManager.js").WindowComponentOptions}
55
56
  */
56
57
  getWindowComponentOptions(app, featureInfo, layer) {
58
+ const position = getBalloonPositionFromFeature(
59
+ featureInfo.feature,
60
+ layer,
61
+ featureInfo.position,
62
+ );
57
63
  const componentOptions = super.getWindowComponentOptions(
58
64
  app,
59
65
  featureInfo,
60
66
  layer,
61
67
  );
62
68
  const resolution = app.maps.activeMap.getCurrentResolution(
63
- featureInfo.position,
69
+ position.position,
64
70
  );
65
71
  componentOptions.props.src =
66
72
  layer.featureProvider.wmsSource.getFeatureInfoUrl(
67
- featureInfo.position,
73
+ position.position,
68
74
  resolution,
69
75
  getOlProj('EPSG:3857'),
70
76
  { INFO_FORMAT: this.infoFormat },
@@ -79,9 +85,13 @@ class IframeWmsFeatureInfoView extends AbstractFeatureInfoView {
79
85
  * @returns {import("./iframeFeatureInfoView.js").IframeFeatureInfoViewProps}
80
86
  */
81
87
  getProperties(featureInfo, layer) {
88
+ const properties = super.getProperties(featureInfo, layer);
82
89
  return {
90
+ ...properties,
83
91
  src: layer.featureProvider.wmsSource.getFeatureInfoUrl(
84
92
  featureInfo.position,
93
+ // no correct resolution available due to missing app.
94
+ // Thats why same is done in `getWindowComponentOptions` to override the src with correct url
85
95
  1,
86
96
  getOlProj('EPSG:3857'),
87
97
  { INFO_FORMAT: this.infoFormat },