@vcmap/core 5.0.0-rc.23 → 5.0.0-rc.24

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 (53) hide show
  1. package/index.d.ts +597 -98
  2. package/index.js +24 -9
  3. package/package.json +2 -2
  4. package/src/category/category.js +1 -1
  5. package/src/featureProvider/abstractFeatureProvider.js +1 -18
  6. package/src/interaction/eventHandler.js +14 -0
  7. package/src/layer/cesium/clusterContext.js +12 -0
  8. package/src/layer/cesium/vectorCesiumImpl.js +2 -2
  9. package/src/layer/cesium/vectorContext.js +115 -7
  10. package/src/layer/cesiumTilesetLayer.js +0 -14
  11. package/src/layer/czmlLayer.js +1 -1
  12. package/src/layer/dataSourceLayer.js +1 -53
  13. package/src/layer/featureLayer.js +0 -44
  14. package/src/layer/featureStoreLayer.js +0 -15
  15. package/src/layer/layer.js +0 -11
  16. package/src/layer/vectorHelpers.js +0 -85
  17. package/src/layer/vectorLayer.js +0 -9
  18. package/src/layer/vectorProperties.js +150 -8
  19. package/src/layer/vectorTileLayer.js +0 -9
  20. package/src/map/cesiumMap.js +26 -7
  21. package/src/style/arcStyle.js +316 -0
  22. package/src/style/arrowStyle.js +269 -0
  23. package/src/util/editor/createFeatureSession.js +3 -1
  24. package/src/util/editor/editFeaturesSession.js +315 -0
  25. package/src/util/editor/editGeometrySession.js +5 -1
  26. package/src/util/editor/editorHelpers.js +118 -14
  27. package/src/util/editor/editorSessionHelpers.js +12 -0
  28. package/src/util/editor/editorSymbols.js +6 -0
  29. package/src/util/editor/interactions/editFeaturesMouseOverInteraction.js +120 -0
  30. package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +1 -3
  31. package/src/util/editor/interactions/ensureHandlerSelectionInteraction.js +48 -0
  32. package/src/util/editor/interactions/mapInteractionController.js +5 -2
  33. package/src/util/editor/interactions/selectMultiFeatureInteraction.js +146 -0
  34. package/src/util/editor/interactions/translateVertexInteraction.js +2 -2
  35. package/src/util/editor/transformation/create2DHandlers.js +294 -0
  36. package/src/util/editor/transformation/create3DHandlers.js +575 -0
  37. package/src/util/editor/transformation/extrudeInteraction.js +91 -0
  38. package/src/util/editor/transformation/rotateInteraction.js +188 -0
  39. package/src/util/editor/transformation/scaleInteraction.js +185 -0
  40. package/src/util/editor/transformation/transformationHandler.js +168 -0
  41. package/src/util/editor/transformation/transformationTypes.js +83 -0
  42. package/src/util/editor/transformation/translateInteraction.js +209 -0
  43. package/src/util/featureconverter/arcToCesium.js +87 -0
  44. package/src/util/featureconverter/convert.js +7 -1
  45. package/src/util/featureconverter/extent3D.js +64 -1
  46. package/src/util/featureconverter/lineStringToCesium.js +103 -2
  47. package/src/util/featureconverter/pointHelpers.js +341 -0
  48. package/src/util/featureconverter/pointToCesium.js +27 -76
  49. package/src/util/geometryHelpers.js +11 -8
  50. package/src/util/math.js +99 -2
  51. package/tests/unit/helpers/cesiumHelpers.js +14 -4
  52. package/tests/unit/helpers/helpers.js +13 -0
  53. package/src/featureProvider/featureProviderHelpers.js +0 -50
@@ -0,0 +1,188 @@
1
+ import {
2
+ Cartesian2,
3
+ Plane,
4
+ Transforms,
5
+ } from '@vcmap/cesium';
6
+ import AbstractInteraction from '../../../interaction/abstractInteraction.js';
7
+ import { EventType } from '../../../interaction/interactionType.js';
8
+ import { handlerSymbol } from '../editorSymbols.js';
9
+ import {
10
+ getCartographicFromPlane,
11
+ } from '../editorHelpers.js';
12
+ import VcsEvent from '../../../vcsEvent.js';
13
+ import Projection from '../../projection.js';
14
+ import { cartographicToWgs84, mercatorToCartesian } from '../../math.js';
15
+ import { AXIS_AND_PLANES } from './transformationTypes.js';
16
+ import CesiumMap from '../../../map/cesiumMap.js';
17
+
18
+ /**
19
+ * @typedef {Object} RotationEvent
20
+ * @property {number} angle - in radians
21
+ * @property {AXIS_AND_PLANES} axis - the axis of rotation
22
+ */
23
+
24
+ /**
25
+ * @param {import("@vcmap/cesium").Cartesian2} start
26
+ * @param {import("@vcmap/cesium").Cartesian2} end
27
+ * @param {number} angle
28
+ * @returns {number}
29
+ */
30
+ function determineOrientation(start, end, angle) {
31
+ const orientation = (start.x * end.y) - (start.y * end.x);
32
+ return orientation > 0 ? angle : angle * -1;
33
+ }
34
+
35
+ /**
36
+ * @param {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):import("@vcmap/cesium").Cartesian2} getPosition
37
+ * @param {InteractionEvent} event
38
+ * @param {AXIS_AND_PLANES} axis
39
+ * @returns {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):RotationEvent}
40
+ */
41
+ function createGetRotationEvent(getPosition, event, axis) {
42
+ let currentPosition = getPosition(event.positionOrPixel, event.windowPosition);
43
+
44
+ return (coordinate, windowPosition) => {
45
+ const newPosition = getPosition(coordinate, windowPosition);
46
+ const angle = determineOrientation(
47
+ currentPosition,
48
+ newPosition,
49
+ Cartesian2.angleBetween(currentPosition, newPosition),
50
+ );
51
+ currentPosition = newPosition;
52
+ return { angle, axis };
53
+ };
54
+ }
55
+
56
+ /**
57
+ * A class to handle events on a {@see TransformationHandler}. Should be used with {@see TransformationHandler} created for mode TransformationMode.ROTATE.
58
+ * If the rings are dragged, the rotated event will be raised.
59
+ * @class
60
+ * @extends {AbstractInteraction}
61
+ */
62
+ class RotateInteraction extends AbstractInteraction {
63
+ /**
64
+ * @param {TransformationHandler} transformationHandler
65
+ */
66
+ constructor(transformationHandler) {
67
+ super(EventType.DRAGEVENTS);
68
+ /**
69
+ * @type {TransformationHandler}
70
+ * @private
71
+ */
72
+ this._transformationHandler = transformationHandler;
73
+ /**
74
+ * @type {VcsEvent<RotationEvent>}
75
+ * @private
76
+ */
77
+ this._rotated = new VcsEvent();
78
+ /**
79
+ * @type {null|function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):{angle: number, axis: AXIS_AND_PLANES}}
80
+ * @private
81
+ */
82
+ this._getRotationEvent = null;
83
+ }
84
+
85
+ /**
86
+ * The event raised, if the rings are dragged. Event is raised with the angle delta to the last event in radians.
87
+ * @type {VcsEvent<RotationEvent>}
88
+ * @readonly
89
+ */
90
+ get rotated() { return this._rotated; }
91
+
92
+ /**
93
+ * @param {InteractionEvent} event
94
+ * @returns {Promise<InteractionEvent>}
95
+ */
96
+ async pipe(event) {
97
+ if (this._getRotationEvent) {
98
+ this._rotated.raiseEvent(this._getRotationEvent(event.positionOrPixel, event.windowPosition));
99
+ if (event.type === EventType.DRAGEND) {
100
+ this._getRotationEvent = null;
101
+ }
102
+ } else if (
103
+ event.type === EventType.DRAGSTART &&
104
+ event?.feature?.[handlerSymbol]
105
+ ) {
106
+ const axis = event.feature[handlerSymbol];
107
+ if (axis !== AXIS_AND_PLANES.NONE) {
108
+ if (event.map instanceof CesiumMap) {
109
+ this._getRotationEvent = this._dragAlongPlane3D(axis, event);
110
+ } else {
111
+ this._getRotationEvent = this._dragAlongPlane2D(axis, event);
112
+ }
113
+ }
114
+ }
115
+ return event;
116
+ }
117
+
118
+ /**
119
+ * @param {AXIS_AND_PLANES} axis
120
+ * @param {InteractionEvent} event
121
+ * @returns {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):{angle: number, axis: AXIS_AND_PLANES}}
122
+ * @private
123
+ */
124
+ _dragAlongPlane3D(axis, event) {
125
+ const scene = /** @type {import("@vcmap/core").CesiumMap} */ (event.map).getScene();
126
+ const center = mercatorToCartesian(this._transformationHandler.center);
127
+ let plane;
128
+ if (axis === AXIS_AND_PLANES.X) {
129
+ plane = Plane.clone(Plane.ORIGIN_YZ_PLANE);
130
+ } else if (axis === AXIS_AND_PLANES.Y) {
131
+ plane = Plane.clone(Plane.ORIGIN_ZX_PLANE);
132
+ } else {
133
+ plane = Plane.clone(Plane.ORIGIN_XY_PLANE);
134
+ }
135
+ plane = Plane.transform(plane, Transforms.eastNorthUpToFixedFrame(center), plane);
136
+
137
+ return createGetRotationEvent(
138
+ (c, windowPosition) => {
139
+ const cartographic = getCartographicFromPlane(plane, scene.camera, windowPosition);
140
+ const centeredCoordinates = Projection.wgs84ToMercator(cartographicToWgs84(cartographic));
141
+ const { center: currentCenter } = this._transformationHandler;
142
+ centeredCoordinates[0] = currentCenter[0] - centeredCoordinates[0];
143
+ centeredCoordinates[1] = currentCenter[1] - centeredCoordinates[1];
144
+ centeredCoordinates[2] = currentCenter[2] - centeredCoordinates[2];
145
+ if (axis === AXIS_AND_PLANES.Z) {
146
+ return Cartesian2.fromArray(centeredCoordinates);
147
+ }
148
+ if (axis === AXIS_AND_PLANES.X) {
149
+ return new Cartesian2(centeredCoordinates[1], centeredCoordinates[2]);
150
+ }
151
+ return new Cartesian2(centeredCoordinates[0], centeredCoordinates[2]);
152
+ },
153
+ event,
154
+ axis,
155
+ );
156
+ }
157
+
158
+ /**
159
+ * @param {AXIS_AND_PLANES} axis
160
+ * @param {InteractionEvent} event
161
+ * @returns {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):{angle: number, axis: AXIS_AND_PLANES}}
162
+ * @private
163
+ */
164
+ _dragAlongPlane2D(axis, event) {
165
+ return createGetRotationEvent(
166
+ (c) => {
167
+ const centeredCoordinates = c.slice();
168
+ const { center } = this._transformationHandler;
169
+ centeredCoordinates[0] = center[0] - centeredCoordinates[0];
170
+ centeredCoordinates[1] = center[1] - centeredCoordinates[1];
171
+ centeredCoordinates[2] = center[2] - centeredCoordinates[2];
172
+ return Cartesian2.fromArray(centeredCoordinates);
173
+ },
174
+ event,
175
+ axis,
176
+ );
177
+ }
178
+
179
+ /**
180
+ * @inheritDoc
181
+ */
182
+ destroy() {
183
+ this._transformationHandler = null;
184
+ this._rotated.destroy();
185
+ }
186
+ }
187
+
188
+ export default RotateInteraction;
@@ -0,0 +1,185 @@
1
+ import {
2
+ Plane,
3
+ Transforms,
4
+ } from '@vcmap/cesium';
5
+ import AbstractInteraction from '../../../interaction/abstractInteraction.js';
6
+ import { EventType } from '../../../interaction/interactionType.js';
7
+ import { handlerSymbol } from '../editorSymbols.js';
8
+ import {
9
+ getCartographicFromPlane,
10
+ } from '../editorHelpers.js';
11
+ import VcsEvent from '../../../vcsEvent.js';
12
+ import Projection from '../../projection.js';
13
+ import { cartesian2DDistance, cartographicToWgs84, mercatorToCartesian } from '../../math.js';
14
+ import { AXIS_AND_PLANES } from './transformationTypes.js';
15
+ import CesiumMap from '../../../map/cesiumMap.js';
16
+
17
+ /**
18
+ * @param {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):import("ol/coordinate").Coordinate} getPosition
19
+ * @param {InteractionEvent} event
20
+ * @param {TransformationHandler} transformationHandler
21
+ * @param {AXIS_AND_PLANES} axis
22
+ * @returns {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):Array<number>}
23
+ */
24
+ function createGetScaledEvent(getPosition, event, transformationHandler, axis) {
25
+ const { center } = transformationHandler;
26
+ let flippedX = false;
27
+ let flippedY = false;
28
+
29
+ const getDistance = (coordinate, windowPosition) => {
30
+ const position = getPosition(coordinate, windowPosition);
31
+ const dx = position[0] - center[0];
32
+ const dy = position[1] - center[1];
33
+ let distance;
34
+ if (axis === AXIS_AND_PLANES.X) {
35
+ distance = Math.abs(dx);
36
+ } else if (axis === AXIS_AND_PLANES.Y) {
37
+ distance = Math.abs(dy);
38
+ } else {
39
+ distance = cartesian2DDistance(center, position);
40
+ }
41
+ return { distance, dx, dy };
42
+ };
43
+
44
+ const { distance: initialDistance } = getDistance(event.positionOrPixel, event.windowPosition);
45
+ let currentDistance = initialDistance;
46
+ return (coordinate, windowPosition) => {
47
+ const { distance, dx, dy } = getDistance(coordinate, windowPosition);
48
+
49
+ const distanceDelta = distance / currentDistance;
50
+ const currentFlippedX = dx < 0;
51
+ const currentFlippedY = dy < 0;
52
+ let sx = distanceDelta;
53
+ let sy = distanceDelta;
54
+ if (currentFlippedX !== flippedX) {
55
+ flippedX = currentFlippedX;
56
+ sx *= -1;
57
+ }
58
+
59
+ if (currentFlippedY !== flippedY) {
60
+ flippedY = currentFlippedY;
61
+ sy *= -1;
62
+ }
63
+
64
+ currentDistance = distance;
65
+ if (axis === AXIS_AND_PLANES.X) {
66
+ return [sx, 1, 1];
67
+ } else if (axis === AXIS_AND_PLANES.Y) {
68
+ return [1, sy, 1];
69
+ } else {
70
+ return [sx, sy, 1];
71
+ }
72
+ };
73
+ }
74
+
75
+ /**
76
+ * A class to handle events on a {@see TransformationHandler}. Should be used with {@see TransformationHandler} created for mode TransformationMode.SCALE..
77
+ * If the handlers are dragged, the scaled event will be raised.
78
+ * @class
79
+ * @extends {AbstractInteraction}
80
+ */
81
+ class ScaleInteraction extends AbstractInteraction {
82
+ /**
83
+ * @param {TransformationHandler} transformationHandler
84
+ */
85
+ constructor(transformationHandler) {
86
+ super(EventType.DRAGEVENTS);
87
+ /**
88
+ * @type {TransformationHandler}
89
+ */
90
+ this._transformationHandler = transformationHandler;
91
+ /**
92
+ * @type {VcsEvent<Array<number>>}
93
+ */
94
+ this._scaled = new VcsEvent();
95
+ /**
96
+ * @type {null|function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):Array<number>}
97
+ * @private
98
+ */
99
+ this._getScaleEvent = null;
100
+ }
101
+
102
+ /**
103
+ * Event raised if the handlers are dragged. The resulting array is of type [sx, sy, sz] where all numbers
104
+ * are considered to be deltas to the previous event (where 1 means no scaling).
105
+ * @type {VcsEvent<Array<number>>}
106
+ * @readonly
107
+ */
108
+ get scaled() { return this._scaled; }
109
+
110
+ /**
111
+ * @param {InteractionEvent} event
112
+ * @returns {Promise<InteractionEvent>}
113
+ */
114
+ async pipe(event) {
115
+ if (this._getScaleEvent) {
116
+ this._scaled.raiseEvent(this._getScaleEvent(event.positionOrPixel, event.windowPosition));
117
+ if (event.type === EventType.DRAGEND) {
118
+ this._getScaleEvent = null;
119
+ this._transformationHandler.showAxis = AXIS_AND_PLANES.NONE;
120
+ }
121
+ } else if (
122
+ event.type === EventType.DRAGSTART &&
123
+ event?.feature?.[handlerSymbol]
124
+ ) {
125
+ const axis = event.feature[handlerSymbol];
126
+ if (axis !== AXIS_AND_PLANES.NONE) {
127
+ this._transformationHandler.showAxis = axis;
128
+ if (event.map instanceof CesiumMap) {
129
+ this._getScaleEvent = this._dragAlongPlane3D(axis, event);
130
+ } else {
131
+ this._getScaleEvent = this._dragAlongPlane2D(axis, event);
132
+ }
133
+ }
134
+ }
135
+ return event;
136
+ }
137
+
138
+ /**
139
+ * @param {AXIS_AND_PLANES} axis
140
+ * @param {InteractionEvent} event
141
+ * @returns {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):Array<number>}
142
+ * @private
143
+ */
144
+ _dragAlongPlane3D(axis, event) {
145
+ const scene = /** @type {import("@vcmap/core").CesiumMap} */ (event.map).getScene();
146
+ const center = mercatorToCartesian(this._transformationHandler.center);
147
+ let plane = Plane.clone(Plane.ORIGIN_XY_PLANE);
148
+ plane = Plane.transform(plane, Transforms.eastNorthUpToFixedFrame(center), plane);
149
+
150
+ return createGetScaledEvent(
151
+ (c, windowPosition) => {
152
+ const cartographic = getCartographicFromPlane(plane, scene.camera, windowPosition);
153
+ return Projection.wgs84ToMercator(cartographicToWgs84(cartographic));
154
+ },
155
+ event,
156
+ this._transformationHandler,
157
+ axis,
158
+ );
159
+ }
160
+
161
+ /**
162
+ * @param {AXIS_AND_PLANES} axis
163
+ * @param {InteractionEvent} event
164
+ * @returns {function(import("ol/coordinate").Coordinate, import("@vcmap/cesium").Cartesian2):Array<number>}
165
+ * @private
166
+ */
167
+ _dragAlongPlane2D(axis, event) {
168
+ return createGetScaledEvent(
169
+ c => c.slice(),
170
+ event,
171
+ this._transformationHandler,
172
+ axis,
173
+ );
174
+ }
175
+
176
+ /**
177
+ * @inheritDoc
178
+ */
179
+ destroy() {
180
+ this._transformationHandler = null;
181
+ this._scaled.destroy();
182
+ }
183
+ }
184
+
185
+ export default ScaleInteraction;
@@ -0,0 +1,168 @@
1
+ import { HeightReference } from '@vcmap/cesium';
2
+ import {
3
+ createEmpty as createEmptyExtent,
4
+ extend as extendExtent,
5
+ getCenter as getExtentCenter,
6
+ } from 'ol/extent.js';
7
+ import Extent3D from '../../featureconverter/extent3D.js';
8
+ import CesiumMap from '../../../map/cesiumMap.js';
9
+ import BaseOLMap from '../../../map/baseOLMap.js';
10
+ import create3DHandlers from './create3DHandlers.js';
11
+ import create2DHandlers from './create2DHandlers.js';
12
+ import { obliqueGeometry } from '../../../layer/vectorSymbols.js';
13
+
14
+ /**
15
+ * @typedef {Object} FeatureCenterInfo
16
+ * @property {import("ol/coordinate").Coordinate} center
17
+ * @property {boolean} someClamped
18
+ * @property {boolean} someNoTerrain
19
+ * @private
20
+ */
21
+
22
+ /**
23
+ * @param {import("@vcmap/core").VectorLayer} layer
24
+ * @param {Array<import("ol").Feature>} features
25
+ * @returns {FeatureCenterInfo}
26
+ */
27
+ function getCenterFromFeatures3D(layer, features) {
28
+ const extent3D = new Extent3D();
29
+ let someClamped = false;
30
+ let someNoTerrain = false;
31
+ const layerIsClamped = layer.vectorProperties.altitudeMode === HeightReference.CLAMP_TO_GROUND;
32
+
33
+ features.forEach((f) => {
34
+ const geometry = f.getGeometry();
35
+ extent3D.extendWithGeometry(geometry);
36
+ if (!someNoTerrain) {
37
+ const firstCoordinates = /** @type {import("ol/geom").SimpleGeometry} */ (geometry).getFirstCoordinate();
38
+ if (!firstCoordinates[2]) {
39
+ someNoTerrain = true;
40
+ }
41
+ }
42
+
43
+ if (!someClamped) {
44
+ const altitudeMode = f.get('olcs_altitudeMode');
45
+ someClamped = altitudeMode === 'clampToGround' || (!altitudeMode && layerIsClamped);
46
+ }
47
+ });
48
+ const center = extent3D.getCenter();
49
+ return {
50
+ center,
51
+ someClamped,
52
+ someNoTerrain,
53
+ };
54
+ }
55
+
56
+ /**
57
+ * @param {Array<import("ol").Feature>} features
58
+ * @returns {FeatureCenterInfo}
59
+ */
60
+ function getCenterFromFeatures2D(features) {
61
+ const extent = createEmptyExtent();
62
+
63
+ features.forEach((f) => {
64
+ const geometry = f[obliqueGeometry] ?? f.getGeometry();
65
+ extendExtent(extent, geometry.getExtent());
66
+ });
67
+
68
+ return {
69
+ center: [...getExtentCenter(extent), 0],
70
+ someClamped: false,
71
+ someNoTerrain: false,
72
+ };
73
+ }
74
+
75
+ /**
76
+ * The transformation handler is a set of handlers used for transformation interactions. these handlers are centered at the
77
+ * origin of the currently selected features and are rendered depending on a) the current mode and b) the current selections
78
+ * sets capabilities. if one or more selected features are clamp to ground, no Z manipulations will be available and
79
+ * they will be greyed out. updates to the selection set are handled by the handler. updates to the features (for instance
80
+ * setting the olcs_altitudeMode on a currently selected feature) is currently not handled.
81
+ * transformation handlers are only valid for the currently active map (and oblique image).
82
+ * it is up to the creator to re-create them as needed (map change, image change, external geometry or property change to a selected feature).
83
+ * In most scenarios, this function must not be called directly and the startEditFeatureSession used instead.
84
+ * @param {import("@vcmap/core").VcsMap} map
85
+ * @param {import("@vcmap/core").VectorLayer} layer
86
+ * @param {import("@vcmap/core").SelectMultiFeatureInteraction} selectMultiFeatureInteraction
87
+ * @param {import("@vcmap/core").VectorLayer} scratchLayer
88
+ * @param {import("@vcmap/core").TransformationMode} mode
89
+ * @returns {TransformationHandler}
90
+ */
91
+ export default function createTransformationHandler(
92
+ map,
93
+ layer,
94
+ selectMultiFeatureInteraction,
95
+ scratchLayer,
96
+ mode,
97
+ ) {
98
+ /** @type {Handlers} */
99
+ let handlerFeatures;
100
+ /** @type {import("ol/coordinate").Coordinate} */
101
+ let center = [0, 0, 0];
102
+
103
+ /** @type {function(Array<import("ol").Feature>):FeatureCenterInfo} */
104
+ let getCenterFromFeatures;
105
+ /** @type {import("@vcmap/core").CesiumMap|null} */
106
+ let cesiumMap = null;
107
+
108
+ let cancelAsyncSetting = () => {};
109
+ const handleFeaturesChanged = async () => {
110
+ cancelAsyncSetting();
111
+ const { selectedFeatures } = selectMultiFeatureInteraction;
112
+ const show = selectedFeatures.length > 0;
113
+ if (show) {
114
+ const { center: newCenter, someClamped, someNoTerrain } = getCenterFromFeatures(selectedFeatures);
115
+ center = newCenter;
116
+ if (!cesiumMap || !someNoTerrain) { // only set center sync, if updating will not change it too drastically (to avoid jumps)
117
+ handlerFeatures.show = true;
118
+ handlerFeatures.setCenter(center);
119
+ }
120
+ handlerFeatures.greyOutZ = someClamped;
121
+ if (cesiumMap && (someClamped || someNoTerrain)) {
122
+ let cancel = false;
123
+ cancelAsyncSetting = () => { cancel = true; };
124
+ await cesiumMap.getHeightFromTerrain([center]);
125
+ if (!cancel) {
126
+ handlerFeatures.show = true;
127
+ handlerFeatures.setCenter(center);
128
+ }
129
+ }
130
+ } else {
131
+ handlerFeatures.show = false;
132
+ }
133
+ };
134
+
135
+ if (map instanceof CesiumMap) {
136
+ handlerFeatures = create3DHandlers(map, mode);
137
+ getCenterFromFeatures = getCenterFromFeatures3D.bind(null, layer);
138
+ cesiumMap = map;
139
+ } else if (map instanceof BaseOLMap) {
140
+ handlerFeatures = create2DHandlers(map, scratchLayer, mode);
141
+ getCenterFromFeatures = getCenterFromFeatures2D;
142
+ }
143
+ handleFeaturesChanged();
144
+ const featuresChangedListener = selectMultiFeatureInteraction.featuresChanged.addEventListener(handleFeaturesChanged);
145
+
146
+ return {
147
+ get showing() { return handlerFeatures.show; },
148
+ get center() { return center.slice(); },
149
+ get showAxis() {
150
+ return handlerFeatures.showAxis;
151
+ },
152
+ set showAxis(axis) {
153
+ handlerFeatures.showAxis = axis;
154
+ },
155
+ translate(dx, dy, dz) {
156
+ center[0] += dx;
157
+ center[1] += dy;
158
+ center[2] += dz;
159
+ handlerFeatures.setCenter(center);
160
+ },
161
+ destroy() {
162
+ cancelAsyncSetting();
163
+ featuresChangedListener();
164
+ handlerFeatures.destroy();
165
+ scratchLayer.removeAllFeatures();
166
+ },
167
+ };
168
+ }
@@ -0,0 +1,83 @@
1
+ import { Color } from '@vcmap/cesium';
2
+
3
+ /**
4
+ * Handlers are map specific transformation handlers wich enable the use of the transformation interactions.
5
+ * There visualization is {@see TransformationMode} specific. It is not adviced to create these handlers yourself,
6
+ * use {@see createTransformationHandler} instead.
7
+ * @typedef {Object} Handlers
8
+ * @property {boolean} show - whether to show or hide all handlers
9
+ * @property {function(import("ol/coordinate").Coordinate):void} setCenter - update the center of the handlers
10
+ * @property {AXIS_AND_PLANES} showAxis - highlight the given axis
11
+ * @property {boolean} greyOutZ - display Z axis handlers in grey and do not allow them to be picked
12
+ * @property {function():void} destroy - destroy the handlers, removing any resources created by create2DHandlers or create3DHandlers
13
+ */
14
+
15
+ /**
16
+ * This interface provides an abstraction from the other {@see Handlers} interface.
17
+ * @typedef {Object} TransformationHandler
18
+ * @property {function(number, number, number):void} translate - translate the center of the underlying handlers
19
+ * @property {import("ol/coordinate").Coordinate} center - readonly current center of the handler. this is a copy, not a reference
20
+ * @property {AXIS_AND_PLANES} showAxis - highlight the given axis
21
+ * @property {boolean} showing - readonly value indicating whether the handlers are showing (proxy for: features are selected)
22
+ * @property {function():void} destroy - destroy the handler and any resources created by it
23
+ */
24
+
25
+ /**
26
+ * @enum {string}
27
+ * @property {string} X
28
+ * @property {string} Y
29
+ * @property {string} Z
30
+ * @property {string} XY
31
+ * @property {string} XZ
32
+ * @property {string} YZ
33
+ * @property {string} NONE
34
+ */
35
+ export const AXIS_AND_PLANES = {
36
+ X: 'X',
37
+ Y: 'Y',
38
+ Z: 'Z',
39
+ XY: 'XY',
40
+ XZ: 'XZ',
41
+ YZ: 'YZ',
42
+ NONE: 'NONE',
43
+ };
44
+
45
+ /**
46
+ * @enum {string}
47
+ * @property {string} TRANSLATE
48
+ * @property {string} ROTATE
49
+ * @property {string} SCALE
50
+ * @property {string} EXTRUDE
51
+ */
52
+ export const TransformationMode = {
53
+ TRANSLATE: 'translate',
54
+ ROTATE: 'rotate',
55
+ SCALE: 'scale',
56
+ EXTRUDE: 'extrude',
57
+ };
58
+
59
+ /**
60
+ * @const
61
+ * @type {import("@vcmap/cesium").Color}
62
+ */
63
+ export const greyedOutColor = Color.GRAY.withAlpha(0.5);
64
+
65
+ /**
66
+ * @param {AXIS_AND_PLANES} axis
67
+ * @returns {boolean}
68
+ */
69
+ export function is1DAxis(axis) {
70
+ return axis === AXIS_AND_PLANES.X ||
71
+ axis === AXIS_AND_PLANES.Y ||
72
+ axis === AXIS_AND_PLANES.Z;
73
+ }
74
+
75
+ /**
76
+ * @param {AXIS_AND_PLANES} axis
77
+ * @returns {boolean}
78
+ */
79
+ export function is2DAxis(axis) {
80
+ return axis === AXIS_AND_PLANES.XY ||
81
+ axis === AXIS_AND_PLANES.XZ ||
82
+ axis === AXIS_AND_PLANES.YZ;
83
+ }