@vcmap/core 5.0.0-rc.21 → 5.0.0-rc.22

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 (32) hide show
  1. package/index.d.ts +229 -24
  2. package/index.js +13 -4
  3. package/package.json +2 -2
  4. package/src/category/appBackedCategory.js +3 -3
  5. package/src/context.js +2 -2
  6. package/src/layer/cesium/dataSourceCesiumImpl.js +1 -1
  7. package/src/layer/cesium/x3dmHelper.js +1 -1
  8. package/src/layer/cesiumTilesetLayer.js +0 -5
  9. package/src/map/cesiumMap.js +10 -13
  10. package/src/map/obliqueMap.js +19 -19
  11. package/src/map/openlayersMap.js +9 -9
  12. package/src/map/vcsMap.js +9 -9
  13. package/src/oblique/obliqueProvider.js +2 -2
  14. package/src/style/declarativeStyleItem.js +2 -8
  15. package/src/util/editor/editGeometrySession.js +401 -0
  16. package/src/util/editor/editorHelpers.js +109 -0
  17. package/src/util/editor/editorSessionHelpers.js +1 -2
  18. package/src/util/editor/editorSymbols.js +10 -0
  19. package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +133 -0
  20. package/src/util/editor/interactions/insertVertexInteraction.js +92 -0
  21. package/src/util/editor/interactions/mapInteractionController.js +99 -0
  22. package/src/util/editor/interactions/removeVertexInteraction.js +39 -0
  23. package/src/util/editor/interactions/selectSingleFeatureInteraction.js +95 -0
  24. package/src/util/editor/interactions/translateVertexInteraction.js +61 -0
  25. package/src/util/mapCollection.js +14 -14
  26. package/src/util/math.js +9 -0
  27. package/src/util/splitScreen.js +1 -1
  28. package/src/util/viewpoint.js +16 -16
  29. package/src/vcsApp.js +15 -15
  30. package/src/vcsAppContextHelpers.js +6 -6
  31. package/tests/unit/helpers/cesiumHelpers.js +3 -4
  32. package/tests/unit/helpers/obliqueHelpers.js +5 -5
@@ -0,0 +1,401 @@
1
+ import { getLogger } from '@vcsuite/logger';
2
+ import { GeometryType, SessionType, setupInteractionChain, setupScratchLayer } from './editorSessionHelpers.js';
3
+ import SelectSingleFeatureInteraction from './interactions/selectSingleFeatureInteraction.js';
4
+ import InteractionChain from '../../interaction/interactionChain.js';
5
+ import VcsEvent from '../../vcsEvent.js';
6
+ import TranslateVertexInteraction from './interactions/translateVertexInteraction.js';
7
+ import RemoveVertexInteraction from './interactions/removeVertexInteraction.js';
8
+ import { createVertex } from './editorHelpers.js';
9
+ import InsertVertexInteraction from './interactions/insertVertexInteraction.js';
10
+ import EditGeometryMouseOverInteraction from './interactions/editGeometryMouseOverInteraction.js';
11
+ import { cartesian2DDistance, modulo } from '../math.js';
12
+ import { createSync, obliqueGeometry } from '../../layer/vectorSymbols.js';
13
+ import geometryIsValid from './validateGeoemetry.js';
14
+ import ObliqueMap from '../../map/obliqueMap.js';
15
+ import { emptyStyle } from '../../style/styleHelpers.js';
16
+ import MapInteractionController from './interactions/mapInteractionController.js';
17
+
18
+ /**
19
+ * @typedef {EditorSession} EditGeometrySession
20
+ * @property {SelectSingleFeatureInteraction} featureSelection - the feature selection for this session.
21
+ */
22
+
23
+ /**
24
+ * @typedef {Object} EditGeometryInteraction
25
+ * @property {InteractionChain} interactionChain
26
+ * @property {function():void} destroy
27
+ * @private
28
+ */
29
+
30
+ /**
31
+ * Create the editing interaction for a feature with a line geometry
32
+ * @param {import("ol").Feature<import("ol/geom").LineString>} feature
33
+ * @param {import("@vcmap/core").VectorLayer} scratchLayer
34
+ * @returns {EditGeometryInteraction}
35
+ */
36
+ function createEditLineStringGeometryInteraction(feature, scratchLayer) {
37
+ const geometry = feature[obliqueGeometry] ?? feature.getGeometry();
38
+ const vertices = geometry.getCoordinates().map(createVertex);
39
+ scratchLayer.addFeatures(vertices);
40
+ const resetGeometry = () => {
41
+ geometry.setCoordinates(vertices.map(f => f.getGeometry().getCoordinates()));
42
+ };
43
+ const translateVertex = new TranslateVertexInteraction();
44
+ translateVertex.vertexChanged.addEventListener(resetGeometry);
45
+
46
+ const insertVertex = new InsertVertexInteraction(feature, geometry);
47
+ insertVertex.vertexInserted.addEventListener(({ vertex, index }) => {
48
+ scratchLayer.addFeatures([vertex]);
49
+ vertices.splice(index, 0, vertex);
50
+ resetGeometry();
51
+ });
52
+
53
+ const removeVertex = new RemoveVertexInteraction();
54
+ removeVertex.vertexRemoved.addEventListener((vertex) => {
55
+ scratchLayer.removeFeaturesById([vertex.getId()]);
56
+ const index = vertices.indexOf(vertex);
57
+ if (index > -1) {
58
+ vertices.splice(index, 1);
59
+ resetGeometry();
60
+ }
61
+ });
62
+
63
+ const interactionChain = new InteractionChain([
64
+ translateVertex,
65
+ insertVertex,
66
+ removeVertex,
67
+ ]);
68
+
69
+ return {
70
+ interactionChain,
71
+ destroy: () => {
72
+ scratchLayer.removeFeaturesById(vertices.map(v => v.getId()));
73
+ interactionChain.destroy();
74
+ },
75
+ };
76
+ }
77
+
78
+ /**
79
+ * @param {import("ol").Feature<import("ol/geom").Circle>} feature
80
+ * @param {import("@vcmap/core").VectorLayer} scratchLayer
81
+ * @returns {EditGeometryInteraction}
82
+ */
83
+ function createEditCircleGeometryInteraction(feature, scratchLayer) {
84
+ const geometry = feature[obliqueGeometry] ?? feature.getGeometry();
85
+ const vertices = geometry.getCoordinates().map(createVertex);
86
+ scratchLayer.addFeatures(vertices);
87
+
88
+ const translateVertex = new TranslateVertexInteraction();
89
+ translateVertex.vertexChanged.addEventListener((vertex) => {
90
+ if (vertices.indexOf(vertex) === 1) {
91
+ const coords = geometry.getCoordinates();
92
+ coords[1] = vertex.getGeometry().getCoordinates();
93
+ const newRadius = cartesian2DDistance(coords[0], coords[1]);
94
+ geometry.setRadius(newRadius);
95
+ } else {
96
+ geometry.setCenter(vertex.getGeometry().getCoordinates());
97
+ vertices[1].getGeometry().setCoordinates(geometry.getCoordinates()[1]);
98
+ }
99
+ });
100
+
101
+ const interactionChain = new InteractionChain([translateVertex]);
102
+
103
+ return {
104
+ interactionChain,
105
+ destroy: () => {
106
+ scratchLayer.removeFeaturesById(vertices.map(v => v.getId()));
107
+ interactionChain.destroy();
108
+ },
109
+ };
110
+ }
111
+
112
+ /**
113
+ * @param {import("ol").Feature<import("ol/geom").Polygon>} feature
114
+ * @param {import("@vcmap/core").VectorLayer} scratchLayer
115
+ * @returns {EditGeometryInteraction}
116
+ */
117
+ function createEditBBoxGeometryInteraction(feature, scratchLayer) {
118
+ const geometry = feature[obliqueGeometry] ?? feature.getGeometry();
119
+ const vertices = geometry.getCoordinates()[0].map(createVertex);
120
+ scratchLayer.addFeatures(vertices);
121
+
122
+ const translateVertex = new TranslateVertexInteraction();
123
+ translateVertex.vertexChanged.addEventListener((vertex) => {
124
+ const vertexIndex = vertices.indexOf(vertex);
125
+ const originIndex = modulo(vertexIndex + 2, 4);
126
+ const rightOfIndex = modulo(vertexIndex + 1, 4);
127
+ const leftOfIndex = modulo(vertexIndex - 1, 4);
128
+
129
+ const originCoords = vertices[originIndex].getGeometry().getCoordinates();
130
+ const vertexCoords = vertex.getGeometry().getCoordinates();
131
+ let preventCollapse = false;
132
+ if (originCoords[0] === vertexCoords[0]) {
133
+ vertexCoords[0] += 1e-8;
134
+ preventCollapse = true;
135
+ }
136
+ if (originCoords[1] === vertexCoords[1]) {
137
+ vertexCoords[1] += 1e-8;
138
+ preventCollapse = true;
139
+ }
140
+
141
+ if (preventCollapse) {
142
+ vertex.getGeometry().setCoordinates(vertexCoords);
143
+ }
144
+ const updateOtherVertex = (otherVertexIndex) => {
145
+ const otherVertexGeometry = vertices[otherVertexIndex].getGeometry();
146
+ const otherVertexCoords = otherVertexGeometry.getCoordinates();
147
+ if (otherVertexCoords[0] === originCoords[0]) {
148
+ otherVertexCoords[1] = vertexCoords[1];
149
+ } else {
150
+ otherVertexCoords[0] = vertexCoords[0];
151
+ }
152
+ otherVertexGeometry.setCoordinates(otherVertexCoords);
153
+ };
154
+
155
+ updateOtherVertex(rightOfIndex);
156
+ updateOtherVertex(leftOfIndex);
157
+
158
+ geometry.setCoordinates([vertices.map(f => f.getGeometry().getCoordinates())]);
159
+ });
160
+
161
+ const interactionChain = new InteractionChain([translateVertex]);
162
+
163
+ return {
164
+ interactionChain,
165
+ destroy: () => {
166
+ scratchLayer.removeFeaturesById(vertices.map(v => v.getId()));
167
+ interactionChain.destroy();
168
+ },
169
+ };
170
+ }
171
+
172
+ /**
173
+ * @param {import("ol").Feature<import("ol/geom").Polygon>} feature
174
+ * @param {import("@vcmap/core").VectorLayer} scratchLayer
175
+ * @returns {EditGeometryInteraction}
176
+ */
177
+ function createEditSimplePolygonInteraction(feature, scratchLayer) {
178
+ const geometry = feature[obliqueGeometry] ?? feature.getGeometry();
179
+ const linearRing = geometry.getLinearRing(0);
180
+ const vertices = linearRing.getCoordinates().map(createVertex);
181
+ scratchLayer.addFeatures(vertices);
182
+ const resetGeometry = () => {
183
+ const coordinates = vertices.map(f => f.getGeometry().getCoordinates());
184
+ linearRing.setCoordinates(coordinates); // update linear ring for proper vertex insertion
185
+ geometry.setCoordinates([vertices.map(f => f.getGeometry().getCoordinates())]); // update actual geometry, since linear ring is a clone and not a ref
186
+ };
187
+
188
+ const translateVertex = new TranslateVertexInteraction();
189
+ translateVertex.vertexChanged.addEventListener(resetGeometry);
190
+
191
+ const insertVertex = new InsertVertexInteraction(feature, linearRing);
192
+ insertVertex.vertexInserted.addEventListener(({ vertex, index }) => {
193
+ scratchLayer.addFeatures([vertex]);
194
+ vertices.splice(index, 0, vertex);
195
+ resetGeometry();
196
+ });
197
+
198
+ const removeVertex = new RemoveVertexInteraction();
199
+ removeVertex.vertexRemoved.addEventListener((vertex) => {
200
+ scratchLayer.removeFeaturesById([vertex.getId()]);
201
+ const index = vertices.indexOf(vertex);
202
+ if (index > -1) {
203
+ vertices.splice(index, 1);
204
+ resetGeometry();
205
+ }
206
+ });
207
+
208
+ const interactionChain = new InteractionChain([
209
+ translateVertex,
210
+ insertVertex,
211
+ removeVertex,
212
+ ]);
213
+
214
+ return {
215
+ interactionChain,
216
+ destroy: () => {
217
+ scratchLayer.removeFeaturesById(vertices.map(v => v.getId()));
218
+ interactionChain.destroy();
219
+ },
220
+ };
221
+ }
222
+
223
+ /**
224
+ * @param {import("ol").Feature<import("ol/geom").Point>} feature
225
+ * @param {import("@vcmap/core").VectorLayer} scratchLayer
226
+ * @returns {EditGeometryInteraction}
227
+ */
228
+ function createEditPointInteraction(feature, scratchLayer) {
229
+ const vertex = createVertex(feature.getGeometry().getCoordinates());
230
+ const featureStyle = feature.getStyle();
231
+ feature.setStyle(emptyStyle);
232
+ scratchLayer.addFeatures([vertex]);
233
+ const translateVertex = new TranslateVertexInteraction();
234
+ translateVertex.vertexChanged.addEventListener(() => {
235
+ feature.getGeometry().setCoordinates(vertex.getGeometry().getCoordinates());
236
+ });
237
+
238
+ const interactionChain = new InteractionChain([
239
+ translateVertex,
240
+ ]);
241
+
242
+ return {
243
+ interactionChain,
244
+ destroy: () => {
245
+ interactionChain.destroy();
246
+ scratchLayer.removeFeaturesById([vertex.getId()]);
247
+ feature.setStyle(featureStyle);
248
+ },
249
+ };
250
+ }
251
+
252
+ /**
253
+ * Creates the edit geometry session.
254
+ * @param {import("@vcmap/core").VcsApp} app
255
+ * @param {import("@vcmap/core").VectorLayer} layer
256
+ * @returns {EditGeometrySession}
257
+ */
258
+ export default function startEditGeometrySession(app, layer) {
259
+ const {
260
+ interactionChain,
261
+ removed: interactionRemoved,
262
+ destroy: destroyInteractionChain,
263
+ } = setupInteractionChain(app.maps.eventHandler);
264
+
265
+ const scratchLayer = setupScratchLayer(app.layers);
266
+
267
+ const selectFeatureInteraction = new SelectSingleFeatureInteraction(layer);
268
+ interactionChain.addInteraction(selectFeatureInteraction);
269
+
270
+ const mapInteractionController = new MapInteractionController();
271
+ interactionChain.addInteraction(mapInteractionController);
272
+
273
+ const mouseOverInteraction = new EditGeometryMouseOverInteraction(layer.name);
274
+ interactionChain.addInteraction(mouseOverInteraction);
275
+
276
+ /**
277
+ * @type {VcsEvent<void>}
278
+ */
279
+ const stopped = new VcsEvent();
280
+
281
+ /**
282
+ * @type {EditGeometryInteraction|null}
283
+ */
284
+ let currentInteractionSet = null;
285
+ let currentFeature = null;
286
+ /**
287
+ * @type {ObliqueMap|null}
288
+ */
289
+ let obliqueMap = null;
290
+
291
+ const destroyCurrentInteractionSet = () => {
292
+ if (currentInteractionSet) {
293
+ interactionChain.removeInteraction(currentInteractionSet.interactionChain);
294
+ currentInteractionSet.destroy();
295
+ currentInteractionSet = null;
296
+ }
297
+
298
+ if (currentFeature) {
299
+ delete currentFeature[createSync];
300
+ if (!geometryIsValid(currentFeature.getGeometry())) {
301
+ layer.removeFeaturesById([currentFeature.getId()]);
302
+ }
303
+ }
304
+ currentFeature = null;
305
+
306
+ if (obliqueMap) {
307
+ obliqueMap.switchEnabled = true;
308
+ }
309
+ };
310
+
311
+ selectFeatureInteraction.featureChanged.addEventListener((feature) => {
312
+ destroyCurrentInteractionSet();
313
+ if (feature) {
314
+ if (obliqueMap) {
315
+ obliqueMap.switchEnabled = false;
316
+ }
317
+ currentFeature = feature;
318
+ currentFeature[createSync] = true;
319
+ const geometry = feature[obliqueGeometry] ?? feature.getGeometry();
320
+ const geometryType = geometry.getType();
321
+ if (geometryType === GeometryType.Polygon) {
322
+ if (geometry.get('_vcsGeomType') === GeometryType.BBox) {
323
+ currentInteractionSet = createEditBBoxGeometryInteraction(
324
+ /** @type {import("ol").Feature<import("ol/geom").Polygon>} */ (feature),
325
+ scratchLayer,
326
+ );
327
+ } else if (/** @type {import("ol/geom").Polygon} */ (geometry).getLinearRingCount() === 1) {
328
+ currentInteractionSet = createEditSimplePolygonInteraction(
329
+ /** @type {import("ol").Feature<import("ol/geom").Polygon>} */ (feature),
330
+ scratchLayer,
331
+ );
332
+ }
333
+ } else if (geometryType === GeometryType.LineString) {
334
+ currentInteractionSet = createEditLineStringGeometryInteraction(
335
+ /** @type {import("ol").Feature<import("ol/geom").LineString>} */ (feature),
336
+ scratchLayer,
337
+ );
338
+ } else if (geometryType === GeometryType.Point) {
339
+ currentInteractionSet = createEditPointInteraction(
340
+ /** @type {import("ol").Feature<import("ol/geom").Point>} */ (feature),
341
+ scratchLayer,
342
+ );
343
+ } else if (geometryType === GeometryType.Circle) {
344
+ currentInteractionSet = createEditCircleGeometryInteraction(
345
+ /** @type {import("ol").Feature<import("ol/geom").Circle>} */ (feature),
346
+ scratchLayer,
347
+ );
348
+ }
349
+
350
+ if (currentInteractionSet) {
351
+ interactionChain.addInteraction(currentInteractionSet.interactionChain);
352
+ } else {
353
+ getLogger('EditGeometrySession').warning(`Geometry of type ${geometryType} is currently not supported`);
354
+ currentFeature[createSync] = false;
355
+ currentFeature = null;
356
+ selectFeatureInteraction.clear();
357
+ }
358
+ }
359
+ });
360
+
361
+ let obliqueImageChangedListener = () => {};
362
+ const setupActiveMap = () => {
363
+ mapInteractionController.reset();
364
+ mouseOverInteraction.reset();
365
+ selectFeatureInteraction.clear();
366
+ obliqueImageChangedListener();
367
+ const { activeMap } = app.maps;
368
+ if (activeMap instanceof ObliqueMap) {
369
+ obliqueMap = activeMap;
370
+ obliqueImageChangedListener = /** @type {ObliqueMap} */ (activeMap).imageChanged
371
+ .addEventListener(() => {
372
+ selectFeatureInteraction.clear();
373
+ });
374
+ } else {
375
+ obliqueMap = null;
376
+ obliqueImageChangedListener = () => {};
377
+ }
378
+ };
379
+ const mapActivatedListener = app.maps.mapActivated.addEventListener(setupActiveMap);
380
+ setupActiveMap();
381
+
382
+ const stop = () => {
383
+ app.layers.remove(scratchLayer);
384
+ obliqueImageChangedListener();
385
+ mapActivatedListener();
386
+ mapInteractionController.reset();
387
+ mouseOverInteraction.reset();
388
+ destroyCurrentInteractionSet();
389
+ destroyInteractionChain();
390
+ stopped.raiseEvent();
391
+ stopped.destroy();
392
+ };
393
+ interactionRemoved.addEventListener(stop);
394
+
395
+ return {
396
+ type: SessionType.EDIT_GEOMETRY,
397
+ featureSelection: selectFeatureInteraction,
398
+ stopped,
399
+ stop,
400
+ };
401
+ }
@@ -0,0 +1,109 @@
1
+ import Point from 'ol/geom/Point.js';
2
+ import Feature from 'ol/Feature.js';
3
+ import { Cartesian2, Cartesian3, Math as CesiumMath, Plane } from '@vcmap/cesium';
4
+ import Projection from '../projection.js';
5
+ import { vertexSymbol } from './editorSymbols.js';
6
+ import Vector from '../../layer/vectorLayer.js';
7
+
8
+ /**
9
+ * @param {import("ol/coordinate").Coordinate} coordinate
10
+ * @returns {Vertex}
11
+ */
12
+ export function createVertex(coordinate) {
13
+ const geometry = new Point(coordinate);
14
+ geometry[Vector.alreadyTransformedToImage] = true;
15
+ const vertex = new Feature({
16
+ geometry,
17
+ });
18
+ vertex[vertexSymbol] = true;
19
+ vertex[Vector.doNotTransform] = true;
20
+ return vertex;
21
+ }
22
+
23
+ let scratchCartesian21 = new Cartesian2();
24
+ let scratchCartesian22 = new Cartesian2();
25
+ let scratchCartesian23 = new Cartesian2();
26
+ let scratchCartesian31 = new Cartesian3();
27
+ let scratchCartesian32 = new Cartesian3();
28
+ let scratchCartesian33 = new Cartesian3();
29
+
30
+ /**
31
+ * @param {import("ol/coordinate").Coordinate} start - line segment start
32
+ * @param {import("ol/coordinate").Coordinate} end - line segment end
33
+ * @param {import("ol/coordinate").Coordinate} point - the point to project
34
+ * @param {number=} [epsilon=CesiumMath.EPSILON5]
35
+ * @returns {boolean}
36
+ */
37
+ export function pointOnLine3D(start, end, point, epsilon = CesiumMath.EPSILON5) {
38
+ scratchCartesian31 = Cartesian3
39
+ .fromElements(end[0] - start[0], end[1] - start[1], end[2] - start[2], scratchCartesian31);
40
+ scratchCartesian32 = Cartesian3
41
+ .fromElements(point[0] - start[0], point[1] - start[1], point[2] - start[2], scratchCartesian32);
42
+ scratchCartesian33 = Cartesian3
43
+ .fromElements(point[0] - end[0], point[1] - end[1], point[2] - point[2], scratchCartesian33);
44
+ const mag1 = Cartesian3.magnitude(scratchCartesian31);
45
+ if (
46
+ mag1 < Cartesian3.magnitude(scratchCartesian32) ||
47
+ mag1 < Cartesian3.magnitude(scratchCartesian33)
48
+ ) {
49
+ return false;
50
+ }
51
+
52
+ scratchCartesian31 = Cartesian3.normalize(scratchCartesian31, scratchCartesian31);
53
+ scratchCartesian32 = Cartesian3.normalize(scratchCartesian32, scratchCartesian32);
54
+ return scratchCartesian31.equalsEpsilon(scratchCartesian32, epsilon);
55
+ }
56
+
57
+
58
+ /**
59
+ * @param {import("ol/coordinate").Coordinate} start - line segment start
60
+ * @param {import("ol/coordinate").Coordinate} end - line segment end
61
+ * @param {import("ol/coordinate").Coordinate} point - the point to project
62
+ * @param {number=} [epsilon=CesiumMath.EPSILON5]
63
+ * @returns {boolean}
64
+ */
65
+ export function pointOnLine2D(start, end, point, epsilon = CesiumMath.EPSILON5) {
66
+ scratchCartesian21 = Cartesian2.fromElements(end[0] - start[0], end[1] - start[1], scratchCartesian21);
67
+ scratchCartesian22 = Cartesian2.fromElements(point[0] - start[0], point[1] - start[1], scratchCartesian22);
68
+ scratchCartesian23 = Cartesian2.fromElements(point[0] - end[0], point[1] - end[1], scratchCartesian23);
69
+ const mag1 = Cartesian2.magnitude(scratchCartesian21);
70
+ if (
71
+ mag1 < Cartesian2.magnitude(scratchCartesian22) ||
72
+ mag1 < Cartesian2.magnitude(scratchCartesian23)
73
+ ) {
74
+ return false;
75
+ }
76
+
77
+ scratchCartesian21 = Cartesian2.normalize(scratchCartesian21, scratchCartesian21);
78
+ scratchCartesian22 = Cartesian2.normalize(scratchCartesian22, scratchCartesian22);
79
+
80
+ return scratchCartesian21.equalsEpsilon(scratchCartesian22, epsilon);
81
+ }
82
+
83
+ /**
84
+ * @param {import("ol/coordinate").Coordinate} originCoordinates
85
+ * @param {import("@vcmap/cesium").Scene} scene
86
+ * @returns {!import("@vcmap/cesium").Plane}
87
+ */
88
+ export function createVerticalPlane(originCoordinates, scene) {
89
+ const wgs84 = Projection.mercatorToWgs84(originCoordinates);
90
+ scratchCartesian31 = Cartesian3.fromDegrees(wgs84[0], wgs84[1], wgs84[2]);
91
+ scratchCartesian32 = scene.globe.ellipsoid.geodeticSurfaceNormal(scratchCartesian31, scratchCartesian32);
92
+ scratchCartesian32 = Cartesian3.cross(scene.camera.rightWC, scratchCartesian32, scratchCartesian32);
93
+ scratchCartesian32 = Cartesian3.normalize(scratchCartesian32, scratchCartesian32);
94
+
95
+ return Plane.fromPointNormal(scratchCartesian31, scratchCartesian32);
96
+ }
97
+
98
+ /**
99
+ * @param {import("ol/coordinate").Coordinate} originCoordinates
100
+ * @param {import("@vcmap/cesium").Scene} scene
101
+ * @returns {!import("@vcmap/cesium").Plane}
102
+ */
103
+ export function createHorizontalPlane(originCoordinates, scene) {
104
+ const wgs84 = Projection.mercatorToWgs84(originCoordinates);
105
+ scratchCartesian31 = Cartesian3.fromDegrees(wgs84[0], wgs84[1], wgs84[2]);
106
+ scratchCartesian32 = scene.globe.ellipsoid.geodeticSurfaceNormal(scratchCartesian31, scratchCartesian32);
107
+
108
+ return Plane.fromPointNormal(scratchCartesian31, scratchCartesian32);
109
+ }
@@ -80,7 +80,7 @@ export function setupInteractionChain(eventHandler) {
80
80
 
81
81
  return {
82
82
  interactionChain,
83
- destroy: () => {
83
+ destroy() {
84
84
  listener();
85
85
  removed.destroy();
86
86
  interactionChain.destroy();
@@ -97,7 +97,6 @@ export function setupInteractionChain(eventHandler) {
97
97
  * @property {string} LineString
98
98
  * @property {string} Polygon
99
99
  * @property {string} BBox
100
- * @property {string} Rectangle
101
100
  */
102
101
  export const GeometryType = {
103
102
  Point: 'Point',
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Symbol to identify a {@see Vertex}
3
+ * @type {symbol}
4
+ */
5
+ export const vertexSymbol = Symbol('Vertex');
6
+ /**
7
+ * Symbol to denote the vertexes index in the vertices array. This is important for snapping & bbox operations
8
+ * @type {symbol}
9
+ */
10
+ export const vertexIndex = Symbol('VertexIndex');
@@ -0,0 +1,133 @@
1
+ import { vertexSymbol } from '../editorSymbols.js';
2
+ import AbstractInteraction from '../../../interaction/abstractInteraction.js';
3
+ import { ModificationKeyType, EventType } from '../../../interaction/interactionType.js';
4
+ import { vcsLayerName } from '../../../layer/layerSymbols.js';
5
+
6
+ /**
7
+ * only exported for tests
8
+ * @type {Object}
9
+ */
10
+ export const cursorMap = { // TODO these can now be designed custom. IE11 no linger required
11
+ auto: 'auto',
12
+ scaleNESW: 'nesw-resize',
13
+ scaleNWSE: 'nwse-resize',
14
+ rotate: 'crosshair',
15
+ translate: 'move',
16
+ select: 'pointer',
17
+ edit: 'pointer', // fa pencil
18
+ translateVertex: 'move', // fa-stack pointer-move
19
+ removeVertex: 'pointer', // fa-stack pencil-minus
20
+ insertVertex: 'cell', // fa-stack pencil-plus
21
+ addToSelection: 'cell', // fa-stack pointer-black
22
+ removeFromSelection: 'not-allowed',
23
+ };
24
+
25
+ /**
26
+ * A class to handle mouse over effects on features for editor sessions.
27
+ * @class
28
+ * @extends {AbstractInteraction}
29
+ */
30
+ class EditGeometryMouseOverInteraction extends AbstractInteraction {
31
+ /**
32
+ * @param {string} layerName - the layer name of the currently editing layer
33
+ */
34
+ constructor(layerName) {
35
+ super(EventType.MOVE, ModificationKeyType.ALL);
36
+ /**
37
+ * @type {import("ol").Feature|import("@vcmap/cesium").Cesium3DTileFeature|import("@vcmap/cesium").Cesium3DTilePointFeature|null}
38
+ * @private
39
+ */
40
+ this._lastFeature = null;
41
+ /**
42
+ * The layer name to react to
43
+ * @type {string}
44
+ */
45
+ this.layerName = layerName;
46
+ /**
47
+ * @type {CSSStyleDeclaration}
48
+ */
49
+ this.cursorStyle = document.body.style;
50
+
51
+ this.setActive();
52
+ }
53
+
54
+ /**
55
+ * @inheritDoc
56
+ * @param {InteractionEvent} event
57
+ * @returns {Promise<InteractionEvent>}
58
+ */
59
+ async pipe(event) {
60
+ if (
61
+ event.feature &&
62
+ (
63
+ event.feature[vcsLayerName] === this.layerName ||
64
+ event.feature[vertexSymbol]
65
+ )
66
+ ) {
67
+ this._lastFeature = /** @type {import("ol").Feature|import("@vcmap/cesium").Cesium3DTileFeature|import("@vcmap/cesium").Cesium3DTilePointFeature} */
68
+ (event.feature);
69
+ } else {
70
+ this._lastFeature = null;
71
+ }
72
+ this._evaluate(event.key);
73
+ return event;
74
+ }
75
+
76
+ /**
77
+ * @inheritDoc
78
+ * @param {ModificationKeyType} modifier
79
+ */
80
+ modifierChanged(modifier) {
81
+ this._evaluate(modifier);
82
+ }
83
+
84
+ /**
85
+ * @inheritDoc
86
+ * @param {(boolean|number)=} active
87
+ */
88
+ setActive(active) {
89
+ super.setActive(active);
90
+ this.reset();
91
+ }
92
+
93
+ /**
94
+ * Reset the cursorStyle to auto
95
+ */
96
+ reset() {
97
+ if (this.cursorStyle && this.cursorStyle.cursor) {
98
+ this.cursorStyle.cursor = cursorMap.auto;
99
+ }
100
+ }
101
+
102
+ /**
103
+ * @param {ModificationKeyType} modifier
104
+ * @private
105
+ */
106
+ _evaluate(modifier) {
107
+ if (this._lastFeature) {
108
+ if (this._lastFeature[vertexSymbol]) {
109
+ if (modifier === ModificationKeyType.SHIFT) {
110
+ this.cursorStyle.cursor = cursorMap.removeVertex;
111
+ } else if (modifier === ModificationKeyType.ALT) {
112
+ this.cursorStyle.cursor = cursorMap.translateVertex;
113
+ } else {
114
+ this.cursorStyle.cursor = cursorMap.auto;
115
+ }
116
+ } else {
117
+ this.cursorStyle.cursor = cursorMap.select;
118
+ }
119
+ } else {
120
+ this.cursorStyle.cursor = cursorMap.auto;
121
+ }
122
+ }
123
+
124
+ /**
125
+ * @inheritDoc
126
+ */
127
+ destroy() {
128
+ this.cursorStyle = null;
129
+ super.destroy();
130
+ }
131
+ }
132
+
133
+ export default EditGeometryMouseOverInteraction;