@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.
- package/index.d.ts +597 -98
- package/index.js +24 -9
- 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/clusterContext.js +12 -0
- package/src/layer/cesium/vectorCesiumImpl.js +2 -2
- package/src/layer/cesium/vectorContext.js +115 -7
- package/src/layer/cesiumTilesetLayer.js +0 -14
- package/src/layer/czmlLayer.js +1 -1
- package/src/layer/dataSourceLayer.js +1 -53
- package/src/layer/featureLayer.js +0 -44
- package/src/layer/featureStoreLayer.js +0 -15
- package/src/layer/layer.js +0 -11
- package/src/layer/vectorHelpers.js +0 -85
- package/src/layer/vectorLayer.js +0 -9
- package/src/layer/vectorProperties.js +150 -8
- package/src/layer/vectorTileLayer.js +0 -9
- package/src/map/cesiumMap.js +26 -7
- 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/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
|
@@ -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
|
+
}
|