• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

geosolutions-it / MapStore2 / 16471160649

23 Jul 2025 12:51PM UTC coverage: 76.923% (-0.003%) from 76.926%
16471160649

push

github

web-flow
Fix #11103 Update cesium to latest stable 1.131.0 , reviewed all the cesium layers and cesium map. (#11331)

* Fix:#11103 Reviewed cesium layers and cesium upgrade to latest stable 1.131.0

* removed debug message

* Fix: Arcgis cesium layer: resource object was missing when cesium was upgrade to latest stable

* Fix: testcases for different layers in cesium

* Fix: test case because  Cesium Changed behavior of DataSourceDisplay.ready to always stay true once it is initially set to true

* Fix : failing test case where tileWidth and tileHeight are now mandatory to create the Single Tile

* fix:Typo

* fix : Arcgislayer and overlay layer

* Removed duplication of function call and added optional chaining

* fix: change in varialbe name

* fix: Remove ready from Geojson Edit in cesium

* added edge cases to check url before creating the terrainprovider

31354 of 48775 branches covered (64.28%)

45 of 59 new or added lines in 9 files covered. (76.27%)

17 existing lines in 6 files now uncovered.

38870 of 50531 relevant lines covered (76.92%)

36.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

56.59
/web/client/utils/cesium/ModifyGeoJSONInteraction.js
1
/*
2
 * Copyright 2023, GeoSolutions Sas.
3
 * All rights reserved.
4
 *
5
 * This source code is licensed under the BSD-style license found in the
6
 * LICENSE file in the root directory of this source tree.
7
 */
8

9
import * as Cesium from 'cesium';
10

11
import {
12
    getCesiumColor,
13
    createPolylinePrimitive,
14
    createPolygonPrimitive,
15
    clearPrimitivesCollection,
16
    createCircleMarkerImage,
17
    createEllipsePrimitive,
18
    createEllipsePolylinePrimitive,
19
    createCylinderPolylinePrimitive,
20
    createCylinderPrimitive
21
} from './PrimitivesUtils';
22
import {
23
    computeDistance,
24
    computeGeodesicCoordinates
25
} from './MathUtils';
26
import throttle from 'lodash/throttle';
27
import isString from 'lodash/isString';
28
import { computePositionInfo } from './ClickUtils';
29
import {
30
    generateEditingStyle,
31
    featureToModifyProperties as defaultFeatureToModifyProperties,
32
    modifyPropertiesToFeatureProperties as defaultModifyPropertiesToFeatureProperties
33
} from '../DrawUtils';
34

35
function featureToCartesianCoordinates(geometryType, feature) {
36

37
    if (feature?.geometry === null) {
30!
38
        return null;
×
39
    }
40

41
    const { geometry = {} } = feature;
30!
42
    const { coordinates = []} = geometry;
30!
43

44
    switch (geometryType || geometry?.type) {
30!
45
    case 'Point':
46
    case 'Circle':
47
        return [Cesium.Cartographic.toCartesian(Cesium.Cartographic.fromDegrees(...coordinates))];
15✔
48
    case 'LineString':
49
        return coordinates.map((coords) => Cesium.Cartographic.toCartesian(Cesium.Cartographic.fromDegrees(...coords)));
18✔
50
    case 'Polygon':
51
        return coordinates[0].map((coords) => Cesium.Cartographic.toCartesian(Cesium.Cartographic.fromDegrees(...coords)));
24✔
52
    default:
53
        return null;
×
54
    }
55
}
56

57
function updateCoordinatesHeight(coordinates) {
58
    return coordinates.map(([lng, lat, height]) => [lng, lat, height === undefined ? 0 : height]);
24✔
59
}
60

61
function updateFeatureCoordinates(feature, updateCallback) {
62
    if (feature?.geometry === null) {
10!
63
        return feature;
×
64
    }
65
    const { geometry = {} } = feature;
10!
66
    switch (geometry?.type) {
10!
67
    case 'Point':
68
        return {
3✔
69
            ...feature,
70
            geometry: {
71
                type: 'Point',
72
                coordinates: updateCoordinatesHeight([feature.geometry.coordinates].reduce(updateCallback, []))[0]
73
            }
74
        };
75
    case 'LineString':
76
        return {
5✔
77
            ...feature,
78
            geometry: {
79
                type: 'LineString',
80
                coordinates: updateCoordinatesHeight(feature.geometry.coordinates.reduce(updateCallback, []))
81
            }
82
        };
83
    case 'Polygon':
84
        return {
2✔
85
            ...feature,
86
            geometry: {
87
                type: 'Polygon',
88
                coordinates: [updateCoordinatesHeight(feature.geometry.coordinates[0].reduce(updateCallback, []))]
89
            }
90
        };
91
    default:
92
        return feature;
×
93
    }
94
}
95

96
/**
97
 * Class to manage all modify interaction of Cesium library given a GeoJSON as input data
98
At the moment  only `Feature` or `FeatureCollection` with single geometries are supported. **The component does not support multi geometry types**.
99
 * Following feature properties are used by the edit tool:
100
 * - properties.geodesic {boolean} if true enabled geodesic geometries editing
101
 * - properties.radius {number} value in meters of radius for `Circle` geometry
102
 * @param {object} options.map a Cesium map instance
103
 * @prop {object} geojson `Feature` or `FeatureCollection` GeoJSON data, **does not support multi geometry types**
104
 * @prop {function} getGeometryType argument of the function is the feature and it should return a string representing the geometry type: `Point`, `LineString`, `Polygon` or `Circle`
105
 * @param {function} options.onEditEnd triggered one the editing has been completed
106
 * @param {object} options.style style for drawing geometries, see the web/client/DrawUtils.js file
107
 * @param {number} options.mouseMoveThrottleTime change the throttle time to get feedback on mouse move event, default 100ms
108
 * @param {function} options.getPositionInfo custom function to return info given a position
109
 */
110
class CesiumModifyGeoJSONInteraction {
111
    constructor(options = {}) {
×
112
        // TODO: add support for multi geometry type.
113
        // We could check if possible to keep the currently workflow by splitting the multi geometry in single geometry
114
        // then on edit end reconstruct the multi geometry
115
        this._map = options.map;
9✔
116
        this._getPositionInfo = options.getPositionInfo ? options.getPositionInfo : (movement) => {
9!
UNCOV
117
            const position = movement.position || movement.endPosition;
×
118
            const intersected = this._map.scene.drillPick(position);
×
119
            const { cartesian, cartographic } = computePositionInfo(this._map, movement);
×
120
            return { intersected, cartesian, cartographic };
×
121
        };
122
        this._handler = new Cesium.ScreenSpaceEventHandler(this._map.canvas);
9✔
123
        this._handler.setInputAction((movement) => {
9✔
124
            this._handleEdit(movement, true);
1✔
125
        }, Cesium.ScreenSpaceEventType.LEFT_DOUBLE_CLICK);
126
        this._handler.setInputAction((movement) => {
9✔
127
            this._handleEdit(movement);
19✔
128
        }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
129
        this._handler.setInputAction(throttle((movement) => {
9✔
130
            this._handleMouseMove(movement);
×
131
        }, options.mouseMoveThrottleTime ?? 100), Cesium.ScreenSpaceEventType.MOUSE_MOVE);
18✔
132
        this._staticPrimitivesCollection = new Cesium.PrimitiveCollection({ destroyPrimitives: true });
9✔
133
        this._map.scene.primitives.add(this._staticPrimitivesCollection);
9✔
134

135
        this._staticBillboardCollection = new Cesium.BillboardCollection({ scene: this._map.scene });
9✔
136
        this._map.scene.primitives.add(this._staticBillboardCollection);
9✔
137

138
        this._dynamicPrimitivesCollection = new Cesium.PrimitiveCollection({ destroyPrimitives: true });
9✔
139
        this._map.scene.primitives.add(this._dynamicPrimitivesCollection);
9✔
140

141
        this._dynamicBillboardCollection = new Cesium.BillboardCollection({ scene: this._map.scene });
9✔
142
        this._map.scene.primitives.add(this._dynamicBillboardCollection);
9✔
143

144
        this._style = generateEditingStyle(options.style);
9✔
145

146
        this._cursorImage = createCircleMarkerImage(this._style ?.cursor?.radius * 2, { stroke: '#ffffff', strokeWidth: this._style ?.cursor?.width, fill: 'rgba(0, 0, 0, 0)' });
9✔
147
        this._coordinateNodeImage = createCircleMarkerImage(this._style ?.coordinatesNode?.radius * 2, { stroke: '#ffffff', strokeWidth: this._style?.coordinatesNode?.width, fill: 'rgba(0, 0, 0, 0.1)' });
9✔
148

149
        this._editing = false;
9✔
150

151
        this._onKeyboardEvent = (event) => {
9✔
152
            if (event.code === 'Escape' && this._editing) {
×
153
                this._editing = false;
×
154
                this._dynamicPrimitivesCollection.removeAll();
×
155
                this._dynamicBillboardCollection.removeAll();
×
156
            }
157
            if (this._map?.scene && !this._map.isDestroyed()) {
×
158
                this._map.scene.requestRender();
×
159
            }
160
        };
161
        window.addEventListener('keydown', this._onKeyboardEvent);
9✔
162

163
        this._featureToModifyProperties = defaultFeatureToModifyProperties({ getGeometryType: options?.getGeometryType });
9✔
164
        this._modifyPropertiesToFeatureProperties = defaultModifyPropertiesToFeatureProperties;
9✔
165
        this._onEditEnd = options?.onEditEnd ? options.onEditEnd : () => {};
9!
166
        this.setGeoJSON(options?.geojson || []);
9!
167
        Cesium.GroundPrimitive.initializeTerrainHeights()
9✔
168
            .then(() => {
169
                this._drawStaticFeatures();
9✔
170
            });
171
    }
172
    setGeoJSON(geojson) {
173
        this._features = geojson ? (
18!
174
            geojson?.type === 'Feature'
18✔
175
                ? [geojson]
176
                : geojson?.features
177
        ).map((feature) => ({ ...feature, properties: this._featureToModifyProperties(feature) })) : [];
20✔
178
        this._geojson = {...geojson};
18✔
179
        this._drawStaticFeatures();
18✔
180
    }
181
    remove() {
182
        if (this._handler) {
9!
183
            this._handler.destroy();
9✔
184
            this._handler = null;
9✔
185
        }
186
        this._editing = false;
9✔
187
        if (this._onKeyboardEvent) {
9!
188
            window.removeEventListener('keydown', this._onKeyboardEvent);
9✔
189
        }
190
        if (this._map?.isDestroyed && !this._map.isDestroyed()) {
9!
191
            clearPrimitivesCollection(this._map, this._staticPrimitivesCollection);
×
192
            this._staticPrimitivesCollection = null;
×
193
            clearPrimitivesCollection(this._map, this._staticBillboardCollection);
×
194
            this._staticBillboardCollection = null;
×
195

196
            clearPrimitivesCollection(this._map, this._dynamicPrimitivesCollection);
×
197
            this._dynamicPrimitivesCollection = null;
×
198
            clearPrimitivesCollection(this._map, this._dynamicBillboardCollection);
×
199
            this._dynamicBillboardCollection = null;
×
200
        }
201
    }
202
    _drawStaticFeatures() {
203
        this._staticBillboardCollection?.removeAll();
27✔
204
        this._staticPrimitivesCollection?.removeAll();
27✔
205
        if (this._features?.length > 0) {
27!
206
            this._features.forEach((feature) => {
27✔
207
                this._updatePrimitives(feature);
30✔
208
            });
209
        }
210
    }
211
    _updatePrimitives(newFeature) {
212
        const { geometryType, geodesic, radius } = newFeature?.properties || {};
30!
213
        const coordinates = featureToCartesianCoordinates(geometryType, newFeature);
30✔
214
        if (coordinates) {
30!
215
            switch (geometryType) {
30!
216
            case 'Circle':
217
                this._staticBillboardCollection.add({
6✔
218
                    id: newFeature?.id,
219
                    position: coordinates[coordinates.length - 1],
220
                    image: this._coordinateNodeImage,
221
                    color: getCesiumColor({
222
                        ...this._style?.coordinatesNode
223
                    }),
224
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
225
                });
226
                if (geodesic) {
6!
227
                    this._staticPrimitivesCollection.add(createEllipsePrimitive({
6✔
228
                        ...this._style?.areaDrawing,
229
                        coordinates: coordinates[coordinates.length - 1],
230
                        radius,
231
                        clampToGround: true
232
                    }));
233
                    // add a transparent line to improve interaction
234
                    this._staticPrimitivesCollection.add(createEllipsePolylinePrimitive({
6✔
235
                        ...this._style?.lineDrawing,
236
                        dashLength: undefined,
237
                        opacity: 0.01,
238
                        coordinates: coordinates[coordinates.length - 1],
239
                        radius,
240
                        allowPicking: true,
241
                        id: newFeature?.id,
242
                        geodesic: true
243
                    }));
244
                    this._staticPrimitivesCollection.add(createEllipsePolylinePrimitive({
6✔
245
                        ...this._style?.lineDrawing,
246
                        coordinates: coordinates[coordinates.length - 1],
247
                        radius,
248
                        allowPicking: false,
249
                        geodesic: true
250
                    }));
251
                } else {
252
                    this._staticPrimitivesCollection.add(createCylinderPrimitive({
×
253
                        ...this._style?.areaDrawing,
254
                        coordinates: coordinates[coordinates.length - 1],
255
                        radius
256
                    }));
257
                    this._staticPrimitivesCollection.add(createCylinderPolylinePrimitive({
×
258
                        ...this._style?.lineDrawing,
259
                        coordinates: coordinates[coordinates.length - 1],
260
                        allowPicking: true,
261
                        id: newFeature?.id,
262
                        radius
263
                    }));
264
                }
265
                break;
6✔
266
            case 'Point':
267
                this._staticBillboardCollection.add({
9✔
268
                    id: newFeature?.id,
269
                    position: coordinates[coordinates.length - 1],
270
                    image: this._coordinateNodeImage,
271
                    color: getCesiumColor({
272
                        ...this._style?.coordinatesNode
273
                    }),
274
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
275
                });
276
                break;
9✔
277
            case 'LineString':
278
                if (coordinates.length > 1) {
9!
279
                    coordinates.forEach((position, idx) => {
9✔
280
                        if (coordinates[idx + 1]) {
18✔
281
                            // add a transparent line to improve interaction
282
                            this._staticPrimitivesCollection.add(createPolylinePrimitive({
9✔
283
                                ...this._style?.lineDrawing,
284
                                dashLength: undefined,
285
                                opacity: 0.01,
286
                                coordinates: [
287
                                    position,
288
                                    coordinates[idx + 1]
289
                                ],
290
                                allowPicking: true,
291
                                id: `${newFeature?.id}:${idx}:segment`,
292
                                geodesic
293
                            }));
294
                            this._staticPrimitivesCollection.add(createPolylinePrimitive({
9✔
295
                                ...this._style?.lineDrawing,
296
                                coordinates: [
297
                                    position,
298
                                    coordinates[idx + 1]
299
                                ],
300
                                allowPicking: false,
301
                                geodesic
302
                            }));
303
                        }
304
                        this._staticBillboardCollection.add({
18✔
305
                            id: `${newFeature?.id}:${idx}:vertex`,
306
                            position,
307
                            image: this._coordinateNodeImage,
308
                            color: getCesiumColor({
309
                                ...this._style?.coordinatesNode
310
                            }),
311
                            disableDepthTestDistance: Number.POSITIVE_INFINITY
312
                        });
313
                    });
314
                }
315
                break;
9✔
316
            case 'Polygon':
317
                if (coordinates.length > 2) {
6!
318
                    this._staticPrimitivesCollection.add(createPolygonPrimitive({
6✔
319
                        ...this._style?.areaDrawing,
320
                        coordinates: [...coordinates],
321
                        geodesic
322
                    }));
323
                    coordinates.forEach((position, idx) => {
6✔
324
                        if (coordinates[idx + 1]) {
24✔
325
                            // add a transparent line to improve interaction
326
                            this._staticPrimitivesCollection.add(createPolylinePrimitive({
18✔
327
                                ...this._style?.lineDrawing,
328
                                dashLength: undefined,
329
                                opacity: 0.01,
330
                                coordinates: [
331
                                    position,
332
                                    coordinates[idx + 1]
333
                                ],
334
                                allowPicking: true,
335
                                id: `${newFeature?.id}:${idx}:segment`,
336
                                geodesic
337
                            }));
338
                            this._staticPrimitivesCollection.add(createPolylinePrimitive({
18✔
339
                                ...this._style?.lineDrawing,
340
                                coordinates: [
341
                                    position,
342
                                    coordinates[idx + 1]
343
                                ],
344
                                allowPicking: false,
345
                                geodesic
346
                            }));
347
                        }
348
                        this._staticBillboardCollection.add({
24✔
349
                            id: `${newFeature?.id}:${idx}:vertex`,
350
                            position,
351
                            image: this._coordinateNodeImage,
352
                            color: getCesiumColor({
353
                                ...this._style?.coordinatesNode
354
                            }),
355
                            disableDepthTestDistance: Number.POSITIVE_INFINITY
356
                        });
357
                    });
358
                }
359
                break;
6✔
360
            default:
361
                break;
×
362
            }
363
        }
364
        this._map.scene.requestRender();
30✔
365
    }
366
    _getPrimitiveFeatureId(objectId) {
367
        return (isString(objectId) ? (objectId || '').split(':') : [])[0];
36!
368
    }
369
    _getIntersectedInfo(movement) {
370
        const { cartesian, cartographic, intersected } = this._getPositionInfo(movement);
20✔
371
        const { id, primitive } = intersected.find((object) => {
20✔
372
            const primitiveFeatureId = this._getPrimitiveFeatureId(object.id);
16✔
373
            const selected = primitiveFeatureId && this._features.find((feature) => feature?.id === primitiveFeatureId);
16✔
374
            if (selected && object.primitive instanceof Cesium.Billboard) {
16✔
375
                return true;
10✔
376
            }
377
            if (selected && object.primitive instanceof Cesium.Primitive) {
6!
378
                return true;
6✔
379
            }
380
            if (selected && object.primitive instanceof Cesium.GroundPolylinePrimitive) {
×
381
                return true;
×
382
            }
383
            return false;
×
384
        }) || {};
385
        return { cartesian, cartographic, primitive, id };
20✔
386
    }
387
    _normalizeGeoJSONResult(callback) {
388
        const newFeatures = [
11✔
389
            ...(this._geojson?.type === 'Feature'
11✔
390
                ? [this._geojson]
391
                : this._geojson.features)
392
        ].map(callback);
393
        return this._geojson?.type === 'Feature'
11✔
394
            ? newFeatures[0]
395
            : { type: 'FeatureCollection', features: newFeatures };
396
    }
397
    _handleOnEditEnd(newFeature) {
398
        const newGeoJSON = this._normalizeGeoJSONResult(feature => feature.id === newFeature?.id
12✔
399
            ? {
400
                ...feature,
401
                geometry: newFeature.geometry,
402
                properties: this._modifyPropertiesToFeatureProperties(newFeature?.properties, feature)
403
            }
404
            : feature);
405
        return this._onEditEnd(newGeoJSON);
11✔
406
    }
407
    _getSingleLinStringGeometry() {
408
        if (this._features?.length !== 1) {
4!
409
            return false;
×
410
        }
411
        return this._features[0]?.geometry?.type === 'LineString'
4!
412
            ? this._features[0]
413
            : false;
414
    }
415
    _handleMouseMove(movement) {
416
        this._dynamicPrimitivesCollection.removeAll();
×
417
        this._dynamicBillboardCollection.removeAll();
×
418
        const intersectedInfo = this._getIntersectedInfo(movement);
×
419
        const { primitive, cartesian } = intersectedInfo;
×
420
        const primitiveFeatureId = this._getPrimitiveFeatureId(this._editing ? this._editing.id : intersectedInfo.id);
×
421
        const feature = primitiveFeatureId && this._features.find(({ id }) => id === primitiveFeatureId);
×
422
        const { geometryType, radius, geodesic } = feature?.properties || {};
×
423

424
        if (this._editing) {
×
425
            if (['Point', 'Circle'].includes(geometryType) && this._editing.primitive instanceof Cesium.Billboard) {
×
426
                this._dynamicBillboardCollection.add({
×
427
                    position: cartesian,
428
                    image: this._coordinateNodeImage,
429
                    color: getCesiumColor({
430
                        ...this._style?.cursor
431
                    }),
432
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
433
                });
434
                if (geometryType === 'Circle') {
×
435
                    this._dynamicPrimitivesCollection.add(geodesic
×
436
                        ? createEllipsePrimitive({
437
                            ...this._style?.area,
438
                            coordinates: cartesian,
439
                            radius,
440
                            clampToGround: true
441
                        })
442
                        : createCylinderPrimitive({
443
                            ...this._style?.areaDrawing,
444
                            coordinates: cartesian,
445
                            radius
446
                        })
447
                    );
448
                }
449
                if (geodesic) {
×
450
                    const geodesicCoordinatesZero = computeGeodesicCoordinates([cartesian]);
×
451
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, coordinates: [geodesicCoordinatesZero[0], cartesian] }));
×
452
                    this._dynamicBillboardCollection.add({
×
453
                        position: geodesicCoordinatesZero[0],
454
                        image: this._coordinateNodeImage,
455
                        color: getCesiumColor({
456
                            ...this._style?.coordinatesNode
457
                        }),
458
                        disableDepthTestDistance: Number.POSITIVE_INFINITY
459
                    });
460
                }
461
            }
462
            if (geometryType === 'Circle' && (this._editing.primitive instanceof Cesium.GroundPolylinePrimitive || this._editing.primitive instanceof Cesium.Primitive)) {
×
463
                const center = Cesium.Cartographic.toCartesian(
×
464
                    new Cesium.Cartographic(
465
                        Cesium.Math.toRadians(feature.geometry.coordinates[0]),
466
                        Cesium.Math.toRadians(feature.geometry.coordinates[1]),
467
                        feature.geometry.coordinates[2] ?? 0
×
468
                    )
469
                );
470
                const newRadius = computeDistance([center, cartesian], geodesic);
×
471
                if (geodesic) {
×
472
                    const geodesicCoordinates = computeGeodesicCoordinates([center, cartesian], cartographic => cartographic[cartographic.length - 1]?.height);
×
473
                    const geodesicCoordinatesZero = computeGeodesicCoordinates([center, cartesian]);
×
474
                    this._dynamicPrimitivesCollection.add(createEllipsePrimitive({
×
475
                        ...this._style?.area,
476
                        coordinates: center,
477
                        radius: newRadius,
478
                        clampToGround: true
479
                    }));
480
                    this._dynamicPrimitivesCollection.add(createEllipsePolylinePrimitive({
×
481
                        ...this._style?.wireframe,
482
                        coordinates: geodesicCoordinates[0],
483
                        radius: newRadius,
484
                        geodesic: true
485
                    }));
486
                    this._dynamicPrimitivesCollection.add(createEllipsePolylinePrimitive({
×
487
                        ...this._style?.lineDrawing,
488
                        coordinates: geodesicCoordinatesZero[0],
489
                        radius: newRadius,
490
                        geodesic: true
491
                    }));
492
                    this._dynamicBillboardCollection.add({
×
493
                        position: center,
494
                        image: this._cursorImage,
495
                        color: getCesiumColor({
496
                            ...this._style?.cursor
497
                        }),
498
                        disableDepthTestDistance: Number.POSITIVE_INFINITY
499
                    });
500
                    this._dynamicBillboardCollection.add({
×
501
                        position: geodesicCoordinates[0],
502
                        image: this._cursorImage,
503
                        color: getCesiumColor({
504
                            ...this._style?.cursor
505
                        }),
506
                        disableDepthTestDistance: Number.POSITIVE_INFINITY
507
                    });
508
                    this._dynamicBillboardCollection.add({
×
509
                        position: geodesicCoordinatesZero[0],
510
                        image: this._cursorImage,
511
                        color: getCesiumColor({
512
                            ...this._style?.cursor
513
                        }),
514
                        disableDepthTestDistance: Number.POSITIVE_INFINITY
515
                    });
516
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({
×
517
                        ...this._style?.wireframe,
518
                        geodesic: true,
519
                        coordinates: [
520
                            center,
521
                            geodesicCoordinatesZero[0],
522
                            geodesicCoordinatesZero[1],
523
                            geodesicCoordinates[1],
524
                            geodesicCoordinates[0]
525
                        ]
526
                    }));
527
                } else {
528
                    this._dynamicPrimitivesCollection.add(createCylinderPolylinePrimitive({
×
529
                        ...this._style?.lineDrawing,
530
                        coordinates: center,
531
                        radius: newRadius
532
                    }));
533
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({
×
534
                        ...this._style?.wireframe,
535
                        coordinates: [center, cartesian]
536
                    }));
537
                }
538
            }
539
            if (['LineString', 'Polygon'].includes(geometryType) && this._editing.primitive instanceof Cesium.Billboard) {
×
540
                const index = parseFloat(this._editing.id.split(':')[1]);
×
541
                const coordinates = featureToCartesianCoordinates(geometryType, feature);
×
542
                const updatedCoordinates = [
×
543
                    ...(coordinates[index - 1]
×
544
                        ? [coordinates[index - 1]]
545
                        : geometryType === 'Polygon'
×
546
                            ? [coordinates[coordinates.length - 2]]
547
                            : []),
548
                    cartesian,
549
                    ...(coordinates[index + 1]
×
550
                        ? [coordinates[index + 1]]
551
                        : geometryType === 'Polygon'
×
552
                            ? [coordinates[1]]
553
                            : [])];
554
                this._dynamicBillboardCollection.add({
×
555
                    position: cartesian,
556
                    image: this._coordinateNodeImage,
557
                    color: getCesiumColor({
558
                        ...this._style?.cursor
559
                    }),
560
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
561
                });
562
                if (geodesic) {
×
563
                    const { height } = Cesium.Cartographic.fromCartesian(cartesian);
×
564
                    const geodesicCoordinates = computeGeodesicCoordinates(updatedCoordinates, () => height);
×
565
                    const geodesicCoordinatesZero = computeGeodesicCoordinates(updatedCoordinates);
×
566
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, geodesic, coordinates: [...geodesicCoordinates] }));
×
567
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.lineDrawing, geodesic, coordinates: [...geodesicCoordinatesZero] }));
×
568
                    geodesicCoordinates.forEach((coord, idx) => {
×
569
                        this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, geodesic, coordinates: [geodesicCoordinatesZero[idx], geodesicCoordinates[idx]] }));
×
570
                    });
571
                } else {
572
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({
×
573
                        ...this._style?.lineDrawing,
574
                        coordinates: updatedCoordinates
575
                    }));
576
                }
577
            }
578
            if (['LineString', 'Polygon'].includes(geometryType) && this._editing.primitive instanceof Cesium.Primitive) {
×
579
                const index = parseFloat(this._editing.id.split(':')[1]);
×
580
                const coordinates = featureToCartesianCoordinates(geometryType, feature);
×
581
                const updatedCoordinates = [
×
582
                    ...(coordinates[index] ? [coordinates[index]] : []),
×
583
                    cartesian,
584
                    ...(coordinates[index + 1] ? [coordinates[index + 1]] : [])
×
585
                ];
586
                this._dynamicBillboardCollection.add({
×
587
                    position: cartesian,
588
                    image: this._coordinateNodeImage,
589
                    color: getCesiumColor({
590
                        ...this._style?.cursor
591
                    }),
592
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
593
                });
594
                if (geodesic) {
×
595
                    const { height } = Cesium.Cartographic.fromCartesian(cartesian);
×
596
                    const geodesicCoordinates = computeGeodesicCoordinates(updatedCoordinates, () => height);
×
597
                    const geodesicCoordinatesZero = computeGeodesicCoordinates(updatedCoordinates);
×
598
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, geodesic, coordinates: [...geodesicCoordinates] }));
×
599
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.lineDrawing, geodesic, coordinates: [...geodesicCoordinatesZero] }));
×
600
                    geodesicCoordinates.forEach((coord, idx) => {
×
601
                        this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, geodesic, coordinates: [geodesicCoordinatesZero[idx], geodesicCoordinates[idx]] }));
×
602
                    });
603
                } else {
604
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({
×
605
                        ...this._style?.lineDrawing,
606
                        coordinates: [
607
                            ...(coordinates[index] ? [coordinates[index]] : []),
×
608
                            cartesian,
609
                            ...(coordinates[index + 1] ? [coordinates[index + 1]] : [])
×
610
                        ]
611
                    }));
612
                }
613
            }
614
            const singleLinStringGeometry = this._getSingleLinStringGeometry();
×
615
            if (!this._editing.primitive && singleLinStringGeometry && cartesian) {
×
616
                const coordinates = featureToCartesianCoordinates(geometryType, singleLinStringGeometry);
×
617
                const singleLinStringGeometryGeodesic = singleLinStringGeometry?.properties?.geodesic;
×
618
                if (singleLinStringGeometryGeodesic) {
×
619
                    const updatedCoordinates = [
×
620
                        coordinates[coordinates.length - 1],
621
                        cartesian
622
                    ];
623
                    const geodesicCoordinates = computeGeodesicCoordinates(updatedCoordinates, cartographic => cartographic[cartographic.length - 1]?.height);
×
624
                    const geodesicCoordinatesZero = computeGeodesicCoordinates(updatedCoordinates);
×
625
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, geodesic: true, coordinates: [...geodesicCoordinates] }));
×
626
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.lineDrawing, geodesic: true, coordinates: [...geodesicCoordinatesZero] }));
×
627
                    geodesicCoordinates.forEach((coord, idx) => {
×
628
                        this._dynamicPrimitivesCollection.add(createPolylinePrimitive({ ...this._style?.wireframe, geodesic: true, coordinates: [geodesicCoordinatesZero[idx], geodesicCoordinates[idx]] }));
×
629
                    });
630
                } else {
631
                    this._dynamicPrimitivesCollection.add(createPolylinePrimitive({
×
632
                        ...this._style?.lineDrawing,
633
                        coordinates: [
634
                            coordinates[coordinates.length - 1],
635
                            cartesian
636
                        ]
637
                    }));
638
                }
639
                this._dynamicBillboardCollection.add({
×
640
                    position: cartesian,
641
                    image: this._coordinateNodeImage,
642
                    color: getCesiumColor({
643
                        ...this._style?.cursor
644
                    }),
645
                    disableDepthTestDistance: Number.POSITIVE_INFINITY
646
                });
647
            }
648
            this._map.scene.requestRender();
×
649
            return null;
×
650
        }
651

652
        if (primitive instanceof Cesium.Billboard) {
×
653
            this._dynamicBillboardCollection.add({
×
654
                position: primitive.position,
655
                image: this._cursorImage,
656
                color: getCesiumColor({
657
                    ...this._style?.cursor
658
                }),
659
                disableDepthTestDistance: Number.POSITIVE_INFINITY
660
            });
661
        }
662
        if (primitive instanceof Cesium.Primitive
×
663
        || primitive instanceof Cesium.GroundPolylinePrimitive) {
664
            this._dynamicBillboardCollection.add({
×
665
                position: cartesian,
666
                image: this._cursorImage,
667
                color: getCesiumColor({
668
                    ...this._style?.cursor
669
                }),
670
                disableDepthTestDistance: Number.POSITIVE_INFINITY
671
            });
672
        }
673
        this._map.scene.requestRender();
×
674
        return null;
×
675
    }
676
    _handleEdit(movement, close) {
677
        const intersectedInfo = this._getIntersectedInfo(movement);
20✔
678
        const primitiveFeatureId = this._getPrimitiveFeatureId(this._editing ? this._editing.id : intersectedInfo.id);
20✔
679
        const feature = primitiveFeatureId && this._features.find(({ id }) => id === primitiveFeatureId);
20✔
680
        const { geometryType, geodesic } = feature?.properties || {};
20✔
681
        if (this._editing) {
20✔
682
            this._dynamicPrimitivesCollection.removeAll();
11✔
683
            this._dynamicBillboardCollection.removeAll();
11✔
684
            const editingPrimitive = this._editing.primitive;
11✔
685
            const editingId = this._editing.id;
11✔
686
            this._editing = false;
11✔
687
            const { cartographic, cartesian } = intersectedInfo;
11✔
688
            const newCoords = cartographic && [
11✔
689
                Cesium.Math.toDegrees(cartographic.longitude),
690
                Cesium.Math.toDegrees(cartographic.latitude),
691
                geodesic ? 0 : cartographic.height
11✔
692
            ];
693
            if (['Circle', 'Point'].includes(geometryType) && newCoords && editingPrimitive instanceof Cesium.Billboard) {
11✔
694
                return this._handleOnEditEnd(
3✔
695
                    updateFeatureCoordinates(
696
                        feature,
697
                        (acc) => [
3✔
698
                            ...acc,
699
                            newCoords
700
                        ])
701
                );
702
            }
703
            if (geometryType === 'Circle' && newCoords && (editingPrimitive instanceof Cesium.GroundPolylinePrimitive || editingPrimitive instanceof Cesium.Primitive)) {
8✔
704
                const center = Cesium.Cartographic.toCartesian(
1✔
705
                    new Cesium.Cartographic(
706
                        Cesium.Math.toRadians(feature.geometry.coordinates[0]),
707
                        Cesium.Math.toRadians(feature.geometry.coordinates[1]),
708
                        feature.geometry.coordinates[2] ?? 0
2✔
709
                    )
710
                );
711
                const newRadius = computeDistance([center, cartesian], geodesic);
1✔
712
                return this._handleOnEditEnd({
1✔
713
                    ...feature,
714
                    properties: {
715
                        ...feature?.properties,
716
                        radius: newRadius
717
                    }
718
                });
719
            }
720
            if (geometryType === 'Polygon' && newCoords && editingPrimitive instanceof Cesium.Billboard) {
7✔
721
                const editingIdx = parseFloat(editingId.split(':')[1]);
1✔
722
                const getIndexes = (coordinates) => (editingIdx === 0 || editingIdx === coordinates.length - 1)
4!
723
                    ? [0, coordinates.length - 1]
724
                    : [editingIdx];
725
                return this._handleOnEditEnd(
1✔
726
                    updateFeatureCoordinates(
727
                        feature,
728
                        (acc, coords, idx, coordinates) => [
4✔
729
                            ...acc,
730
                            getIndexes(coordinates).includes(idx) ? newCoords : coords
4✔
731
                        ])
732
                );
733
            }
734
            if (newCoords && editingPrimitive instanceof Cesium.Billboard) {
6✔
735
                const editingIdx = parseFloat(editingId.split(':')[1]);
1✔
736
                return this._handleOnEditEnd(
1✔
737
                    updateFeatureCoordinates(
738
                        feature,
739
                        (acc, coords, idx) => [
2✔
740
                            ...acc,
741
                            idx === editingIdx ? newCoords : coords
2✔
742
                        ])
743
                );
744
            }
745

746
            if (newCoords && editingPrimitive instanceof Cesium.Primitive) {
5✔
747
                const editingIdx = parseFloat(editingId.split(':')[1]);
2✔
748
                return this._handleOnEditEnd(
2✔
749
                    updateFeatureCoordinates(
750
                        feature,
751
                        (acc, coords, idx) => [
6✔
752
                            ...acc,
753
                            coords,
754
                            ...(idx === editingIdx ? [newCoords] : [])
6✔
755
                        ])
756
                );
757
            }
758
            const singleLinStringGeometry = this._getSingleLinStringGeometry();
3✔
759
            if (newCoords && !editingPrimitive && singleLinStringGeometry) {
3!
760
                const fixedCoords = [
3✔
761
                    newCoords[0],
762
                    newCoords[1],
763
                    singleLinStringGeometry?.properties?.geodesic ? 0 : newCoords[2]
3!
764
                ];
765
                if (close) {
3✔
766
                    return this._handleOnEditEnd(
1✔
767
                        updateFeatureCoordinates(
768
                            singleLinStringGeometry,
769
                            (acc, coords, idx, coordinates) => [
2✔
770
                                ...acc,
771
                                ...(idx < coordinates.length - 1
2✔
772
                                    ? [coords]
773
                                    : [])
774
                            ]
775
                        )
776
                    );
777
                }
778
                this._editing = { id: editingId };
2✔
779
                return this._handleOnEditEnd(
2✔
780
                    updateFeatureCoordinates(
781
                        singleLinStringGeometry,
782
                        (acc, coords, idx, coordinates) => [
4✔
783
                            ...acc,
784
                            coords,
785
                            ...(idx === coordinates.length - 1
4✔
786
                                ? [fixedCoords]
787
                                : [])
788
                        ]
789
                    )
790
                );
791
            }
792
        } else {
793
            this._editing = intersectedInfo;
9✔
794
            if (!intersectedInfo.primitive && !this._getSingleLinStringGeometry()) {
9!
795
                this._editing = false;
×
796
            }
797
        }
798
        return null;
9✔
799
    }
800
}
801

802
export default CesiumModifyGeoJSONInteraction;
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc