@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.
- package/README.md +11 -4
- package/build/build.js +0 -3
- package/build/buildHelpers.js +0 -1
- package/build/buildPreview.js +7 -0
- package/config/aerowest.config.json +13 -3
- package/config/base.config.json +89 -64
- package/config/codes.config.json +397 -0
- package/config/dev.config.json +169 -0
- package/config/graphFeatureInfo.config.json +100 -0
- package/config/www.config.json +1232 -0
- package/dist/assets/{cesium.eb5667.js → cesium.e67536.js} +0 -0
- package/dist/assets/cesium.js +1 -1
- package/dist/assets/core.ebf665.js +4 -0
- package/dist/assets/core.js +1 -1
- package/dist/assets/{index.4ccd4433.js → index.9b213929.js} +1 -1
- package/dist/assets/{ol.ef03b1.js → ol.8bbd50.js} +0 -0
- package/dist/assets/ol.js +1 -1
- package/dist/assets/ui.fdfe0d.css +1 -0
- package/dist/assets/ui.fdfe0d.js +68 -0
- package/dist/assets/ui.js +1 -1
- package/dist/assets/vue.0bb7c6.js +9 -0
- package/dist/assets/vue.js +2 -1
- package/dist/assets/{vuetify.401a29.css → vuetify.53300f.css} +1 -1
- package/dist/assets/{vuetify.401a29.js → vuetify.53300f.js} +71 -71
- package/dist/assets/vuetify.js +2 -2
- package/dist/index.html +1 -1
- package/index.js +36 -5
- package/lib/vue.js +1 -0
- package/map.config.json +15 -6
- package/package.json +6 -7
- package/plugins/@vcmap/create-link/fallbackCreateLink.vue +71 -0
- package/plugins/@vcmap/create-link/index.js +83 -0
- package/plugins/@vcmap/create-link/package.json +6 -0
- package/plugins/@vcmap/pluginExample/index.js +1 -1
- package/plugins/@vcmap/pluginExample/pluginExampleComponent.vue +20 -3
- package/plugins/@vcmap/project-selector/ProjectSelectorComponent.vue +1 -1
- package/plugins/@vcmap/project-selector/index.js +1 -1
- package/plugins/@vcmap/project-selector/package.json +1 -2
- package/plugins/@vcmap/theme-changer/ThemeChangerComponent.vue +1 -1
- package/plugins/@vcmap/theme-changer/index.js +1 -1
- package/plugins/@vcmap/theme-changer/package.json +1 -2
- package/plugins/categoryTest/Categories.vue +89 -1
- package/plugins/categoryTest/Category.vue +1 -1
- package/plugins/simple-graph/README.md +51 -0
- package/plugins/simple-graph/SimpleGraphComponent.vue +70 -0
- package/plugins/simple-graph/index.js +17 -0
- package/plugins/simple-graph/package.json +11 -0
- package/plugins/simple-graph/simpleGraphView.js +76 -0
- package/plugins/test/editor.vue +1 -1
- package/plugins/test/index.js +63 -2
- package/plugins/test/windowManagerExample.vue +1 -1
- package/src/actions/stateRefAction.js +2 -2
- package/src/actions/styleSelector.vue +1 -1
- package/src/application/Navbar.vue +13 -2
- package/src/application/VcsApp.vue +201 -92
- package/src/application/VcsMap.vue +1 -1
- package/src/application/VcsSettings.vue +1 -1
- package/src/application/vcsAppWrapper.vue +1 -0
- package/src/components/form-inputs-controls/VcsCheckbox.vue +13 -0
- package/src/components/form-inputs-controls/VcsColorPicker.vue +1 -1
- package/src/components/form-inputs-controls/VcsRadio.vue +123 -0
- package/src/components/form-output/VcsFormattedNumber.vue +1 -1
- package/src/components/lists/VcsActionList.vue +13 -4
- package/src/components/lists/VcsTreeview.vue +4 -4
- package/src/components/lists/VcsTreeviewLeaf.vue +9 -2
- package/src/components/lists/VcsTreeviewSearchbar.vue +1 -2
- package/src/components/tables/VcsTable.vue +245 -0
- package/src/contentTree/LayerTree.vue +1 -1
- package/src/contentTree/contentTreeCollection.js +4 -4
- package/src/contentTree/contentTreeItem.js +9 -9
- package/src/contentTree/groupContentTreeItem.js +1 -1
- package/src/contentTree/layerContentTreeItem.js +15 -1
- package/src/contentTree/layerGroupContentTreeItem.js +21 -1
- package/src/contentTree/nodeContentTreeItem.js +1 -1
- package/src/featureInfo/AddressBalloonComponent.vue +47 -0
- package/src/featureInfo/BalloonComponent.vue +138 -0
- package/src/featureInfo/abstractFeatureInfoView.js +313 -0
- package/src/featureInfo/addressBalloonFeatureInfoView.js +118 -0
- package/src/featureInfo/balloonFeatureInfoView.js +151 -0
- package/src/featureInfo/balloonHelper.js +132 -0
- package/src/featureInfo/featureInfo.js +455 -0
- package/src/featureInfo/featureInfoInteraction.js +42 -0
- package/src/featureInfo/iframeFeatureInfoView.js +95 -0
- package/src/featureInfo/tableFeatureInfoView.js +106 -0
- package/src/i18n/de.js +16 -0
- package/src/i18n/en.js +16 -0
- package/src/i18n/i18nCollection.js +17 -0
- package/src/manager/buttonManager.js +5 -5
- package/src/manager/categoryManager/ComponentsManager.vue +30 -0
- package/src/manager/categoryManager/categoryManager.js +500 -0
- package/src/manager/contextMenu/contextMenuComponent.vue +43 -0
- package/src/manager/contextMenu/contextMenuInteraction.js +42 -0
- package/src/manager/contextMenu/contextMenuManager.js +197 -0
- package/src/manager/navbarManager.js +8 -8
- package/src/manager/toolbox/ToolboxManager.vue +2 -2
- package/src/manager/toolbox/toolboxManager.js +7 -3
- package/src/manager/window/WindowComponent.vue +1 -1
- package/src/manager/window/WindowManager.vue +5 -3
- package/src/manager/window/windowManager.js +118 -14
- package/src/navigation/mapNavigation.vue +3 -5
- package/src/navigation/overviewMap.js +28 -5
- package/src/navigation/vcsCompass.vue +1 -1
- package/src/setup.js +0 -2
- package/src/state.js +256 -0
- package/src/styles/_theming.scss +0 -5
- package/src/uiConfig.js +79 -0
- package/src/vcsUiApp.js +210 -20
- package/src/vuePlugins/vuetify.js +14 -4
- package/config/berlin.config.json +0 -510
- package/dist/assets/core.216494.js +0 -4
- package/dist/assets/ui.99a1a7.css +0 -1
- package/dist/assets/ui.99a1a7.js +0 -70
- package/dist/assets/vue-composition-api.c5aca1.js +0 -14
- package/dist/assets/vue-composition-api.js +0 -2
- package/dist/assets/vue.762edd.js +0 -9
- 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;
|