@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.
- package/index.d.ts +229 -24
- package/index.js +13 -4
- package/package.json +2 -2
- package/src/category/appBackedCategory.js +3 -3
- package/src/context.js +2 -2
- package/src/layer/cesium/dataSourceCesiumImpl.js +1 -1
- package/src/layer/cesium/x3dmHelper.js +1 -1
- package/src/layer/cesiumTilesetLayer.js +0 -5
- package/src/map/cesiumMap.js +10 -13
- package/src/map/obliqueMap.js +19 -19
- package/src/map/openlayersMap.js +9 -9
- package/src/map/vcsMap.js +9 -9
- package/src/oblique/obliqueProvider.js +2 -2
- package/src/style/declarativeStyleItem.js +2 -8
- package/src/util/editor/editGeometrySession.js +401 -0
- package/src/util/editor/editorHelpers.js +109 -0
- package/src/util/editor/editorSessionHelpers.js +1 -2
- package/src/util/editor/editorSymbols.js +10 -0
- package/src/util/editor/interactions/editGeometryMouseOverInteraction.js +133 -0
- package/src/util/editor/interactions/insertVertexInteraction.js +92 -0
- package/src/util/editor/interactions/mapInteractionController.js +99 -0
- package/src/util/editor/interactions/removeVertexInteraction.js +39 -0
- package/src/util/editor/interactions/selectSingleFeatureInteraction.js +95 -0
- package/src/util/editor/interactions/translateVertexInteraction.js +61 -0
- package/src/util/mapCollection.js +14 -14
- package/src/util/math.js +9 -0
- package/src/util/splitScreen.js +1 -1
- package/src/util/viewpoint.js +16 -16
- package/src/vcsApp.js +15 -15
- package/src/vcsAppContextHelpers.js +6 -6
- package/tests/unit/helpers/cesiumHelpers.js +3 -4
- 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;
|