@vcmap/core 5.0.0-rc.23 → 5.0.0-rc.25
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 +824 -200
- package/index.js +24 -10
- package/package.json +2 -2
- package/src/category/category.js +1 -1
- package/src/featureProvider/abstractFeatureProvider.js +1 -18
- package/src/interaction/eventHandler.js +14 -0
- package/src/layer/cesium/cesiumTilesetCesiumImpl.js +4 -19
- package/src/layer/cesium/clusterContext.js +18 -0
- package/src/layer/cesium/vectorCesiumImpl.js +17 -2
- package/src/layer/cesium/vectorContext.js +187 -11
- package/src/layer/cesium/vectorRasterTileCesiumImpl.js +0 -1
- package/src/layer/cesiumTilesetLayer.js +1 -63
- package/src/layer/czmlLayer.js +1 -1
- package/src/layer/dataSourceLayer.js +1 -53
- package/src/layer/featureLayer.js +42 -38
- package/src/layer/featureStoreLayer.js +0 -15
- package/src/layer/layer.js +6 -11
- package/src/layer/layerSymbols.js +2 -1
- package/src/layer/oblique/vectorObliqueImpl.js +6 -0
- package/src/layer/openStreetMapLayer.js +6 -0
- package/src/layer/openlayers/layerOpenlayersImpl.js +69 -4
- package/src/layer/openlayers/rasterLayerOpenlayersImpl.js +0 -80
- package/src/layer/rasterLayer.js +1 -1
- package/src/layer/vectorHelpers.js +0 -85
- package/src/layer/vectorLayer.js +1 -9
- package/src/layer/vectorProperties.js +150 -8
- package/src/layer/vectorTileLayer.js +0 -9
- package/src/map/baseOLMap.js +17 -0
- package/src/map/cesiumMap.js +46 -8
- package/src/map/vcsMap.js +23 -5
- package/src/style/arcStyle.js +316 -0
- package/src/style/arrowStyle.js +269 -0
- package/src/util/editor/createFeatureSession.js +3 -1
- package/src/util/editor/editFeaturesSession.js +315 -0
- package/src/util/editor/editGeometrySession.js +5 -1
- package/src/util/editor/editorHelpers.js +118 -14
- package/src/util/editor/editorSessionHelpers.js +12 -0
- package/src/util/editor/editorSymbols.js +6 -0
- package/src/util/editor/interactions/editFeaturesMouseOverInteraction.js +120 -0
- package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +1 -3
- package/src/util/editor/interactions/ensureHandlerSelectionInteraction.js +48 -0
- package/src/util/editor/interactions/mapInteractionController.js +5 -2
- package/src/util/editor/interactions/selectMultiFeatureInteraction.js +146 -0
- package/src/util/editor/interactions/translateVertexInteraction.js +2 -2
- package/src/util/editor/transformation/create2DHandlers.js +294 -0
- package/src/util/editor/transformation/create3DHandlers.js +575 -0
- package/src/util/editor/transformation/extrudeInteraction.js +91 -0
- package/src/util/editor/transformation/rotateInteraction.js +188 -0
- package/src/util/editor/transformation/scaleInteraction.js +185 -0
- package/src/util/editor/transformation/transformationHandler.js +168 -0
- package/src/util/editor/transformation/transformationTypes.js +83 -0
- package/src/util/editor/transformation/translateInteraction.js +209 -0
- package/src/util/featureconverter/arcToCesium.js +87 -0
- package/src/util/featureconverter/convert.js +7 -1
- package/src/util/featureconverter/extent3D.js +64 -1
- package/src/util/featureconverter/lineStringToCesium.js +103 -2
- package/src/util/featureconverter/pointHelpers.js +341 -0
- package/src/util/featureconverter/pointToCesium.js +27 -76
- package/src/util/geometryHelpers.js +11 -8
- package/src/util/mapCollection.js +30 -24
- package/src/util/math.js +99 -2
- package/tests/unit/helpers/cesiumHelpers.js +14 -4
- package/tests/unit/helpers/helpers.js +13 -0
- package/src/featureProvider/featureProviderHelpers.js +0 -50
- package/src/util/splitScreen.js +0 -233
|
@@ -108,10 +108,8 @@ class EditGeometryMouseOverInteraction extends AbstractInteraction {
|
|
|
108
108
|
if (this._lastFeature[vertexSymbol]) {
|
|
109
109
|
if (modifier === ModificationKeyType.SHIFT) {
|
|
110
110
|
this.cursorStyle.cursor = cursorMap.removeVertex;
|
|
111
|
-
} else if (modifier === ModificationKeyType.ALT) {
|
|
112
|
-
this.cursorStyle.cursor = cursorMap.translateVertex;
|
|
113
111
|
} else {
|
|
114
|
-
this.cursorStyle.cursor = cursorMap.
|
|
112
|
+
this.cursorStyle.cursor = cursorMap.translateVertex;
|
|
115
113
|
}
|
|
116
114
|
} else {
|
|
117
115
|
this.cursorStyle.cursor = cursorMap.select;
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import AbstractInteraction from '../../../interaction/abstractInteraction.js';
|
|
2
|
+
import { EventType } from '../../../interaction/interactionType.js';
|
|
3
|
+
import { handlerSymbol } from '../../../../index.js';
|
|
4
|
+
import CesiumMap from '../../../map/cesiumMap.js';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* This interaction ensure a potential handler is dragged in 3D when it is obscured by a transparent feature.
|
|
8
|
+
* It uses drillPick on MOVE if: the map is 3D, there is a feature at said position, there is a feature selected in
|
|
9
|
+
* the feature selection & the feature at the position is _not_ a handler
|
|
10
|
+
* @class
|
|
11
|
+
*/
|
|
12
|
+
class EnsureHandlerSelectionInteraction extends AbstractInteraction {
|
|
13
|
+
/**
|
|
14
|
+
* @param {import("@vcmap/core").SelectMultiFeatureInteraction} selectMultiFeatureInteraction
|
|
15
|
+
*/
|
|
16
|
+
constructor(selectMultiFeatureInteraction) {
|
|
17
|
+
super(EventType.DRAGSTART | EventType.MOVE);
|
|
18
|
+
/**
|
|
19
|
+
* @type {import("@vcmap/core").SelectMultiFeatureInteraction}
|
|
20
|
+
* @private
|
|
21
|
+
*/
|
|
22
|
+
this._featureSelection = selectMultiFeatureInteraction;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @param {InteractionEvent} event
|
|
27
|
+
* @returns {Promise<InteractionEvent>}
|
|
28
|
+
*/
|
|
29
|
+
async pipe(event) {
|
|
30
|
+
if (
|
|
31
|
+
event.feature &&
|
|
32
|
+
this._featureSelection.selectedFeatures.length > 0 &&
|
|
33
|
+
!event.feature[handlerSymbol] &&
|
|
34
|
+
event.map instanceof CesiumMap
|
|
35
|
+
) {
|
|
36
|
+
const handler = event.map.getScene().drillPick(event.windowPosition, undefined, 10, 10)
|
|
37
|
+
.find((p) => {
|
|
38
|
+
return p?.primitive?.olFeature?.[handlerSymbol];
|
|
39
|
+
});
|
|
40
|
+
if (handler) {
|
|
41
|
+
event.feature = handler.primitive.olFeature;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
return event;
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export default EnsureHandlerSelectionInteraction;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import DragPan from 'ol/interaction/DragPan.js';
|
|
2
2
|
import { EventType, ModificationKeyType } from '../../../interaction/interactionType.js';
|
|
3
|
-
import { vertexSymbol } from '../editorSymbols.js';
|
|
3
|
+
import { handlerSymbol, vertexSymbol } from '../editorSymbols.js';
|
|
4
4
|
import AbstractInteraction from '../../../interaction/abstractInteraction.js';
|
|
5
5
|
|
|
6
6
|
/**
|
|
@@ -75,7 +75,10 @@ class MapInteractionController extends AbstractInteraction {
|
|
|
75
75
|
*/
|
|
76
76
|
async pipe(event) {
|
|
77
77
|
this.reset();
|
|
78
|
-
if (
|
|
78
|
+
if (
|
|
79
|
+
event.feature &&
|
|
80
|
+
(event.feature[vertexSymbol] || event.feature[handlerSymbol])
|
|
81
|
+
) {
|
|
79
82
|
if (event.map.className === 'CesiumMap') {
|
|
80
83
|
this._clear = suspendCesiumMap(/** @type {import("@vcmap/core").CesiumMap} */ (event.map));
|
|
81
84
|
} else {
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
import AbstractInteraction from '../../../interaction/abstractInteraction.js';
|
|
2
|
+
import { EventType, ModificationKeyType } from '../../../interaction/interactionType.js';
|
|
3
|
+
import VcsEvent from '../../../vcsEvent.js';
|
|
4
|
+
import { vcsLayerName } from '../../../layer/layerSymbols.js';
|
|
5
|
+
import { isTiledFeature } from '../../../layer/featureStoreLayer.js';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Interaction to create a selection set from the given layer.
|
|
9
|
+
* Will use CTRL modifier key to add more features to the set.
|
|
10
|
+
* Clears the set if not clicking a feature
|
|
11
|
+
* Creates a new set when clicking a feature
|
|
12
|
+
* FeatureStore features will be converted to their dynamic state on selection.
|
|
13
|
+
* @class
|
|
14
|
+
* @extends {AbstractInteraction}
|
|
15
|
+
*/
|
|
16
|
+
class SelectMultiFeatureInteraction extends AbstractInteraction {
|
|
17
|
+
/**
|
|
18
|
+
* @param {import("@vcmap/core").VectorLayer} layer
|
|
19
|
+
*/
|
|
20
|
+
constructor(layer) {
|
|
21
|
+
super(EventType.CLICK, ModificationKeyType.NONE | ModificationKeyType.CTRL);
|
|
22
|
+
/**
|
|
23
|
+
* @type {import("@vcmap/core").VectorLayer|import("@vcmap/core").FeatureStoreLayer}
|
|
24
|
+
* @private
|
|
25
|
+
*/
|
|
26
|
+
this._layer = layer;
|
|
27
|
+
/**
|
|
28
|
+
* @type {Map<string|number, import("ol").Feature>}
|
|
29
|
+
* @private
|
|
30
|
+
*/
|
|
31
|
+
this._selectedFeatures = new Map();
|
|
32
|
+
/**
|
|
33
|
+
* @type {VcsEvent<Array<import("ol").Feature>>}
|
|
34
|
+
* @private
|
|
35
|
+
*/
|
|
36
|
+
this._featuresChanged = new VcsEvent();
|
|
37
|
+
this.setActive();
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Event raised when the feature selection changes. Will be called with an array of features or an empty array, when no feature is selected
|
|
42
|
+
* @type {VcsEvent<Array<import("ol").Feature>>}
|
|
43
|
+
* @readonly
|
|
44
|
+
*/
|
|
45
|
+
get featuresChanged() { return this._featuresChanged; }
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @returns {Array<import("ol").Feature>}
|
|
49
|
+
*/
|
|
50
|
+
get selectedFeatures() {
|
|
51
|
+
return [...this._selectedFeatures.values()];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* @param {string} featureId
|
|
56
|
+
* @returns {boolean}
|
|
57
|
+
*/
|
|
58
|
+
hasFeatureId(featureId) {
|
|
59
|
+
return this._selectedFeatures.has(featureId);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* @inheritDoc
|
|
64
|
+
* @param {InteractionEvent} event
|
|
65
|
+
* @returns {Promise<InteractionEvent>}
|
|
66
|
+
*/
|
|
67
|
+
async pipe(event) {
|
|
68
|
+
if (
|
|
69
|
+
event.feature &&
|
|
70
|
+
event.feature[vcsLayerName] === this._layer.name
|
|
71
|
+
) {
|
|
72
|
+
if (event.key & ModificationKeyType.CTRL) {
|
|
73
|
+
event.stopPropagation = true;
|
|
74
|
+
await this._modifySelectionSet(event.feature);
|
|
75
|
+
} else if (!this._selectedFeatures.has(event.feature.getId())) {
|
|
76
|
+
event.stopPropagation = true;
|
|
77
|
+
await this.setSelectionSet([event.feature]);
|
|
78
|
+
}
|
|
79
|
+
} else if (!(event.key & ModificationKeyType.CTRL)) {
|
|
80
|
+
this.clear();
|
|
81
|
+
}
|
|
82
|
+
return event;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* @param {Array<import("ol").Feature|import("@vcmap/cesium").Cesium3DTileFeature|import("@vcmap/cesium").Cesium3DTilePointFeature|import("@vcmap/cesium").Entity>} features
|
|
87
|
+
* @returns {Promise<void>}
|
|
88
|
+
*/
|
|
89
|
+
async setSelectionSet(features) {
|
|
90
|
+
this._selectedFeatures.clear();
|
|
91
|
+
const olFeatures = await Promise.all(features.map((f) => {
|
|
92
|
+
if (f[isTiledFeature]) {
|
|
93
|
+
return /** @type {import("@vcmap/core").FeatureStoreLayer} */ (this._layer)
|
|
94
|
+
.switchStaticFeatureToDynamic(f.getId());
|
|
95
|
+
}
|
|
96
|
+
return f;
|
|
97
|
+
}));
|
|
98
|
+
olFeatures.forEach(/** @param {import("ol").Feature} f */ (f) => {
|
|
99
|
+
this._selectedFeatures.set(f.getId(), f);
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
this._featuresChanged.raiseEvent(this.selectedFeatures);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* @param {import("ol").Feature|import("@vcmap/cesium").Cesium3DTileFeature|import("@vcmap/cesium").Cesium3DTilePointFeature|import("@vcmap/cesium").Entity} feature
|
|
107
|
+
* @returns {Promise<void>}
|
|
108
|
+
* @private
|
|
109
|
+
*/
|
|
110
|
+
async _modifySelectionSet(feature) {
|
|
111
|
+
const id = feature.getId();
|
|
112
|
+
if (this._selectedFeatures.has(id)) {
|
|
113
|
+
this._selectedFeatures.delete(id);
|
|
114
|
+
} else {
|
|
115
|
+
let olFeature = feature;
|
|
116
|
+
if (feature[isTiledFeature]) {
|
|
117
|
+
olFeature = await /** @type {import("@vcmap/core").FeatureStoreLayer} */ (this._layer)
|
|
118
|
+
.switchStaticFeatureToDynamic(id);
|
|
119
|
+
}
|
|
120
|
+
this._selectedFeatures.set(id, /** @type {import("ol").Feature} */ (olFeature));
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
this._featuresChanged.raiseEvent(this.selectedFeatures);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Clears the interaction, removing all features and calling the featureChange event with an empty array
|
|
128
|
+
*/
|
|
129
|
+
clear() {
|
|
130
|
+
if (this._selectedFeatures.size > 0) {
|
|
131
|
+
this._selectedFeatures.clear();
|
|
132
|
+
this._featuresChanged.raiseEvent([]);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* @inheritDoc
|
|
138
|
+
*/
|
|
139
|
+
destroy() {
|
|
140
|
+
this._selectedFeatures.clear();
|
|
141
|
+
this._featuresChanged.destroy();
|
|
142
|
+
super.destroy();
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
export default SelectMultiFeatureInteraction;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import AbstractInteraction from '../../../interaction/abstractInteraction.js';
|
|
2
|
-
import { EventType
|
|
2
|
+
import { EventType } from '../../../interaction/interactionType.js';
|
|
3
3
|
import { vertexSymbol } from '../editorSymbols.js';
|
|
4
4
|
import { emptyStyle } from '../../../style/styleHelpers.js';
|
|
5
5
|
import VcsEvent from '../../../vcsEvent.js';
|
|
@@ -12,7 +12,7 @@ import VcsEvent from '../../../vcsEvent.js';
|
|
|
12
12
|
*/
|
|
13
13
|
class TranslateVertexInteraction extends AbstractInteraction {
|
|
14
14
|
constructor() {
|
|
15
|
-
super(EventType.DRAGEVENTS
|
|
15
|
+
super(EventType.DRAGEVENTS);
|
|
16
16
|
/**
|
|
17
17
|
* @type {import("@vcmap/core").VcsEvent<Vertex>}
|
|
18
18
|
*/
|
|
@@ -0,0 +1,294 @@
|
|
|
1
|
+
import { Feature } from 'ol';
|
|
2
|
+
import { Circle, LineString, Point, Polygon } from 'ol/geom.js';
|
|
3
|
+
import { Fill, Icon, Stroke, Style } from 'ol/style.js';
|
|
4
|
+
import { Color } from '@vcmap/cesium';
|
|
5
|
+
import { unByKey } from 'ol/Observable.js';
|
|
6
|
+
import { handlerSymbol } from '../editorSymbols.js';
|
|
7
|
+
import { AXIS_AND_PLANES, greyedOutColor, is1DAxis, is2DAxis, TransformationMode } from './transformationTypes.js';
|
|
8
|
+
import { mercatorProjection } from '../../projection.js';
|
|
9
|
+
import Vector from '../../../layer/vectorLayer.js';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* @param {import("ol/coordinate").Coordinate} center
|
|
13
|
+
* @param {AXIS_AND_PLANES} axis
|
|
14
|
+
* @param {import("ol/extent").Extent} extent
|
|
15
|
+
* @returns {Array<import("ol/coordinate").Coordinate>}
|
|
16
|
+
*/
|
|
17
|
+
function createAxisPositions(center, axis, extent) {
|
|
18
|
+
if (axis === AXIS_AND_PLANES.X) {
|
|
19
|
+
return [
|
|
20
|
+
[extent[0], center[1], center[2]],
|
|
21
|
+
[center[0], center[1], center[2]],
|
|
22
|
+
[extent[2], center[1], center[2]],
|
|
23
|
+
];
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return [
|
|
27
|
+
[center[0], extent[1], center[2]],
|
|
28
|
+
[center[0], center[1], center[2]],
|
|
29
|
+
[center[0], extent[3], center[2]],
|
|
30
|
+
];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Creates a function to add axis to the scratch layer. To cleanup features created by this function, you can call
|
|
35
|
+
* it with AXIS_AND_PLANES.NONE.
|
|
36
|
+
* @param {import("@vcmap/core").VectorLayer} scratchLayer
|
|
37
|
+
* @param {import("ol/extent").Extent} [projectionExtent]
|
|
38
|
+
* @returns {function(AXIS_AND_PLANES, import("ol/coordinate").Coordinate):void}
|
|
39
|
+
*/
|
|
40
|
+
function createShowAxisFeatures(scratchLayer, projectionExtent) {
|
|
41
|
+
let featureIds;
|
|
42
|
+
const extent = projectionExtent ?? mercatorProjection.proj.getExtent();
|
|
43
|
+
|
|
44
|
+
return (axis, center) => {
|
|
45
|
+
if (featureIds) {
|
|
46
|
+
scratchLayer.removeFeaturesById(featureIds);
|
|
47
|
+
featureIds = null;
|
|
48
|
+
}
|
|
49
|
+
if (axis !== AXIS_AND_PLANES.NONE) {
|
|
50
|
+
const features = [];
|
|
51
|
+
if (axis === AXIS_AND_PLANES.X || axis === AXIS_AND_PLANES.XY) {
|
|
52
|
+
const feature = new Feature({
|
|
53
|
+
geometry: new LineString(createAxisPositions(center, AXIS_AND_PLANES.X, extent)),
|
|
54
|
+
});
|
|
55
|
+
feature.setStyle(new Style({
|
|
56
|
+
stroke: new Stroke({ color: Color.RED.withAlpha(0.5).toCssColorString(), width: 1 }),
|
|
57
|
+
}));
|
|
58
|
+
features.push(feature);
|
|
59
|
+
}
|
|
60
|
+
if (axis === AXIS_AND_PLANES.Y || axis === AXIS_AND_PLANES.XY) {
|
|
61
|
+
const feature = new Feature({
|
|
62
|
+
geometry: new LineString(createAxisPositions(center, AXIS_AND_PLANES.Y, extent)),
|
|
63
|
+
});
|
|
64
|
+
feature.setStyle(new Style({
|
|
65
|
+
stroke: new Stroke({ color: Color.GREEN.withAlpha(0.5).toCssColorString(), width: 1 }),
|
|
66
|
+
}));
|
|
67
|
+
features.push(feature);
|
|
68
|
+
}
|
|
69
|
+
features.forEach((f) => {
|
|
70
|
+
const geometry = f.getGeometry();
|
|
71
|
+
geometry[Vector.alreadyTransformedToImage] = true;
|
|
72
|
+
geometry[Vector.doNotTransform] = true;
|
|
73
|
+
});
|
|
74
|
+
featureIds = scratchLayer.addFeatures(features);
|
|
75
|
+
}
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* @param {AXIS_AND_PLANES} axis
|
|
81
|
+
* @param {TransformationMode} mode
|
|
82
|
+
* @param {string=} [colorOverride]
|
|
83
|
+
* @returns {Array<import("ol").Feature>}
|
|
84
|
+
*/
|
|
85
|
+
function createLineAxisFeatures(axis, mode, colorOverride) {
|
|
86
|
+
let color;
|
|
87
|
+
let coordinates;
|
|
88
|
+
let rotation = 0;
|
|
89
|
+
|
|
90
|
+
if (axis === AXIS_AND_PLANES.X) {
|
|
91
|
+
color = Color.RED.toCssColorString();
|
|
92
|
+
coordinates = [[0, 0, 0], [1, 0, 0]];
|
|
93
|
+
rotation = Math.PI / 2;
|
|
94
|
+
} else {
|
|
95
|
+
color = Color.GREEN.toCssColorString();
|
|
96
|
+
coordinates = [[0, 0, 0], [0, 1, 0]];
|
|
97
|
+
}
|
|
98
|
+
color = colorOverride ?? color;
|
|
99
|
+
let src;
|
|
100
|
+
if (mode === TransformationMode.TRANSLATE) {
|
|
101
|
+
src = '<svg height="13" width="13" xmlns="http://www.w3.org/2000/svg"><polygon points="0,13 13,13 6,0" style="fill:white;" /></svg>'; // an arrow svg
|
|
102
|
+
} else {
|
|
103
|
+
src = '<svg height="13" width="13" xmlns="http://www.w3.org/2000/svg"><polygon points="0,0 13,0 13,13 0,13" style="fill:white" /></svg>'; // a cube svg
|
|
104
|
+
}
|
|
105
|
+
src = `data:image/svg+xml,${encodeURIComponent(src)}`;
|
|
106
|
+
|
|
107
|
+
const features = [
|
|
108
|
+
new Feature({
|
|
109
|
+
geometry: new Point(coordinates[1].slice()),
|
|
110
|
+
axis,
|
|
111
|
+
}),
|
|
112
|
+
new Feature({
|
|
113
|
+
geometry: new LineString(coordinates),
|
|
114
|
+
axis,
|
|
115
|
+
}),
|
|
116
|
+
];
|
|
117
|
+
features[0].setStyle(new Style({
|
|
118
|
+
image: new Icon({
|
|
119
|
+
src,
|
|
120
|
+
anchor: [0.5, 1],
|
|
121
|
+
color,
|
|
122
|
+
rotation,
|
|
123
|
+
}),
|
|
124
|
+
}));
|
|
125
|
+
features[1].setStyle(new Style({ stroke: new Stroke({ color, width: 4 }) }));
|
|
126
|
+
|
|
127
|
+
return features;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @param {string=} [colorOverride]
|
|
132
|
+
* @returns {import("ol").Feature<Polygon>}
|
|
133
|
+
*/
|
|
134
|
+
function createPlaneFeature(colorOverride) {
|
|
135
|
+
const feature = new Feature({
|
|
136
|
+
geometry: new Polygon([[
|
|
137
|
+
[0.2, 0.2, 0],
|
|
138
|
+
[0.2, 0.4, 0],
|
|
139
|
+
[0.4, 0.4, 0],
|
|
140
|
+
[0.4, 0.2, 0],
|
|
141
|
+
[0.2, 0.2, 0],
|
|
142
|
+
]]),
|
|
143
|
+
axis: AXIS_AND_PLANES.XY,
|
|
144
|
+
});
|
|
145
|
+
const color = colorOverride ?? Color.BLUE.toCssColorString();
|
|
146
|
+
feature.setStyle(new Style({ fill: new Fill({ color }) }));
|
|
147
|
+
return feature;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Creates a function to add a shadow (greyed out clone of a handler) to the scratch layer. To cleanup features created by this function, you can call
|
|
152
|
+
* it with AXIS_AND_PLANES.NONE.
|
|
153
|
+
* @param {import("@vcmap/core").VectorLayer} scratchLayer
|
|
154
|
+
* @returns {function(AXIS_AND_PLANES, TransformationMode, import("ol/coordinate").Coordinate, number):void}
|
|
155
|
+
*/
|
|
156
|
+
function createShowShadowFeatures(scratchLayer) {
|
|
157
|
+
let featureIds;
|
|
158
|
+
const color = greyedOutColor.toCssColorString();
|
|
159
|
+
|
|
160
|
+
return (axis, mode, center, scale) => {
|
|
161
|
+
if (featureIds) {
|
|
162
|
+
scratchLayer.removeFeaturesById(featureIds);
|
|
163
|
+
featureIds = null;
|
|
164
|
+
}
|
|
165
|
+
if (axis !== AXIS_AND_PLANES.NONE) {
|
|
166
|
+
let features = [];
|
|
167
|
+
if (is1DAxis(axis)) {
|
|
168
|
+
features = createLineAxisFeatures(axis, mode, color);
|
|
169
|
+
} else if (is2DAxis(axis)) {
|
|
170
|
+
features = [createPlaneFeature(color)];
|
|
171
|
+
}
|
|
172
|
+
features.forEach((f) => {
|
|
173
|
+
f.getGeometry().applyTransform((input, output) => {
|
|
174
|
+
const inputLength = input.length;
|
|
175
|
+
for (let i = 0; i < inputLength; i += 3) {
|
|
176
|
+
output[i] = (input[i] * scale) + center[0];
|
|
177
|
+
output[i + 1] = (input[i + 1] * scale) + center[1];
|
|
178
|
+
output[i + 2] = 0;
|
|
179
|
+
}
|
|
180
|
+
return output;
|
|
181
|
+
});
|
|
182
|
+
});
|
|
183
|
+
featureIds = scratchLayer.addFeatures(features);
|
|
184
|
+
}
|
|
185
|
+
};
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* The function will create 2D handlers for the {@see OpenlayerMap} and the {@see ObliqueMap} depending on the provided mode.
|
|
190
|
+
* In most scenarios, handlers must not be created using this function, but using the startEditFeaturesSession or for lower
|
|
191
|
+
* level access the createTransformationHandler instead.
|
|
192
|
+
* @param {import("@vcmap/core").BaseOLMap} map
|
|
193
|
+
* @param {import("@vcmap/core").VectorLayer} scratchLayer
|
|
194
|
+
* @param {TransformationMode} mode
|
|
195
|
+
* @returns {Handlers}
|
|
196
|
+
*/
|
|
197
|
+
export default function create2DHandlers(map, scratchLayer, mode) {
|
|
198
|
+
let center = [0, 0, 0];
|
|
199
|
+
let scale = 1;
|
|
200
|
+
let features = [];
|
|
201
|
+
if (mode === TransformationMode.TRANSLATE || mode === TransformationMode.SCALE) {
|
|
202
|
+
features = [
|
|
203
|
+
...createLineAxisFeatures(AXIS_AND_PLANES.X, mode),
|
|
204
|
+
...createLineAxisFeatures(AXIS_AND_PLANES.Y, mode),
|
|
205
|
+
createPlaneFeature(),
|
|
206
|
+
];
|
|
207
|
+
} else if (mode === TransformationMode.ROTATE) {
|
|
208
|
+
features = [
|
|
209
|
+
new Feature({
|
|
210
|
+
geometry: new Circle([0, 0, 0], 0.5),
|
|
211
|
+
axis: AXIS_AND_PLANES.Z,
|
|
212
|
+
}),
|
|
213
|
+
];
|
|
214
|
+
features[0].setStyle(new Style({ stroke: new Stroke({ color: Color.BLUE.toCssColorString(), width: 2 }) }));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
features.forEach((f) => {
|
|
218
|
+
const geometry = f.getGeometry();
|
|
219
|
+
geometry[Vector.alreadyTransformedToImage] = true;
|
|
220
|
+
geometry[Vector.doNotTransform] = true;
|
|
221
|
+
f[handlerSymbol] = f.get('axis');
|
|
222
|
+
});
|
|
223
|
+
|
|
224
|
+
const postRenderListenerKey = map.olMap.on('postrender', () => {
|
|
225
|
+
if (!(center[0] === 0 && center[1] === 0 && center[2] === 0)) {
|
|
226
|
+
const res = map.getCurrentResolution(center) * 60;
|
|
227
|
+
const factor = res / scale;
|
|
228
|
+
if (factor !== 1) {
|
|
229
|
+
features.forEach((f) => {
|
|
230
|
+
f.getGeometry().applyTransform((input, output) => {
|
|
231
|
+
const inputLength = input.length;
|
|
232
|
+
for (let i = 0; i < inputLength; i += 3) {
|
|
233
|
+
output[i] = ((input[i] - center[0]) * factor) + center[0];
|
|
234
|
+
output[i + 1] = ((input[i + 1] - center[1]) * factor) + center[1];
|
|
235
|
+
output[i + 2] = 0;
|
|
236
|
+
}
|
|
237
|
+
return output;
|
|
238
|
+
});
|
|
239
|
+
});
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
scale = res;
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
let showAxis = AXIS_AND_PLANES.NONE;
|
|
247
|
+
const showAxisFeatures = createShowAxisFeatures(scratchLayer);
|
|
248
|
+
const showShadowFeatures = createShowShadowFeatures(scratchLayer);
|
|
249
|
+
|
|
250
|
+
let showing = false;
|
|
251
|
+
|
|
252
|
+
return {
|
|
253
|
+
get show() { return showing; },
|
|
254
|
+
set show(show) {
|
|
255
|
+
if (show !== showing) {
|
|
256
|
+
showing = show;
|
|
257
|
+
if (!show) {
|
|
258
|
+
scratchLayer.removeFeaturesById(features.map(f => f.getId()));
|
|
259
|
+
} else {
|
|
260
|
+
scratchLayer.addFeatures(features);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
get showAxis() { return showAxis; },
|
|
265
|
+
set showAxis(axis) {
|
|
266
|
+
showAxis = axis;
|
|
267
|
+
showAxisFeatures(axis, center);
|
|
268
|
+
showShadowFeatures(axis, mode, center, scale);
|
|
269
|
+
},
|
|
270
|
+
greyOutZ: false,
|
|
271
|
+
setCenter(newCenter) {
|
|
272
|
+
const dx = newCenter[0] - center[0];
|
|
273
|
+
const dy = newCenter[1] - center[1];
|
|
274
|
+
features.forEach((f) => {
|
|
275
|
+
f.getGeometry().applyTransform((input, output) => {
|
|
276
|
+
const inputLength = input.length;
|
|
277
|
+
for (let i = 0; i < inputLength; i += 3) {
|
|
278
|
+
output[i] = input[i] + dx;
|
|
279
|
+
output[i + 1] = input[i + 1] + dy;
|
|
280
|
+
output[i + 2] = input[i + 2];
|
|
281
|
+
}
|
|
282
|
+
return output;
|
|
283
|
+
});
|
|
284
|
+
});
|
|
285
|
+
center = newCenter.slice();
|
|
286
|
+
},
|
|
287
|
+
destroy() {
|
|
288
|
+
unByKey(postRenderListenerKey);
|
|
289
|
+
showAxisFeatures(AXIS_AND_PLANES.NONE, center);
|
|
290
|
+
showShadowFeatures(AXIS_AND_PLANES.NONE, mode, center, scale);
|
|
291
|
+
scratchLayer.removeFeaturesById(features.map(f => f.getId()).filter(id => id));
|
|
292
|
+
},
|
|
293
|
+
};
|
|
294
|
+
}
|