@vcmap/ui 6.1.0-rc.6 → 6.1.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.
Files changed (149) hide show
  1. package/config/base.config.json +7 -3
  2. package/config/cluster.config.json +5 -14
  3. package/config/dev.config.json +175 -56
  4. package/config/projects.config.json +2 -1
  5. package/config/splashscreen.config.json +6 -10
  6. package/config/vectorTile.config.json +42 -1
  7. package/dist/assets/{cesium-f5e8e354.js → cesium-664ad022.js} +53 -23
  8. package/dist/assets/cesium.js +1 -1
  9. package/dist/assets/{core-c134a524.js → core-841b71a4.js} +8458 -5828
  10. package/dist/assets/core.js +1 -1
  11. package/dist/assets/{ol-2752311f.js → ol-2e095c08.js} +87 -37
  12. package/dist/assets/ol.js +1 -1
  13. package/dist/assets/ui-2fd6f47d.css +1 -0
  14. package/dist/assets/{ui-83514586.js → ui-2fd6f47d.js} +21376 -20063
  15. package/dist/assets/ui.js +1 -1
  16. package/dist/assets/vue.js +1 -1
  17. package/dist/assets/{vuetify-5dbe2644.css → vuetify-4bc77ff7.css} +2 -2
  18. package/dist/assets/{vuetify-5dbe2644.js → vuetify-4bc77ff7.js} +7520 -7373
  19. package/dist/assets/vuetify.js +1 -1
  20. package/dist/index.html +1 -1
  21. package/index.d.ts +15 -5
  22. package/index.html +1 -1
  23. package/index.js +14 -5
  24. package/package.json +12 -8
  25. package/plugins/@vcmap-show-case/theming-example/src/index.js +1 -0
  26. package/plugins/@vcmap-show-case/vector-properties-example/src/LayerSettings.vue +39 -0
  27. package/plugins/@vcmap-show-case/vector-properties-example/src/VectorPropertiesExample.vue +3 -0
  28. package/plugins/@vcmap-show-case/vector-properties-example/src/lib.js +13 -0
  29. package/plugins/@vcmap-show-case/window-tester/src/WindowExample.vue +9 -0
  30. package/plugins/package.json +7 -5
  31. package/src/actions/actionHelper.d.ts +6 -0
  32. package/src/actions/actionHelper.js +22 -0
  33. package/src/actions/deepPickingAction.d.ts +23 -0
  34. package/src/actions/deepPickingAction.js +399 -0
  35. package/src/application/MapsGroupMobileMenu.vue +105 -0
  36. package/src/application/MapsGroupMobileMenu.vue.d.ts +7 -0
  37. package/src/application/VcsApp.vue +51 -24
  38. package/src/application/VcsApp.vue.d.ts +9 -2
  39. package/src/application/VcsAttributionsFooter.vue +1 -0
  40. package/src/application/VcsContainer.vue +36 -13
  41. package/src/application/VcsContainer.vue.d.ts +7 -0
  42. package/src/application/VcsMobileMenuList.vue +111 -0
  43. package/src/application/VcsMobileMenuList.vue.d.ts +2 -0
  44. package/src/application/VcsNavbar.vue +15 -3
  45. package/src/application/VcsNavbarMobile.vue +206 -0
  46. package/src/application/VcsNavbarMobile.vue.d.ts +42 -0
  47. package/src/application/VcsPositionDisplay.vue +1 -0
  48. package/src/application/VcsSplashScreen.vue +39 -7
  49. package/src/application/VcsSplashScreen.vue.d.ts +6 -0
  50. package/src/application/uiConfigHelper.d.ts +12 -0
  51. package/src/application/uiConfigHelper.js +37 -0
  52. package/src/components/buttons/VcsActionButtonList.vue +1 -0
  53. package/src/components/buttons/VcsToolButton.vue +8 -1
  54. package/src/components/buttons/VcsToolButton.vue.d.ts +1 -0
  55. package/src/components/form-inputs-controls/VcsSelect.vue +8 -6
  56. package/src/components/form-output/VcsTemplateMarkdown.vue +43 -0
  57. package/src/components/form-output/VcsTemplateMarkdown.vue.d.ts +9 -0
  58. package/src/components/icons/+all.d.ts +5 -0
  59. package/src/components/icons/+all.js +14 -0
  60. package/src/components/lists/VcsActionList.vue +1 -0
  61. package/src/components/lists/VcsGroupedList.vue +2 -1
  62. package/src/components/lists/VcsListItemComponent.vue +1 -0
  63. package/src/components/lists/VcsTreeNode.vue +11 -2
  64. package/src/components/lists/VcsTreeview.vue +40 -3
  65. package/src/components/lists/VcsTreeview.vue.d.ts +1 -0
  66. package/src/components/lists/VcsTreeviewTitle.vue +8 -1
  67. package/src/components/style/{MenuWrapper.vue → StyleMenuWrapper.vue} +2 -1
  68. package/src/components/style/VcsFillMenu.vue +4 -4
  69. package/src/components/style/VcsImageMenu.vue +4 -4
  70. package/src/components/style/VcsStrokeMenu.vue +4 -4
  71. package/src/components/style/VcsTextMenu.vue +4 -4
  72. package/src/contentTree/LayerTree.vue +8 -46
  73. package/src/contentTree/LayerTree.vue.d.ts +1 -3
  74. package/src/contentTree/contentTreeCollection.d.ts +7 -0
  75. package/src/contentTree/contentTreeCollection.js +31 -10
  76. package/src/contentTree/contentTreeItem.d.ts +4 -4
  77. package/src/contentTree/contentTreeItem.js +2 -2
  78. package/src/contentTree/flightContentTreeItem.d.ts +8 -1
  79. package/src/contentTree/flightContentTreeItem.js +26 -3
  80. package/src/contentTree/groupContentTreeItem.d.ts +21 -0
  81. package/src/contentTree/groupContentTreeItem.js +32 -2
  82. package/src/contentTree/layerContentTreeItem.d.ts +8 -1
  83. package/src/contentTree/layerContentTreeItem.js +26 -4
  84. package/src/contentTree/layerGroupContentTreeItem.d.ts +6 -0
  85. package/src/contentTree/layerGroupContentTreeItem.js +27 -3
  86. package/src/contentTree/nodeContentTreeItem.d.ts +21 -0
  87. package/src/contentTree/nodeContentTreeItem.js +31 -2
  88. package/src/contentTree/obliqueCollectionContentTreeItem.d.ts +6 -0
  89. package/src/contentTree/obliqueCollectionContentTreeItem.js +22 -2
  90. package/src/contentTree/wmsChildContentTreeItem.d.ts +56 -0
  91. package/src/contentTree/wmsChildContentTreeItem.js +159 -0
  92. package/src/contentTree/wmsGroupContentTreeItem.d.ts +171 -0
  93. package/src/contentTree/wmsGroupContentTreeItem.js +619 -0
  94. package/src/featureInfo/BalloonComponent.vue +6 -6
  95. package/src/featureInfo/ClusterFeatureComponent.vue +47 -11
  96. package/src/featureInfo/ClusterFeatureComponent.vue.d.ts +1 -0
  97. package/src/featureInfo/MarkdownBalloonComponent.vue +3 -9
  98. package/src/featureInfo/MarkdownBalloonComponent.vue.d.ts +1 -11
  99. package/src/featureInfo/balloonFeatureInfoView.d.ts +3 -0
  100. package/src/featureInfo/balloonFeatureInfoView.js +78 -11
  101. package/src/featureInfo/balloonHelper.js +9 -13
  102. package/src/featureInfo/featureInfo.d.ts +32 -7
  103. package/src/featureInfo/featureInfo.js +192 -93
  104. package/src/featureInfo/markdownBalloonFeatureInfoView.d.ts +0 -6
  105. package/src/featureInfo/markdownBalloonFeatureInfoView.js +5 -14
  106. package/src/featureInfo/markdownFeatureInfoView.d.ts +2 -8
  107. package/src/featureInfo/markdownFeatureInfoView.js +6 -15
  108. package/src/i18n/de.d.ts +64 -50
  109. package/src/i18n/de.js +9 -0
  110. package/src/i18n/en.d.ts +64 -50
  111. package/src/i18n/en.js +9 -0
  112. package/src/legend/VcsLegend.vue +21 -2
  113. package/src/legend/VcsLegend.vue.d.ts +1 -0
  114. package/src/legend/legendHelper.d.ts +0 -13
  115. package/src/legend/legendHelper.js +3 -27
  116. package/src/manager/navbarManager.d.ts +14 -1
  117. package/src/manager/navbarManager.js +22 -2
  118. package/src/manager/toolbox/GroupToolboxComponent.vue +17 -3
  119. package/src/manager/toolbox/GroupToolboxComponent.vue.d.ts +1 -0
  120. package/src/manager/toolbox/SelectToolboxComponent.vue +17 -3
  121. package/src/manager/toolbox/SelectToolboxComponent.vue.d.ts +1 -0
  122. package/src/manager/toolbox/ToolboxManagerComponent.vue +45 -14
  123. package/src/manager/toolbox/ToolboxManagerComponent.vue.d.ts +9 -0
  124. package/src/manager/toolbox/toolboxManager.d.ts +2 -1
  125. package/src/manager/toolbox/toolboxManager.js +13 -1
  126. package/src/manager/window/WindowComponent.vue +3 -2
  127. package/src/manager/window/WindowComponentHeader.vue +9 -1
  128. package/src/manager/window/WindowComponentHeader.vue.d.ts +1 -0
  129. package/src/manager/window/WindowManager.vue +175 -30
  130. package/src/manager/window/WindowManager.vue.d.ts +5 -0
  131. package/src/manager/window/windowManager.d.ts +2 -2
  132. package/src/manager/window/windowManager.js +12 -10
  133. package/src/navigation/MapNavigation.vue +29 -19
  134. package/src/navigation/MapNavigation.vue.d.ts +1 -0
  135. package/src/notifier/NotifierComponent.vue +1 -0
  136. package/src/search/ResultsComponent.vue +44 -17
  137. package/src/search/ResultsComponent.vue.d.ts +11 -1
  138. package/src/search/SearchComponent.vue +60 -9
  139. package/src/search/SearchComponent.vue.d.ts +2 -0
  140. package/src/search/search.js +3 -16
  141. package/src/state.d.ts +2 -1
  142. package/src/state.js +2 -1
  143. package/src/uiConfig.d.ts +9 -0
  144. package/src/uiConfig.js +1 -0
  145. package/src/vuePlugins/vuetify.d.ts +4 -0
  146. package/src/vuePlugins/vuetify.js +49 -3
  147. package/dist/assets/ui-83514586.css +0 -1
  148. /package/dist/assets/{vue-f8b1b5f8.js → vue-71fd14e8.js} +0 -0
  149. /package/src/components/style/{MenuWrapper.vue.d.ts → StyleMenuWrapper.vue.d.ts} +0 -0
@@ -18,13 +18,18 @@ import {
18
18
  alreadyTransformedToImage,
19
19
  ObliqueMap,
20
20
  originalFeatureSymbol,
21
+ mercatorToCartesian,
22
+ cartesianToMercator,
23
+ CesiumMap,
21
24
  } from '@vcmap/core';
22
25
  import { getLogger as getLoggerByName } from '@vcsuite/logger';
23
26
  import {
27
+ Cartographic,
24
28
  Cesium3DTileFeature,
25
29
  Cesium3DTilePointFeature,
26
30
  Color,
27
31
  Entity,
32
+ HeightReference,
28
33
  } from '@vcmap-cesium/engine';
29
34
  import { Feature } from 'ol';
30
35
  import { check, maybe, oneOf } from '@vcsuite/check';
@@ -44,6 +49,7 @@ import { ToolboxType } from '../manager/toolbox/toolboxManager.js';
44
49
  import MarkdownBalloonFeatureInfoView from './markdownBalloonFeatureInfoView.js';
45
50
  import IframeWmsFeatureInfoView from './iframeWmsFeatureInfoView.js';
46
51
  import ClusterFeatureComponent from './ClusterFeatureComponent.vue';
52
+ import { createZoomToFeatureAction } from '../actions/actionHelper.js';
47
53
 
48
54
  /** @typedef {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity} FeatureType */
49
55
 
@@ -152,6 +158,89 @@ export function getClusterHighlightStyle(
152
158
  return getHighlightStyleFromStyle(clusterStyle, fillColor);
153
159
  }
154
160
 
161
+ /**
162
+ * @param {import("../vcsUiApp.js").default} app
163
+ * @param {import("ol").Feature} feature
164
+ * @returns {import("./abstractFeatureInfoView.js").default|null}
165
+ */
166
+ export function getFeatureInfoViewForFeature(app, feature) {
167
+ if (feature[featureInfoViewSymbol]) {
168
+ return feature[featureInfoViewSymbol];
169
+ }
170
+ const layer = app.layers.getByKey(feature[vcsLayerName]);
171
+ const name = layer?.properties?.featureInfo;
172
+ if (!name) {
173
+ getLogger().debug(
174
+ `No view has been configured for layer '${layer?.name}'.`,
175
+ );
176
+ return null;
177
+ }
178
+ if (!app.featureInfo.hasKey(name)) {
179
+ getLogger().warning(`No view with name '${name}' has been registered.`);
180
+ return null;
181
+ }
182
+ return app.featureInfo.getByKey(name);
183
+ }
184
+
185
+ /**
186
+ * Returns a VcsGroupedListItem for each provided feature and corresponding groups
187
+ * @param {import("../vcsUiApp.js").default} app
188
+ * @param {import("@vcmap/core").EventFeature[]} features
189
+ * @param {import("ol/coordinate.js").Coordinate?} position
190
+ * @returns {{
191
+ * groups: import("../components/lists/VcsGroupedList.vue").VcsListGroup,
192
+ * items: import("../components/lists/VcsGroupedList.vue").VcsGroupedListItem,
193
+ * }}
194
+ */
195
+ export function getGroupedFeatureList(app, features, position = undefined) {
196
+ const groups = {};
197
+ const items = features.map((f) => {
198
+ const oFeature = f[originalFeatureSymbol] ?? f;
199
+ let actions;
200
+ if (oFeature instanceof Feature) {
201
+ actions = [
202
+ createZoomToFeatureAction(
203
+ { name: 'zoomToFeature', icon: 'mdi-target' },
204
+ oFeature,
205
+ app.maps,
206
+ ),
207
+ ];
208
+ }
209
+ /** @type {import("../components/lists/VcsListItemComponent.vue").VcsListItem} */
210
+ const listItem = reactive({
211
+ name: oFeature.getId(),
212
+ title:
213
+ oFeature.getAttributes()?.title ||
214
+ oFeature.getAttributes()?.name ||
215
+ oFeature.getId(),
216
+ disabled: !getFeatureInfoViewForFeature(app, oFeature),
217
+ selectionChanged: (value) => {
218
+ if (value) {
219
+ app.featureInfo
220
+ .selectFeature(oFeature, position)
221
+ .catch((e) => getLogger().error(e));
222
+ } else {
223
+ app.featureInfo.clearFeature();
224
+ }
225
+ },
226
+ actions,
227
+ });
228
+ const layerName = oFeature[vcsLayerName];
229
+ if (layerName) {
230
+ if (!groups[layerName]) {
231
+ const title = app.layers.getByKey(layerName)?.properties?.title;
232
+ groups[layerName] = {
233
+ name: layerName,
234
+ title: title || layerName,
235
+ };
236
+ }
237
+ listItem.group = layerName;
238
+ }
239
+ return listItem;
240
+ });
241
+ return { groups: Object.values(groups), items };
242
+ }
243
+
155
244
  /**
156
245
  * @param {import("../vcsUiApp.js").default} app
157
246
  * @returns {FeatureInfoSession}
@@ -224,6 +313,7 @@ function setupFeatureInfoTool(app) {
224
313
  action,
225
314
  },
226
315
  vcsAppSymbol,
316
+ { desktop: true, tablet: true, mobile: true },
227
317
  );
228
318
  }
229
319
  }
@@ -277,6 +367,52 @@ function setupFeatureInfoTool(app) {
277
367
  };
278
368
  }
279
369
 
370
+ /**
371
+ * @param {import("../vcsUiApp.js").default} app
372
+ * @param {import("./balloonFeatureInfoView.js").BalloonFeatureInfoViewProps} props
373
+ * @returns {() => void}
374
+ */
375
+ function setupBalloonHeightListener(app, props) {
376
+ let updateHeightListener = () => {};
377
+ function setupUpdateHeightListener(map) {
378
+ updateHeightListener();
379
+ if (map instanceof CesiumMap) {
380
+ const cartesian = mercatorToCartesian(props.position);
381
+ const cartographic = Cartographic.fromCartesian(cartesian);
382
+ const scene = map.getScene();
383
+ cartographic.height =
384
+ scene.getHeight(cartographic, props.heightReference) +
385
+ props.heightOffset;
386
+ props.position.splice(
387
+ 0,
388
+ Infinity,
389
+ ...cartesianToMercator(Cartographic.toCartesian(cartographic)),
390
+ );
391
+
392
+ updateHeightListener = scene.updateHeight(
393
+ cartographic,
394
+ (clampedCartographic) => {
395
+ const pos = cartesianToMercator(
396
+ Cartographic.toCartesian(clampedCartographic),
397
+ );
398
+ pos[2] += props.heightOffset;
399
+ props.position.splice(0, Infinity, ...pos);
400
+ },
401
+ props.heightReference,
402
+ );
403
+ }
404
+ }
405
+ setupUpdateHeightListener(app.maps.activeMap);
406
+ const mapActivatedListener = app.maps.mapActivated.addEventListener((map) => {
407
+ setupUpdateHeightListener(map);
408
+ });
409
+
410
+ return () => {
411
+ updateHeightListener();
412
+ mapActivatedListener();
413
+ };
414
+ }
415
+
280
416
  /**
281
417
  * @typedef {Object} FeatureInfoSession
282
418
  * @property {VcsEvent<void>} stopped
@@ -316,7 +452,7 @@ class FeatureInfo extends Collection {
316
452
  */
317
453
  this._clusterWindowId = null;
318
454
  /**
319
- * @type {VcsEvent<FeatureType|null>}
455
+ * @type {import("@vcmap/core").VcsEvent<FeatureType|null>}
320
456
  * @private
321
457
  */
322
458
  this._featureChanged = new VcsEvent();
@@ -345,6 +481,12 @@ class FeatureInfo extends Collection {
345
481
  * @private
346
482
  */
347
483
  this._selectedClusterFeatureId = null;
484
+
485
+ /**
486
+ * @type {Array<function():void>}
487
+ * @private
488
+ */
489
+ this._destroyBalloonClampedListener = () => {};
348
490
  /**
349
491
  * @type {Array<function():void>}
350
492
  * @private
@@ -370,24 +512,6 @@ class FeatureInfo extends Collection {
370
512
  ) {
371
513
  this._app.windowManager.remove(this._windowId);
372
514
  }
373
-
374
- if (
375
- this._clusterWindowId &&
376
- this._app.windowManager.has(this._clusterWindowId)
377
- ) {
378
- const { props } = this._app.windowManager.get(this._clusterWindowId);
379
- if (props.items.some((item) => item.group === layer.name)) {
380
- props.items = props.items.filter(
381
- (item) => item.group !== layer.name,
382
- );
383
- props.groups = props.groups.filter(
384
- (group) => group.name !== layer.name,
385
- );
386
- if (props.items.length === 0) {
387
- this._app.windowManager.remove(this._clusterWindowId);
388
- }
389
- }
390
- }
391
515
  }),
392
516
  this._app.windowManager.removed.addEventListener(({ id }) => {
393
517
  if (id === this._windowId) {
@@ -406,7 +530,7 @@ class FeatureInfo extends Collection {
406
530
  ];
407
531
  /**
408
532
  * A vector layer to render provided features on
409
- * @type {VectorLayer|null}
533
+ * @type {import("@vcmap/core").VectorLayer|null}
410
534
  * @private
411
535
  */
412
536
  this._scratchLayer = null;
@@ -420,7 +544,7 @@ class FeatureInfo extends Collection {
420
544
  /**
421
545
  * Emitted whenever a feature is selected or cleared.
422
546
  * Does not reflect cluster feature changes!
423
- * @type {VcsEvent<null|FeatureType>}
547
+ * @type {import("@vcmap/core").VcsEvent<null|FeatureType>}
424
548
  */
425
549
  get featureChanged() {
426
550
  return this._featureChanged;
@@ -442,7 +566,7 @@ class FeatureInfo extends Collection {
442
566
 
443
567
  /**
444
568
  * Emitted whenever a cluster feature is selected or cleared.
445
- * @type {VcsEvent<null|import("ol").Feature>}
569
+ * @type {import("@vcmap/core").VcsEvent<null|import("ol").Feature>}
446
570
  */
447
571
  get clusterFeatureChanged() {
448
572
  return this._clusterFeatureChanged;
@@ -495,23 +619,11 @@ class FeatureInfo extends Collection {
495
619
 
496
620
  /**
497
621
  * @param {FeatureType} feature
498
- * @returns {null|AbstractFeatureInfoView}
622
+ * @returns {null|import("./abstractFeatureInfoView.js").default}
499
623
  * @private
500
624
  */
501
625
  _getFeatureInfoViewForFeature(feature) {
502
- const layer = this._app.layers.getByKey(feature[vcsLayerName]);
503
- const name = layer?.properties?.featureInfo;
504
- if (!name) {
505
- getLogger().debug(
506
- `No view has been configured for layer '${layer?.name}'.`,
507
- );
508
- return null;
509
- }
510
- if (!this.hasKey(name)) {
511
- getLogger().warning(`No view with name '${name}' has been registered.`);
512
- return null;
513
- }
514
- return /** @type {AbstractFeatureInfoView} */ this.getByKey(name);
626
+ return getFeatureInfoViewForFeature(this._app, feature);
515
627
  }
516
628
 
517
629
  /**
@@ -524,7 +636,7 @@ class FeatureInfo extends Collection {
524
636
  * @param {FeatureType} feature
525
637
  * @param {import("ol/coordinate.js").Coordinate=} [position] - optional clicked position. If not given feature's center point is used to place balloons
526
638
  * @param {import("ol/coordinate.js").Coordinate=} [windowPosition] - optional clicked window position. If not given derived from position for balloons
527
- * @param {AbstractFeatureInfoView=} featureInfoView
639
+ * @param {import("./abstractFeatureInfoView.js").default=} featureInfoView
528
640
  * @returns {Promise<void>}
529
641
  */
530
642
  async selectFeature(feature, position, windowPosition, featureInfoView) {
@@ -558,6 +670,7 @@ class FeatureInfo extends Collection {
558
670
  // we need to clone the feature to avoid changing vcsLayerNameSymbol on the original feature
559
671
  const clonedFeature = feature.clone();
560
672
  clonedFeature.setId(feature.getId());
673
+ clonedFeature.set('olcs_allowPicking', true);
561
674
  this._scratchLayer.addFeatures([clonedFeature]);
562
675
  const featureId = clonedFeature.getId(); // make sure to grab ID after adding it to the layer
563
676
  this._scratchLayer.featureVisibility.highlight({
@@ -572,17 +685,17 @@ class FeatureInfo extends Collection {
572
685
  this._scratchLayer.featureVisibility.unHighlight([featureId]);
573
686
  } else if (layer.vectorClusterGroup) {
574
687
  this._ensureScratchLayer();
575
- const clone = feature.clone();
688
+ const clonedFeature = feature.clone();
576
689
  const featureId = feature.getId();
577
690
  this._scratchLayer.vectorProperties.setValuesForFeatures(
578
- layer.vectorProperties.getValuesForFeatures([clone]),
579
- [clone],
691
+ layer.vectorProperties.getValuesForFeatures([clonedFeature]),
692
+ [clonedFeature],
580
693
  );
581
- const eyeOffset = clone.get('olcs_eyeOffset') ?? [0, 0, 0];
694
+ const eyeOffset = clonedFeature.get('olcs_eyeOffset') ?? [0, 0, 0];
582
695
  eyeOffset[2] -= 10;
583
- clone.set('olcs_eyeOffset', eyeOffset);
584
- clone.setId(featureId);
585
- this._scratchLayer.addFeatures([clone]);
696
+ clonedFeature.set('olcs_eyeOffset', eyeOffset);
697
+ clonedFeature.setId(featureId);
698
+ this._scratchLayer.addFeatures([clonedFeature]);
586
699
  this._scratchLayer.featureVisibility.highlight({
587
700
  [featureId]: getHighlightStyle(
588
701
  feature,
@@ -592,7 +705,7 @@ class FeatureInfo extends Collection {
592
705
  ),
593
706
  });
594
707
  this._clearHighlightingCb = () =>
595
- this._scratchLayer.featureVisibility.unHighlight([clone]);
708
+ this._scratchLayer.featureVisibility.unHighlight([clonedFeature]);
596
709
  } else if (layer.featureVisibility) {
597
710
  const featureId = feature.getId();
598
711
  layer.featureVisibility.highlight({
@@ -607,14 +720,31 @@ class FeatureInfo extends Collection {
607
720
  layer.featureVisibility.unHighlight([featureId]);
608
721
  }
609
722
  this._windowId = usedFeatureInfoView.className; // use className for a type based position caching
723
+ const windowComponentOptions =
724
+ usedFeatureInfoView.getWindowComponentOptions(
725
+ this._app,
726
+ { feature, position, windowPosition },
727
+ layer,
728
+ );
729
+
730
+ let { props } = windowComponentOptions;
731
+ // check if Balloon should be Rendered Relative or ClampedTo Ground
732
+ if (usedFeatureInfoView instanceof BalloonFeatureInfoView) {
733
+ props = reactive(props);
734
+ if (
735
+ windowComponentOptions.props.heightReference !== HeightReference.NONE
736
+ ) {
737
+ this._destroyBalloonClampedListener = setupBalloonHeightListener(
738
+ this._app,
739
+ props,
740
+ );
741
+ }
742
+ }
610
743
  this._app.windowManager.add(
611
744
  {
612
745
  id: this._windowId,
613
- ...usedFeatureInfoView.getWindowComponentOptions(
614
- this._app,
615
- { feature, position, windowPosition },
616
- layer,
617
- ),
746
+ ...windowComponentOptions,
747
+ props,
618
748
  },
619
749
  vcsAppSymbol,
620
750
  );
@@ -644,8 +774,8 @@ class FeatureInfo extends Collection {
644
774
  const id = `cluster-at-${clusterFeature.getGeometry().getCoordinates().join('-')}`;
645
775
 
646
776
  this._ensureScratchLayer();
647
- const feature = clusterFeature.clone();
648
- feature.setId(id);
777
+ const clonedFeature = clusterFeature.clone();
778
+ clonedFeature.setId(id);
649
779
 
650
780
  clusterFeature[hidden] = true;
651
781
  clusterFeature.changed();
@@ -659,62 +789,30 @@ class FeatureInfo extends Collection {
659
789
  clusterFeature[vectorClusterGroupName],
660
790
  );
661
791
  this._scratchLayer.vectorProperties.setValuesForFeatures(
662
- clusterGroup.vectorProperties.getValuesForFeatures([feature]),
663
- [feature],
792
+ clusterGroup.vectorProperties.getValuesForFeatures([clonedFeature]),
793
+ [clonedFeature],
664
794
  );
665
- const clusterStyle = clusterGroup.style.createStyleFunction((layerName) =>
666
- this._app.layers.getByKey(layerName),
667
- )(clusterFeature, 1);
795
+ const clusterStyle = clusterGroup.styleFunction(clusterFeature, 1);
668
796
  const highlightStyle = getClusterHighlightStyle(
669
797
  clusterFeature,
670
798
  clusterGroup,
671
799
  clusterStyle,
672
800
  fillColor,
673
801
  );
674
- feature.setStyle(highlightStyle);
802
+ clonedFeature.setStyle(highlightStyle);
675
803
  } else if (clusterFeature[isProvidedClusterFeature]) {
676
- feature.setStyle(
804
+ clonedFeature.setStyle(
677
805
  fromCesiumColor(Color.fromCssColorString(fillColor)).style,
678
806
  );
679
807
  }
680
808
 
681
809
  if (this._app.maps.activeMap instanceof ObliqueMap) {
682
- feature.getGeometry()[alreadyTransformedToImage] = true;
810
+ clonedFeature.getGeometry()[alreadyTransformedToImage] = true;
683
811
  }
684
- this._scratchLayer.addFeatures([feature]);
812
+ this._scratchLayer.addFeatures([clonedFeature]);
685
813
 
686
814
  const features = clusterFeature.get('features');
687
- const groups = {};
688
- const items = features.map((f) => {
689
- const oFeature = f[originalFeatureSymbol] ?? f;
690
- const listItem = reactive({
691
- name: oFeature.getId(),
692
- title:
693
- oFeature.getAttributes()?.title ||
694
- oFeature.getAttributes()?.name ||
695
- oFeature.getId(),
696
- disabled: !this._getFeatureInfoViewForFeature(oFeature),
697
- selectionChanged: (value) => {
698
- if (value) {
699
- this.selectFeature(oFeature);
700
- } else {
701
- this.clearFeature();
702
- }
703
- },
704
- });
705
- const layerName = oFeature[vcsLayerName];
706
- if (layerName) {
707
- if (!groups[layerName]) {
708
- const title = this._app.layers.getByKey(layerName)?.properties?.title;
709
- groups[layerName] = {
710
- name: layerName,
711
- title: title || layerName,
712
- };
713
- }
714
- listItem.group = layerName;
715
- }
716
- return listItem;
717
- });
815
+ const { items, groups } = getGroupedFeatureList(this._app, features);
718
816
 
719
817
  this._clusterWindowId = id;
720
818
  this._app.windowManager.add(
@@ -723,7 +821,7 @@ class FeatureInfo extends Collection {
723
821
  component: ClusterFeatureComponent,
724
822
  props: reactive({
725
823
  items,
726
- groups: Object.values(groups),
824
+ groups,
727
825
  }),
728
826
  state: {
729
827
  headerTitle: 'featureInfo.cluster.headerTitle',
@@ -743,6 +841,7 @@ class FeatureInfo extends Collection {
743
841
  * @private
744
842
  */
745
843
  _clearInternal() {
844
+ this._destroyBalloonClampedListener();
746
845
  if (this._clearHighlightingCb) {
747
846
  this._clearHighlightingCb();
748
847
  this._clearHighlightingCb = null;
@@ -26,12 +26,6 @@ declare class MarkdownBalloonFeatureInfoView extends BalloonFeatureInfoView {
26
26
  * @type {string | string[]}
27
27
  */
28
28
  template: string | string[];
29
- /**
30
- * @param {Record<string, unknown>} attributes
31
- * @protected
32
- * @returns {string}
33
- */
34
- protected _renderTemplate(attributes: Record<string, unknown>): string;
35
29
  /**
36
30
  * Supports markdown templates (e.g. {{someProperty}}) and style expressions to derive markdown rendering
37
31
  * @param {import("./featureInfo.js").FeatureInfoEvent} featureInfo
@@ -1,5 +1,3 @@
1
- import { renderTemplate } from '@vcmap/core';
2
- import { parseAndSanitizeMarkdown } from '../components/form-output/markdownHelper.js';
3
1
  import BalloonFeatureInfoView from './balloonFeatureInfoView.js';
4
2
  import MarkdownBalloonComponent from './MarkdownBalloonComponent.vue';
5
3
 
@@ -39,15 +37,6 @@ class MarkdownBalloonFeatureInfoView extends BalloonFeatureInfoView {
39
37
  : options.template;
40
38
  }
41
39
 
42
- /**
43
- * @param {Record<string, unknown>} attributes
44
- * @protected
45
- * @returns {string}
46
- */
47
- _renderTemplate(attributes) {
48
- return renderTemplate(this.template, attributes);
49
- }
50
-
51
40
  /**
52
41
  * Supports markdown templates (e.g. {{someProperty}}) and style expressions to derive markdown rendering
53
42
  * @param {import("./featureInfo.js").FeatureInfoEvent} featureInfo
@@ -58,9 +47,11 @@ class MarkdownBalloonFeatureInfoView extends BalloonFeatureInfoView {
58
47
  const properties = super.getProperties(featureInfo, layer);
59
48
  return {
60
49
  ...properties,
61
- content: parseAndSanitizeMarkdown(
62
- this._renderTemplate({ ...properties, ...properties.attributes }),
63
- ),
50
+ template: this.template,
51
+ context: {
52
+ ...properties,
53
+ ...properties.attributes,
54
+ },
64
55
  };
65
56
  }
66
57
 
@@ -3,14 +3,14 @@ export type MarkdownFeatureInfoViewOptions = import("./abstractFeatureInfoView.j
3
3
  template: string | string[];
4
4
  };
5
5
  export type MarkdownFeatureInfoViewProps = import("./abstractFeatureInfoView.js").FeatureInfoProps & {
6
- html: string;
6
+ template: string;
7
7
  };
8
8
  /**
9
9
  * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoViewOptions & { template: string | string[] }} MarkdownFeatureInfoViewOptions
10
10
  * @property {string | string[]} template - a string or an array of strings which will be concatenated using \n
11
11
  */
12
12
  /**
13
- * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoProps & { html: string }} MarkdownFeatureInfoViewProps
13
+ * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoProps & { template: string }} MarkdownFeatureInfoViewProps
14
14
  */
15
15
  /**
16
16
  * @class
@@ -26,12 +26,6 @@ declare class MarkdownFeatureInfoView extends AbstractFeatureInfoView {
26
26
  * @type {string | string[]}
27
27
  */
28
28
  template: string | string[];
29
- /**
30
- * @param {Record<string, unknown>} attributes
31
- * @protected
32
- * @returns {string}
33
- */
34
- protected _renderTemplate(attributes: Record<string, unknown>): string;
35
29
  /**
36
30
  * Supports markdown templates (e.g. {{someProperty}}) and style expressions to derive a markdown rendering
37
31
  * @param {import("./featureInfo.js").FeatureInfoEvent} featureInfo
@@ -1,6 +1,5 @@
1
- import { renderTemplate } from '@vcmap/core';
2
1
  import AbstractFeatureInfoView from './abstractFeatureInfoView.js';
3
- import VcsMarkdown from '../components/form-output/VcsMarkdown.vue';
2
+ import VcsTemplateMarkdown from '../components/form-output/VcsTemplateMarkdown.vue';
4
3
 
5
4
  /**
6
5
  * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoViewOptions & { template: string | string[] }} MarkdownFeatureInfoViewOptions
@@ -8,7 +7,7 @@ import VcsMarkdown from '../components/form-output/VcsMarkdown.vue';
8
7
  */
9
8
 
10
9
  /**
11
- * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoProps & { html: string }} MarkdownFeatureInfoViewProps
10
+ * @typedef {import("./abstractFeatureInfoView.js").FeatureInfoProps & { template: string }} MarkdownFeatureInfoViewProps
12
11
  */
13
12
 
14
13
  /**
@@ -28,7 +27,7 @@ class MarkdownFeatureInfoView extends AbstractFeatureInfoView {
28
27
  * @param {MarkdownFeatureInfoViewOptions} options
29
28
  */
30
29
  constructor(options) {
31
- super(options, VcsMarkdown);
30
+ super(options, VcsTemplateMarkdown);
32
31
 
33
32
  /**
34
33
  * @type {string | string[]}
@@ -38,15 +37,6 @@ class MarkdownFeatureInfoView extends AbstractFeatureInfoView {
38
37
  : options.template;
39
38
  }
40
39
 
41
- /**
42
- * @param {Record<string, unknown>} attributes
43
- * @protected
44
- * @returns {string}
45
- */
46
- _renderTemplate(attributes) {
47
- return renderTemplate(this.template, attributes);
48
- }
49
-
50
40
  /**
51
41
  * Supports markdown templates (e.g. {{someProperty}}) and style expressions to derive a markdown rendering
52
42
  * @param {import("./featureInfo.js").FeatureInfoEvent} featureInfo
@@ -57,10 +47,11 @@ class MarkdownFeatureInfoView extends AbstractFeatureInfoView {
57
47
  const properties = super.getProperties(featureInfo, layer);
58
48
  return {
59
49
  ...properties,
60
- content: this._renderTemplate({
50
+ template: this.template,
51
+ context: {
61
52
  ...properties,
62
53
  ...properties.attributes,
63
- }),
54
+ },
64
55
  };
65
56
  }
66
57