@vcmap/core 5.0.0-rc.27 → 5.0.0-rc.28
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/index.d.ts +273 -125
- package/index.js +6 -4
- package/package.json +1 -1
- package/src/category/category.js +16 -16
- package/src/category/categoryCollection.js +13 -13
- package/src/interaction/eventHandler.js +4 -4
- package/src/layer/featureVisibility.js +3 -4
- package/src/layer/globalHider.js +1 -1
- package/src/layer/layer.js +2 -1
- package/src/layer/vectorLayer.js +7 -0
- package/src/oblique/helpers.js +7 -9
- package/src/ol/feature.js +28 -0
- package/src/overrideClassRegistry.js +17 -17
- package/src/style/declarativeStyleItem.js +2 -3
- package/src/util/editor/editFeaturesSession.js +150 -166
- package/src/util/editor/editGeometrySession.js +69 -47
- package/src/util/editor/editorHelpers.js +3 -1
- package/src/util/editor/editorSessionHelpers.js +11 -3
- package/src/util/editor/editorSymbols.js +5 -0
- package/src/util/editor/interactions/editFeaturesMouseOverInteraction.js +15 -49
- package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +16 -33
- package/src/util/editor/interactions/ensureHandlerSelectionInteraction.js +5 -5
- package/src/util/editor/interactions/selectFeatureMouseOverInteraction.js +143 -0
- package/src/util/editor/interactions/selectMultiFeatureInteraction.js +17 -11
- package/src/util/editor/interactions/selectSingleFeatureInteraction.js +27 -8
- package/src/util/editor/interactions/translateVertexInteraction.js +2 -3
- package/src/util/editor/selectFeaturesSession.js +287 -0
- package/src/util/editor/transformation/transformationHandler.js +4 -9
- package/src/util/editor/transformation/transformationTypes.js +1 -0
- package/src/util/featureconverter/convert.js +1 -1
- package/src/util/indexedCollection.js +19 -3
- package/src/util/layerCollection.js +4 -2
- package/src/util/overrideCollection.js +20 -20
- package/src/vcsApp.js +107 -85
- package/src/vcsModule.js +129 -0
- package/src/{vcsAppContextHelpers.js → vcsModuleHelpers.js} +5 -5
- package/src/context.js +0 -89
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import AbstractInteraction from '../../../interaction/abstractInteraction.js';
|
|
2
|
+
import { EventType, ModificationKeyType } from '../../../interaction/interactionType.js';
|
|
3
|
+
import SelectMultiFeatureInteraction from './selectMultiFeatureInteraction.js';
|
|
4
|
+
import SelectSingleFeatureInteraction from './selectSingleFeatureInteraction.js';
|
|
5
|
+
import { cursorMap } from './editGeometryMouseOverInteraction.js';
|
|
6
|
+
import { vcsLayerName } from '../../../layer/layerSymbols.js';
|
|
7
|
+
import { mouseOverSymbol } from '../editorSymbols.js';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Enumeration of editor selection modes.
|
|
11
|
+
* @enum {string}
|
|
12
|
+
* @property {string} SINGLE
|
|
13
|
+
* @property {string} MULTI
|
|
14
|
+
*/
|
|
15
|
+
export const SelectionMode = {
|
|
16
|
+
SINGLE: 'single',
|
|
17
|
+
MULTI: 'multi',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* A class to handle mouse over effects on features for select sessions.
|
|
22
|
+
* @class
|
|
23
|
+
* @extends {AbstractInteraction}
|
|
24
|
+
*/
|
|
25
|
+
class SelectFeatureMouseOverInteraction extends AbstractInteraction {
|
|
26
|
+
/**
|
|
27
|
+
* @param {string} layerName Name of the layer for which the mouse over effect should accur.
|
|
28
|
+
* @param {SelectSingleFeatureInteraction | SelectMultiFeatureInteraction} selectFeatureInteraction The select feature interaction that handles the feature selection. Supports both, single and multi selection.
|
|
29
|
+
*/
|
|
30
|
+
constructor(layerName, selectFeatureInteraction) {
|
|
31
|
+
let modkeys;
|
|
32
|
+
let selectionMode;
|
|
33
|
+
if (selectFeatureInteraction instanceof SelectSingleFeatureInteraction) {
|
|
34
|
+
modkeys = ModificationKeyType.NONE;
|
|
35
|
+
selectionMode = SelectionMode.SINGLE;
|
|
36
|
+
} else if (selectFeatureInteraction instanceof SelectMultiFeatureInteraction) {
|
|
37
|
+
modkeys = ModificationKeyType.NONE | ModificationKeyType.CTRL;
|
|
38
|
+
selectionMode = SelectionMode.MULTI;
|
|
39
|
+
} else {
|
|
40
|
+
throw new Error('This interaction is not supported');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
super(EventType.MOVE, modkeys);
|
|
44
|
+
/** @type {SelectSingleFeatureInteraction | SelectMultiFeatureInteraction} */
|
|
45
|
+
this._selectFeatureInteraction = selectFeatureInteraction;
|
|
46
|
+
/** @type {SelectionMode} */
|
|
47
|
+
this.selectionMode = selectionMode;
|
|
48
|
+
/**
|
|
49
|
+
* The feature that is currently hovered and belongs to the layer with the layerName.
|
|
50
|
+
* @type {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|null}
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
this._currentFeature = null;
|
|
54
|
+
/**
|
|
55
|
+
* The layer name to react to
|
|
56
|
+
* @type {string}
|
|
57
|
+
*/
|
|
58
|
+
this.layerName = layerName;
|
|
59
|
+
/**
|
|
60
|
+
* @type {CSSStyleDeclaration}
|
|
61
|
+
* @private
|
|
62
|
+
*/
|
|
63
|
+
this.cursorStyle = document.body.style;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* @inheritDoc
|
|
68
|
+
* @param {InteractionEvent} event
|
|
69
|
+
* @returns {Promise<InteractionEvent>}
|
|
70
|
+
*/
|
|
71
|
+
async pipe(event) {
|
|
72
|
+
if (
|
|
73
|
+
event.feature &&
|
|
74
|
+
(event.feature[vcsLayerName] === this.layerName)
|
|
75
|
+
) {
|
|
76
|
+
this._currentFeature = /** @type {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature} */
|
|
77
|
+
(event.feature);
|
|
78
|
+
} else {
|
|
79
|
+
this._currentFeature = null;
|
|
80
|
+
}
|
|
81
|
+
this._evaluate(event.key);
|
|
82
|
+
return event;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @inheritDoc
|
|
87
|
+
* @param {ModificationKeyType} modifier
|
|
88
|
+
*/
|
|
89
|
+
modifierChanged(modifier) {
|
|
90
|
+
this._evaluate(modifier);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* @inheritDoc
|
|
95
|
+
* @param {(boolean|number)=} active
|
|
96
|
+
*/
|
|
97
|
+
setActive(active) {
|
|
98
|
+
super.setActive(active);
|
|
99
|
+
this.reset();
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Reset the cursorStyle to auto
|
|
104
|
+
*/
|
|
105
|
+
reset() {
|
|
106
|
+
if (this.cursorStyle && this.cursorStyle.cursor) {
|
|
107
|
+
this.cursorStyle.cursor = cursorMap.auto;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* @param {ModificationKeyType} modifier
|
|
113
|
+
* @private
|
|
114
|
+
*/
|
|
115
|
+
_evaluate(modifier) {
|
|
116
|
+
if (this._currentFeature) {
|
|
117
|
+
const isCtrlPressed = this.selectionMode === SelectionMode.MULTI && (modifier & ModificationKeyType.CTRL);
|
|
118
|
+
const isSelected = this._selectFeatureInteraction.hasFeatureId(this._currentFeature.getId());
|
|
119
|
+
|
|
120
|
+
if (isCtrlPressed) {
|
|
121
|
+
this.cursorStyle.cursor = isSelected ? cursorMap.removeFromSelection : cursorMap.addToSelection;
|
|
122
|
+
} else {
|
|
123
|
+
this.cursorStyle.cursor = cursorMap.select;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
this.cursorStyle[mouseOverSymbol] = this.id;
|
|
127
|
+
} else if (this.cursorStyle?.[mouseOverSymbol] === this.id) {
|
|
128
|
+
this.cursorStyle.cursor = cursorMap.auto;
|
|
129
|
+
delete this.cursorStyle[mouseOverSymbol];
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* @inheritDoc
|
|
135
|
+
*/
|
|
136
|
+
destroy() {
|
|
137
|
+
this.reset();
|
|
138
|
+
this.cursorStyle = null;
|
|
139
|
+
super.destroy();
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export default SelectFeatureMouseOverInteraction;
|
|
@@ -12,6 +12,7 @@ import { isTiledFeature } from '../../../layer/featureStoreLayer.js';
|
|
|
12
12
|
* FeatureStore features will be converted to their dynamic state on selection.
|
|
13
13
|
* @class
|
|
14
14
|
* @extends {AbstractInteraction}
|
|
15
|
+
* @implements {SelectFeatureInteraction}
|
|
15
16
|
*/
|
|
16
17
|
class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
17
18
|
/**
|
|
@@ -47,16 +48,17 @@ class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
|
47
48
|
/**
|
|
48
49
|
* @returns {Array<import("ol").Feature>}
|
|
49
50
|
*/
|
|
50
|
-
get
|
|
51
|
+
get selected() {
|
|
51
52
|
return [...this._selectedFeatures.values()];
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
/**
|
|
55
|
-
*
|
|
56
|
+
* Checks if a feature with a spicific id is selected.
|
|
57
|
+
* @param {string | number} id
|
|
56
58
|
* @returns {boolean}
|
|
57
59
|
*/
|
|
58
|
-
hasFeatureId(
|
|
59
|
-
return this._selectedFeatures.has(
|
|
60
|
+
hasFeatureId(id) {
|
|
61
|
+
return this._selectedFeatures.has(id);
|
|
60
62
|
}
|
|
61
63
|
|
|
62
64
|
/**
|
|
@@ -72,9 +74,12 @@ class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
|
72
74
|
if (event.key & ModificationKeyType.CTRL) {
|
|
73
75
|
event.stopPropagation = true;
|
|
74
76
|
await this._modifySelectionSet(event.feature);
|
|
75
|
-
} else if (
|
|
77
|
+
} else if (
|
|
78
|
+
!this._selectedFeatures.has(event.feature.getId()) ||
|
|
79
|
+
(this._selectedFeatures.has(event.feature.getId()) && this._selectedFeatures.size > 1)
|
|
80
|
+
) {
|
|
76
81
|
event.stopPropagation = true;
|
|
77
|
-
await this.
|
|
82
|
+
await this.setSelected([event.feature]);
|
|
78
83
|
}
|
|
79
84
|
} else if (!(event.key & ModificationKeyType.CTRL)) {
|
|
80
85
|
this.clear();
|
|
@@ -83,12 +88,13 @@ class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
/**
|
|
86
|
-
* @param {Array<import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity>} features
|
|
91
|
+
* @param {Array<import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity> | import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity} features
|
|
87
92
|
* @returns {Promise<void>}
|
|
88
93
|
*/
|
|
89
|
-
async
|
|
94
|
+
async setSelected(features) {
|
|
90
95
|
this._selectedFeatures.clear();
|
|
91
|
-
const
|
|
96
|
+
const featureArray = Array.isArray(features) ? features : [features];
|
|
97
|
+
const olFeatures = await Promise.all(featureArray.map((f) => {
|
|
92
98
|
if (f[isTiledFeature]) {
|
|
93
99
|
return /** @type {import("@vcmap/core").FeatureStoreLayer} */ (this._layer)
|
|
94
100
|
.switchStaticFeatureToDynamic(f.getId());
|
|
@@ -99,7 +105,7 @@ class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
|
99
105
|
this._selectedFeatures.set(f.getId(), f);
|
|
100
106
|
});
|
|
101
107
|
|
|
102
|
-
this._featuresChanged.raiseEvent(this.
|
|
108
|
+
this._featuresChanged.raiseEvent(this.selected);
|
|
103
109
|
}
|
|
104
110
|
|
|
105
111
|
/**
|
|
@@ -120,7 +126,7 @@ class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
|
120
126
|
this._selectedFeatures.set(id, /** @type {import("ol").Feature} */ (olFeature));
|
|
121
127
|
}
|
|
122
128
|
|
|
123
|
-
this._featuresChanged.raiseEvent(this.
|
|
129
|
+
this._featuresChanged.raiseEvent(this.selected);
|
|
124
130
|
}
|
|
125
131
|
|
|
126
132
|
/**
|
|
@@ -9,6 +9,7 @@ import { isTiledFeature } from '../../../layer/featureStoreLayer.js';
|
|
|
9
9
|
* Static FeatureStore features will be converted into their dynamic form
|
|
10
10
|
* @class
|
|
11
11
|
* @extends {AbstractInteraction}
|
|
12
|
+
* @implements {SelectFeatureInteraction}
|
|
12
13
|
*/
|
|
13
14
|
class SelectSingleFeatureInteraction extends AbstractInteraction {
|
|
14
15
|
/**
|
|
@@ -35,10 +36,10 @@ class SelectSingleFeatureInteraction extends AbstractInteraction {
|
|
|
35
36
|
}
|
|
36
37
|
|
|
37
38
|
/**
|
|
38
|
-
* @returns {import("ol").Feature
|
|
39
|
+
* @returns {Array<import("ol").Feature>}
|
|
39
40
|
*/
|
|
40
|
-
get
|
|
41
|
-
return this._selectedFeature;
|
|
41
|
+
get selected() {
|
|
42
|
+
return this._selectedFeature ? [this._selectedFeature] : [];
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
/**
|
|
@@ -53,7 +54,7 @@ class SelectSingleFeatureInteraction extends AbstractInteraction {
|
|
|
53
54
|
) {
|
|
54
55
|
if (!(this._selectedFeature && event.feature.getId() === this._selectedFeature.getId())) {
|
|
55
56
|
event.stopPropagation = true;
|
|
56
|
-
await this.
|
|
57
|
+
await this.setSelected(
|
|
57
58
|
/** @type {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature} */
|
|
58
59
|
(event.feature),
|
|
59
60
|
);
|
|
@@ -67,20 +68,29 @@ class SelectSingleFeatureInteraction extends AbstractInteraction {
|
|
|
67
68
|
/**
|
|
68
69
|
* Selects the given feature. if passed in a tiled feature store feature, it will be converted. Do not pass in uneditable features (feature which do not
|
|
69
70
|
* belong to the layer for which this interaction was created)
|
|
70
|
-
* @param {import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature} feature
|
|
71
|
+
* @param {Array<import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity> | import("ol").Feature|import("@vcmap-cesium/engine").Cesium3DTileFeature|import("@vcmap-cesium/engine").Cesium3DTilePointFeature|import("@vcmap-cesium/engine").Entity} feature
|
|
71
72
|
* @returns {Promise<void>}
|
|
72
73
|
*/
|
|
73
|
-
async
|
|
74
|
-
let olFeature = feature;
|
|
74
|
+
async setSelected(feature) {
|
|
75
|
+
let olFeature = Array.isArray(feature) ? feature[0] : feature;
|
|
75
76
|
if (feature[isTiledFeature]) {
|
|
76
77
|
olFeature = await /** @type {import("@vcmap/core").FeatureStoreLayer} */ (this._layer)
|
|
77
|
-
.switchStaticFeatureToDynamic(
|
|
78
|
+
.switchStaticFeatureToDynamic(olFeature.getId());
|
|
78
79
|
}
|
|
79
80
|
|
|
80
81
|
this._selectedFeature = /** @type {import("ol").Feature} */ (olFeature);
|
|
81
82
|
this.featureChanged.raiseEvent(this._selectedFeature);
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
/**
|
|
86
|
+
* Checks if a feature with a spicific id is selected.
|
|
87
|
+
* @param {string | number} id
|
|
88
|
+
* @returns {boolean}
|
|
89
|
+
*/
|
|
90
|
+
hasFeatureId(id) {
|
|
91
|
+
return this._selectedFeature?.getId() === id;
|
|
92
|
+
}
|
|
93
|
+
|
|
84
94
|
/**
|
|
85
95
|
* Clears the current selection, if there is one.
|
|
86
96
|
*/
|
|
@@ -90,6 +100,15 @@ class SelectSingleFeatureInteraction extends AbstractInteraction {
|
|
|
90
100
|
this.featureChanged.raiseEvent(null);
|
|
91
101
|
}
|
|
92
102
|
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* @inheritDoc
|
|
106
|
+
*/
|
|
107
|
+
destroy() {
|
|
108
|
+
this._selectedFeature = null;
|
|
109
|
+
this.featureChanged.destroy();
|
|
110
|
+
super.destroy();
|
|
111
|
+
}
|
|
93
112
|
}
|
|
94
113
|
|
|
95
114
|
export default SelectSingleFeatureInteraction;
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import AbstractInteraction from '../../../interaction/abstractInteraction.js';
|
|
2
2
|
import { EventType } from '../../../interaction/interactionType.js';
|
|
3
3
|
import { vertexSymbol } from '../editorSymbols.js';
|
|
4
|
-
import { emptyStyle } from '../../../style/styleHelpers.js';
|
|
5
4
|
import VcsEvent from '../../../vcsEvent.js';
|
|
6
5
|
|
|
7
6
|
/**
|
|
@@ -36,7 +35,7 @@ class TranslateVertexInteraction extends AbstractInteraction {
|
|
|
36
35
|
this.vertexChanged.raiseEvent(this._vertex);
|
|
37
36
|
|
|
38
37
|
if (event.type & EventType.DRAGEND) {
|
|
39
|
-
this._vertex.
|
|
38
|
+
this._vertex.unset('olcs_allowPicking');
|
|
40
39
|
this._vertex = null;
|
|
41
40
|
}
|
|
42
41
|
event.stopPropagation = true;
|
|
@@ -46,7 +45,7 @@ class TranslateVertexInteraction extends AbstractInteraction {
|
|
|
46
45
|
event.feature[vertexSymbol]
|
|
47
46
|
) {
|
|
48
47
|
this._vertex = /** @type {Vertex} */ (event.feature);
|
|
49
|
-
this._vertex.
|
|
48
|
+
this._vertex.set('olcs_allowPicking', false);
|
|
50
49
|
event.stopPropagation = true;
|
|
51
50
|
}
|
|
52
51
|
return event;
|
|
@@ -0,0 +1,287 @@
|
|
|
1
|
+
import { check } from '@vcsuite/check';
|
|
2
|
+
import { Circle, Fill, Style, Stroke } from 'ol/style.js';
|
|
3
|
+
import { createSync } from '../../layer/vectorSymbols.js';
|
|
4
|
+
import { SessionType, setupInteractionChain } from './editorSessionHelpers.js';
|
|
5
|
+
import SelectSingleFeatureInteraction from './interactions/selectSingleFeatureInteraction.js';
|
|
6
|
+
import SelectMultiFeatureInteraction from './interactions/selectMultiFeatureInteraction.js';
|
|
7
|
+
import SelectFeatureMouseOverInteraction, { SelectionMode } from './interactions/selectFeatureMouseOverInteraction.js';
|
|
8
|
+
import VcsEvent from '../../vcsEvent.js';
|
|
9
|
+
import ObliqueMap from '../../map/obliqueMap.js';
|
|
10
|
+
import { vcsLayerName } from '../../layer/layerSymbols.js';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* @typedef {Object} SelectionHighlightManager
|
|
14
|
+
* @property {Array<import("ol").Feature>} highlightedFeatures Currently highlighted features.
|
|
15
|
+
* @property {function(Array<import("ol").Feature>):void} update Sets the new features to be highlighted. All currently highlighted features that are not part of the new features are unhighlighted. Features that are not on the highlight layer are ignored.
|
|
16
|
+
* @property {function():void} destroy Stops all listeners, unhighlights all features.
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Creates the selection highlight manager. By calling update features can be highlighted.
|
|
21
|
+
* @param {import("@vcmap/core").VectorLayer} layer The layer of the features to be highlighted.
|
|
22
|
+
* @param {import("ol/style").Style} highlightStyle Highlight style for the highlighted features.
|
|
23
|
+
* @returns {SelectionHighlightManager} Object that manages highlighting and allowPicking property of features. Has own state for selected features.
|
|
24
|
+
*/
|
|
25
|
+
function createHighlightManager(layer, highlightStyle) {
|
|
26
|
+
const currentFeaturesMap = new Map();
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Sets the new features to be highlighted. All currently highlighted features that are not part of the new features are unhighlighted.
|
|
30
|
+
* @param {Array<import("ol").Feature>} newFeatures Features to be highlighted.
|
|
31
|
+
*/
|
|
32
|
+
const update = (newFeatures) => {
|
|
33
|
+
const newIds = new Set(newFeatures.map((f) => {
|
|
34
|
+
f[createSync] = true;
|
|
35
|
+
return f.getId();
|
|
36
|
+
}));
|
|
37
|
+
const idsToHighlight = [];
|
|
38
|
+
newIds.forEach((id) => {
|
|
39
|
+
if (!currentFeaturesMap.has(id)) {
|
|
40
|
+
idsToHighlight.push(id);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
const idsToUnHighlight = [];
|
|
45
|
+
currentFeaturesMap.forEach((feature, id) => {
|
|
46
|
+
if (!newIds.has(id)) {
|
|
47
|
+
idsToUnHighlight.push(id);
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
layer.featureVisibility.unHighlight(idsToUnHighlight);
|
|
52
|
+
layer.featureVisibility.highlight(Object.fromEntries(idsToHighlight.map(id => [id, highlightStyle])));
|
|
53
|
+
layer.getFeaturesById(idsToUnHighlight).forEach(feature => delete feature[createSync]);
|
|
54
|
+
|
|
55
|
+
currentFeaturesMap.clear();
|
|
56
|
+
newFeatures.forEach(feature => currentFeaturesMap.set(feature.getId(), feature));
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
get highlightedFeatures() {
|
|
61
|
+
return [...currentFeaturesMap.values()];
|
|
62
|
+
},
|
|
63
|
+
update(newFeatures) {
|
|
64
|
+
const featuresOnHighlightLayer = newFeatures.filter(feature => feature[vcsLayerName] === layer.name);
|
|
65
|
+
update(featuresOnHighlightLayer);
|
|
66
|
+
},
|
|
67
|
+
destroy() {
|
|
68
|
+
if (currentFeaturesMap.size > 0) {
|
|
69
|
+
const idsToUnHighlight = [...currentFeaturesMap.keys()];
|
|
70
|
+
layer.featureVisibility.unHighlight(idsToUnHighlight);
|
|
71
|
+
layer.getFeaturesById(idsToUnHighlight).forEach(feature => delete feature[createSync]);
|
|
72
|
+
currentFeaturesMap.clear();
|
|
73
|
+
}
|
|
74
|
+
},
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* @returns {import("ol/style").Style}
|
|
80
|
+
*/
|
|
81
|
+
export function getDefaultHighlightStyle() {
|
|
82
|
+
const fill = new Fill({ color: 'rgba(76,175,80,0.2)' });
|
|
83
|
+
const stroke = new Stroke({ color: '#4CAF50', width: 2 });
|
|
84
|
+
|
|
85
|
+
return new Style({
|
|
86
|
+
fill,
|
|
87
|
+
stroke,
|
|
88
|
+
image: new Circle({
|
|
89
|
+
fill,
|
|
90
|
+
stroke,
|
|
91
|
+
radius: 14,
|
|
92
|
+
}),
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* @typedef {EditorSession} SelectFeaturesSession
|
|
98
|
+
* @property {Array<import("ol").Feature>} currentFeatures
|
|
99
|
+
* @property {import("ol").Feature} firstFeature
|
|
100
|
+
* @property {VcsEvent<Array<import("ol").Feature>>} featuresChanged
|
|
101
|
+
* @property {SelectionMode} mode
|
|
102
|
+
* @property {function(SelectionMode):void} setMode Sets the selection mode. Raises modeChanged event, throws error when invalid selection mode input.
|
|
103
|
+
* @property {VcsEvent<SelectionMode>} modeChanged
|
|
104
|
+
* @property {function(Array<import("ol").Feature>): Promise<void>} setCurrentFeatures Promise that resolves when features are set.
|
|
105
|
+
* @property {function(): void} clearSelection Deselects all features.
|
|
106
|
+
*/
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {import("@vcmap/core").VcsApp} app
|
|
110
|
+
* @param {import("@vcmap/core").VectorLayer} layer
|
|
111
|
+
* @param {string=} [interactionId] id for registering mutliple exclusive interaction.
|
|
112
|
+
* @param {SelectionMode=} [initialMode=SelectionMode.MULTI]
|
|
113
|
+
* @param {import("ol/style").Style=} [highlightStyle]
|
|
114
|
+
* @returns {SelectFeaturesSession}
|
|
115
|
+
*/
|
|
116
|
+
function startSelectFeaturesSession(
|
|
117
|
+
app,
|
|
118
|
+
layer,
|
|
119
|
+
interactionId,
|
|
120
|
+
initialMode = SelectionMode.MULTI,
|
|
121
|
+
highlightStyle = getDefaultHighlightStyle(),
|
|
122
|
+
) {
|
|
123
|
+
/** @type {VcsEvent<void>} */
|
|
124
|
+
const stopped = new VcsEvent();
|
|
125
|
+
/** @type {VcsEvent<SelectionMode>} */
|
|
126
|
+
const modeChanged = new VcsEvent();
|
|
127
|
+
/** @type {VcsEvent<Array<import("ol").Feature>>} */
|
|
128
|
+
const featuresChanged = new VcsEvent();
|
|
129
|
+
/** @type {SelectSingleFeatureInteraction | SelectMultiFeatureInteraction | null} */
|
|
130
|
+
let currentSelectInteraction = null;
|
|
131
|
+
/** @type {SelectFeatureMouseOverInteraction} */
|
|
132
|
+
let mouseOverInteraction = null;
|
|
133
|
+
/** @type {SelectionMode} */
|
|
134
|
+
let currentSelectionMode = null;
|
|
135
|
+
/** @type {ObliqueMap|null} */
|
|
136
|
+
let obliqueMap = null;
|
|
137
|
+
|
|
138
|
+
const {
|
|
139
|
+
interactionChain,
|
|
140
|
+
removed: interactionRemoved,
|
|
141
|
+
destroy: destroyInteractionChain,
|
|
142
|
+
} = setupInteractionChain(app.maps.eventHandler, interactionId);
|
|
143
|
+
|
|
144
|
+
const highlightManager = createHighlightManager(
|
|
145
|
+
layer, highlightStyle,
|
|
146
|
+
);
|
|
147
|
+
|
|
148
|
+
let interactionListeners = [];
|
|
149
|
+
function destroyCurrentSelectInteraction() {
|
|
150
|
+
if (currentSelectInteraction) {
|
|
151
|
+
if (currentSelectInteraction) {
|
|
152
|
+
interactionChain.removeInteraction(currentSelectInteraction);
|
|
153
|
+
currentSelectInteraction.destroy();
|
|
154
|
+
currentSelectInteraction = null;
|
|
155
|
+
}
|
|
156
|
+
if (mouseOverInteraction) {
|
|
157
|
+
interactionChain.removeInteraction(mouseOverInteraction);
|
|
158
|
+
mouseOverInteraction.destroy();
|
|
159
|
+
mouseOverInteraction = null;
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
interactionListeners.forEach((cb) => { cb(); });
|
|
163
|
+
interactionListeners = [];
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Destroys current selection interaction and creates new one. Sets all highlighted features as selected. If previous mode was multi and new mode is single, only first feature is selected and highlighted.
|
|
168
|
+
* @param {SelectionMode} newMode Either single or multi selection mode.
|
|
169
|
+
* @returns {void}
|
|
170
|
+
*/
|
|
171
|
+
function createSelectInteraction(newMode) {
|
|
172
|
+
if (newMode === currentSelectionMode) {
|
|
173
|
+
return;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
destroyCurrentSelectInteraction();
|
|
177
|
+
if (newMode === SelectionMode.SINGLE) {
|
|
178
|
+
currentSelectInteraction = new SelectSingleFeatureInteraction(layer);
|
|
179
|
+
|
|
180
|
+
interactionListeners.push(
|
|
181
|
+
currentSelectInteraction.featureChanged.addEventListener((feature) => {
|
|
182
|
+
const featureArray = feature ? [feature] : [];
|
|
183
|
+
highlightManager.update(featureArray);
|
|
184
|
+
featuresChanged.raiseEvent(featureArray);
|
|
185
|
+
if (obliqueMap) {
|
|
186
|
+
obliqueMap.switchEnabled = !feature;
|
|
187
|
+
}
|
|
188
|
+
}),
|
|
189
|
+
);
|
|
190
|
+
} else if (newMode === SelectionMode.MULTI) {
|
|
191
|
+
currentSelectInteraction = new SelectMultiFeatureInteraction(layer);
|
|
192
|
+
interactionListeners.push(
|
|
193
|
+
currentSelectInteraction.featuresChanged.addEventListener((features) => {
|
|
194
|
+
highlightManager.update(features);
|
|
195
|
+
featuresChanged.raiseEvent(features);
|
|
196
|
+
if (obliqueMap) {
|
|
197
|
+
obliqueMap.switchEnabled = features.length === 0;
|
|
198
|
+
}
|
|
199
|
+
}),
|
|
200
|
+
);
|
|
201
|
+
}
|
|
202
|
+
const { highlightedFeatures } = highlightManager;
|
|
203
|
+
if (highlightedFeatures) {
|
|
204
|
+
// if single select interaction, only one features is set. This triggers the event listener which ensures that only this feature is highlighted.
|
|
205
|
+
currentSelectInteraction.setSelected(highlightedFeatures);
|
|
206
|
+
}
|
|
207
|
+
currentSelectionMode = newMode;
|
|
208
|
+
interactionChain.addInteraction(currentSelectInteraction);
|
|
209
|
+
mouseOverInteraction = new SelectFeatureMouseOverInteraction(
|
|
210
|
+
layer.name,
|
|
211
|
+
currentSelectInteraction,
|
|
212
|
+
);
|
|
213
|
+
interactionChain.addInteraction(mouseOverInteraction);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
createSelectInteraction(initialMode);
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* @type {function():void}
|
|
220
|
+
*/
|
|
221
|
+
let obliqueImageChangedListener = () => {};
|
|
222
|
+
const mapChanged = (map) => {
|
|
223
|
+
obliqueImageChangedListener();
|
|
224
|
+
if (map instanceof ObliqueMap) {
|
|
225
|
+
currentSelectInteraction.clear();
|
|
226
|
+
obliqueImageChangedListener = map.imageChanged.addEventListener(() => {
|
|
227
|
+
currentSelectInteraction.clear();
|
|
228
|
+
});
|
|
229
|
+
obliqueMap = map;
|
|
230
|
+
} else {
|
|
231
|
+
if (obliqueMap) {
|
|
232
|
+
currentSelectInteraction.clear();
|
|
233
|
+
}
|
|
234
|
+
obliqueMap = null;
|
|
235
|
+
obliqueImageChangedListener = () => {};
|
|
236
|
+
}
|
|
237
|
+
};
|
|
238
|
+
const mapChangedListener = app.maps.mapActivated.addEventListener(mapChanged);
|
|
239
|
+
mapChanged(app.maps.activeMap);
|
|
240
|
+
|
|
241
|
+
const stop = () => {
|
|
242
|
+
destroyCurrentSelectInteraction();
|
|
243
|
+
destroyInteractionChain();
|
|
244
|
+
highlightManager.destroy();
|
|
245
|
+
mapChangedListener();
|
|
246
|
+
obliqueImageChangedListener();
|
|
247
|
+
if (obliqueMap) {
|
|
248
|
+
obliqueMap.switchEnabled = true;
|
|
249
|
+
}
|
|
250
|
+
stopped.raiseEvent();
|
|
251
|
+
stopped.destroy();
|
|
252
|
+
modeChanged.destroy();
|
|
253
|
+
featuresChanged.destroy();
|
|
254
|
+
};
|
|
255
|
+
interactionRemoved.addEventListener(stop);
|
|
256
|
+
|
|
257
|
+
return {
|
|
258
|
+
type: SessionType.SELECT,
|
|
259
|
+
stopped,
|
|
260
|
+
stop,
|
|
261
|
+
setCurrentFeatures(features) {
|
|
262
|
+
return currentSelectInteraction.setSelected(features);
|
|
263
|
+
},
|
|
264
|
+
get currentFeatures() {
|
|
265
|
+
return currentSelectInteraction.selected;
|
|
266
|
+
},
|
|
267
|
+
get firstFeature() {
|
|
268
|
+
return currentSelectInteraction.selected[0] || null;
|
|
269
|
+
},
|
|
270
|
+
get mode() {
|
|
271
|
+
return currentSelectionMode;
|
|
272
|
+
},
|
|
273
|
+
setMode(newMode) {
|
|
274
|
+
check(newMode, Object.values(SelectionMode));
|
|
275
|
+
|
|
276
|
+
createSelectInteraction(newMode);
|
|
277
|
+
modeChanged.raiseEvent(currentSelectionMode);
|
|
278
|
+
},
|
|
279
|
+
modeChanged,
|
|
280
|
+
featuresChanged,
|
|
281
|
+
clearSelection() {
|
|
282
|
+
currentSelectInteraction.clear();
|
|
283
|
+
},
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
export default startSelectFeaturesSession;
|
|
@@ -83,7 +83,6 @@ function getCenterFromFeatures2D(features) {
|
|
|
83
83
|
* In most scenarios, this function must not be called directly and the startEditFeatureSession used instead.
|
|
84
84
|
* @param {import("@vcmap/core").VcsMap} map
|
|
85
85
|
* @param {import("@vcmap/core").VectorLayer} layer
|
|
86
|
-
* @param {import("@vcmap/core").SelectMultiFeatureInteraction} selectMultiFeatureInteraction
|
|
87
86
|
* @param {import("@vcmap/core").VectorLayer} scratchLayer
|
|
88
87
|
* @param {import("@vcmap/core").TransformationMode} mode
|
|
89
88
|
* @returns {TransformationHandler}
|
|
@@ -91,7 +90,6 @@ function getCenterFromFeatures2D(features) {
|
|
|
91
90
|
export default function createTransformationHandler(
|
|
92
91
|
map,
|
|
93
92
|
layer,
|
|
94
|
-
selectMultiFeatureInteraction,
|
|
95
93
|
scratchLayer,
|
|
96
94
|
mode,
|
|
97
95
|
) {
|
|
@@ -106,12 +104,11 @@ export default function createTransformationHandler(
|
|
|
106
104
|
let cesiumMap = null;
|
|
107
105
|
|
|
108
106
|
let cancelAsyncSetting = () => {};
|
|
109
|
-
const
|
|
107
|
+
const setFeatures = async (features) => {
|
|
110
108
|
cancelAsyncSetting();
|
|
111
|
-
const
|
|
112
|
-
const show = selectedFeatures.length > 0;
|
|
109
|
+
const show = features.length > 0;
|
|
113
110
|
if (show) {
|
|
114
|
-
const { center: newCenter, someClamped, someNoTerrain } = getCenterFromFeatures(
|
|
111
|
+
const { center: newCenter, someClamped, someNoTerrain } = getCenterFromFeatures(features);
|
|
115
112
|
center = newCenter;
|
|
116
113
|
if (!cesiumMap || !someNoTerrain) { // only set center sync, if updating will not change it too drastically (to avoid jumps)
|
|
117
114
|
handlerFeatures.show = true;
|
|
@@ -140,8 +137,6 @@ export default function createTransformationHandler(
|
|
|
140
137
|
handlerFeatures = create2DHandlers(map, scratchLayer, mode);
|
|
141
138
|
getCenterFromFeatures = getCenterFromFeatures2D;
|
|
142
139
|
}
|
|
143
|
-
handleFeaturesChanged();
|
|
144
|
-
const featuresChangedListener = selectMultiFeatureInteraction.featuresChanged.addEventListener(handleFeaturesChanged);
|
|
145
140
|
|
|
146
141
|
return {
|
|
147
142
|
get showing() { return handlerFeatures.show; },
|
|
@@ -158,9 +153,9 @@ export default function createTransformationHandler(
|
|
|
158
153
|
center[2] += dz;
|
|
159
154
|
handlerFeatures.setCenter(center);
|
|
160
155
|
},
|
|
156
|
+
setFeatures,
|
|
161
157
|
destroy() {
|
|
162
158
|
cancelAsyncSetting();
|
|
163
|
-
featuresChangedListener();
|
|
164
159
|
handlerFeatures.destroy();
|
|
165
160
|
scratchLayer.removeAllFeatures();
|
|
166
161
|
},
|
|
@@ -19,6 +19,7 @@ import { Color } from '@vcmap-cesium/engine';
|
|
|
19
19
|
* @property {import("ol/coordinate").Coordinate} center - readonly current center of the handler. this is a copy, not a reference
|
|
20
20
|
* @property {AXIS_AND_PLANES} showAxis - highlight the given axis
|
|
21
21
|
* @property {boolean} showing - readonly value indicating whether the handlers are showing (proxy for: features are selected)
|
|
22
|
+
* @property {function(Array<import("ol").Feature>):void} setFeatures - Sets transformation handle features. The handler is located in the center of the features.
|
|
22
23
|
* @property {function():void} destroy - destroy the handler and any resources created by it
|
|
23
24
|
*/
|
|
24
25
|
|
|
@@ -71,7 +71,7 @@ export function getStylesArray(style, feature, resolution = 1) {
|
|
|
71
71
|
|
|
72
72
|
/**
|
|
73
73
|
* function to convert a feature to an array of Cesium.Primitives given a style and default properties. the resulting primitives
|
|
74
|
-
* must be added to the
|
|
74
|
+
* must be added to the modules collections here
|
|
75
75
|
* @param {import("ol").Feature<import("ol/geom/Geometry").default>} feature
|
|
76
76
|
* @param {import("ol/style/Style").StyleLike} style
|
|
77
77
|
* @param {import("@vcmap/core").VectorProperties} vectorProperties
|