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

geosolutions-it / MapStore2 / 19668982994

25 Nov 2025 12:08PM UTC coverage: 76.663% (-0.1%) from 76.758%
19668982994

Pull #11725

github

web-flow
Merge b651fa3a4 into 15cf36181
Pull Request #11725: Disable tests that randomly fail on jenkins CI

32265 of 50209 branches covered (64.26%)

40156 of 52380 relevant lines covered (76.66%)

37.95 hits per line

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

67.69
/web/client/utils/cesium/PrimitivesUtils.js
1
/*
2
 * Copyright 2022, 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
import chroma from 'chroma-js';
11
import EllipseGeometryLibrary from '@cesium/engine/Source/Core/EllipseGeometryLibrary';
12
import CylinderGeometryLibrary from '@cesium/engine/Source/Core/CylinderGeometryLibrary';
13

14
/**
15
 * return a cesium color
16
 * @param {object} options
17
 * @param {string} options.color a color css string (eg. #ff0000)
18
 * @param {number} options.opacity a number between 0 and 1 where 1 is fully opaque
19
 */
20
export const getCesiumColor = ({ color, opacity }) => {
1✔
21
    const [r, g, b, a] = chroma(color).gl();
477✔
22
    if (opacity !== undefined) {
477✔
23
        return new Cesium.Color(r, g, b, opacity);
279✔
24
    }
25
    return new Cesium.Color(r, g, b, a);
198✔
26
};
27

28
/**
29
 * create a polyline primitive
30
 */
31
export const createPolylinePrimitive = ({
1✔
32
    coordinates,
33
    width = 4,
2✔
34
    color = '#ff00ff',
2✔
35
    opacity = 1.0,
2✔
36
    depthFailColor,
37
    depthFailOpacity,
38
    dashLength,
39
    clampToGround,
40
    allowPicking = false,
48✔
41
    geodesic,
42
    id,
43
    modelMatrix
44
}) => {
45
    return new Cesium[clampToGround ? 'GroundPolylinePrimitive' : 'Primitive']({
115!
46
        geometryInstances: new Cesium.GeometryInstance({
47
            id,
48
            geometry: clampToGround
115!
49
                ? new Cesium.GroundPolylineGeometry({
50
                    positions: [...coordinates],
51
                    width
52
                })
53
                : new Cesium.PolylineGeometry({
54
                    positions: [...coordinates],
55
                    width,
56
                    arcType: geodesic ? Cesium.ArcType.GEODESIC : Cesium.ArcType.NONE
115✔
57
                }),
58
            modelMatrix
59
        }),
60
        appearance: new Cesium.PolylineMaterialAppearance({
61
            material: !dashLength
115✔
62
                ? Cesium.Material.fromType('Color', {
63
                    color: getCesiumColor({
64
                        color: color,
65
                        opacity
66
                    })
67
                })
68
                : Cesium.Material.fromType('PolylineDash', {
69
                    color: getCesiumColor({
70
                        color: color,
71
                        opacity
72
                    }),
73
                    dashLength
74
                })
75
        }),
76
        ...(depthFailColor && {
227✔
77
            depthFailAppearance: new Cesium.PolylineMaterialAppearance({
78
                material: !dashLength
112✔
79
                    ? Cesium.Material.fromType('Color', {
80
                        color: getCesiumColor({
81
                            color: depthFailColor,
82
                            opacity: depthFailOpacity
83
                        })
84
                    })
85
                    : Cesium.Material.fromType('PolylineDash', {
86
                        color: getCesiumColor({
87
                            color: depthFailColor,
88
                            opacity: depthFailOpacity
89
                        }),
90
                        dashLength
91
                    })
92
            })
93
        }),
94
        allowPicking,
95
        asynchronous: false
96
    });
97
};
98

99
/**
100
 * create a polygon primitive
101
 */
102
export const createPolygonPrimitive = ({
1✔
103
    id,
104
    coordinates,
105
    color = '#ff00ffAA',
1✔
106
    opacity = 1.0,
1✔
107
    depthFailColor,
108
    depthFailOpacity,
109
    allowPicking = false,
20✔
110
    geodesic
111
}) => {
112
    return new Cesium.Primitive({
20✔
113
        geometryInstances: new Cesium.GeometryInstance({
114
            id,
115
            geometry: new Cesium.PolygonGeometry({
116
                polygonHierarchy: new Cesium.PolygonHierarchy([...coordinates]),
117
                perPositionHeight: !geodesic
118
            })
119
        }),
120
        appearance: new Cesium.MaterialAppearance({
121
            material: Cesium.Material.fromType('Color', {
122
                color: getCesiumColor({
123
                    color,
124
                    opacity
125
                })
126
            }),
127
            faceForward: true
128
        }),
129
        ...(depthFailColor && {
39✔
130
            depthFailAppearance: new Cesium.MaterialAppearance({
131
                material: Cesium.Material.fromType('Color', {
132
                    color: getCesiumColor({
133
                        color: depthFailColor,
134
                        opacity: depthFailOpacity
135
                    })
136
                }),
137
                faceForward: true
138
            })
139
        }),
140
        allowPicking,
141
        asynchronous: false
142
    });
143
};
144

145
/**
146
 * create an ellipse polyline primitive
147
 */
148
export const createEllipsePolylinePrimitive = ({
1✔
149
    width = 4,
1✔
150
    coordinates,
151
    color = '#ff00ff',
1✔
152
    opacity = 1.0,
1✔
153
    depthFailColor,
154
    depthFailOpacity,
155
    dashLength,
156
    clampToGround,
157
    allowPicking = false,
1✔
158
    id,
159
    radius,
160
    semiMajorAxis,
161
    semiMinorAxis,
162
    geodesic
163
}) => {
164
    const { outerPositions } = EllipseGeometryLibrary.computeEllipsePositions({
13✔
165
        granularity: 0.02,
166
        semiMajorAxis: radius || semiMajorAxis,
13!
167
        semiMinorAxis: radius || semiMinorAxis,
13!
168
        rotation: 0,
169
        center: coordinates
170
    }, false, true);
171
    const ellipseCoordinates = Cesium.Cartesian3.unpackArray(outerPositions);
13✔
172
    return createPolylinePrimitive({
13✔
173
        coordinates: [...ellipseCoordinates, ellipseCoordinates[0]],
174
        width,
175
        color,
176
        opacity,
177
        depthFailColor,
178
        depthFailOpacity,
179
        dashLength,
180
        clampToGround,
181
        allowPicking,
182
        id,
183
        geodesic
184
    });
185
};
186

187
/**
188
 * create an ellipse primitive
189
 */
190
export const createEllipsePrimitive = ({
1✔
191
    id,
192
    coordinates,
193
    color = '#ff00ffAA',
×
194
    opacity = 1.0,
×
195
    depthFailColor,
196
    depthFailOpacity,
197
    allowPicking = false,
6✔
198
    clampToGround,
199
    radius,
200
    semiMajorAxis,
201
    semiMinorAxis
202
}) => {
203
    return new Cesium[clampToGround ? 'GroundPrimitive' : 'Primitive']({
6!
204
        geometryInstances: new Cesium.GeometryInstance({
205
            id,
206
            geometry: new Cesium.EllipseGeometry({
207
                center: coordinates,
208
                semiMajorAxis: radius || semiMajorAxis,
6!
209
                semiMinorAxis: radius || semiMinorAxis
6!
210
            })
211
        }),
212
        appearance: new Cesium.MaterialAppearance({
213
            material: Cesium.Material.fromType('Color', {
214
                color: getCesiumColor({
215
                    color,
216
                    opacity
217
                })
218
            }),
219
            faceForward: true
220
        }),
221
        ...(depthFailColor && {
12✔
222
            depthFailAppearance: new Cesium.MaterialAppearance({
223
                material: Cesium.Material.fromType('Color', {
224
                    color: getCesiumColor({
225
                        color: depthFailColor,
226
                        opacity: depthFailOpacity
227
                    })
228
                }),
229
                faceForward: true
230
            })
231
        }),
232
        allowPicking,
233
        asynchronous: false
234
    });
235
};
236

237
/**
238
 * create a cylinder primitive
239
 */
240
export const createCylinderPrimitive = ({
1✔
241
    id,
242
    coordinates,
243
    color = '#ff00ffAA',
×
244
    opacity = 1.0,
×
245
    depthFailColor,
246
    depthFailOpacity,
247
    allowPicking = false,
×
248
    length,
249
    radius,
250
    topRadius,
251
    bottomRadius
252
}) => {
253
    return new Cesium.Primitive({
×
254
        geometryInstances: new Cesium.GeometryInstance({
255
            id,
256
            geometry: new Cesium.CylinderGeometry({
257
                length: length ?? 0.1,
×
258
                topRadius: topRadius ?? radius,
×
259
                bottomRadius: bottomRadius ?? radius
×
260
            }),
261
            modelMatrix: Cesium.Matrix4.multiplyByTranslation(
262
                Cesium.Transforms.eastNorthUpToFixedFrame(
263
                    coordinates
264
                ),
265
                new Cesium.Cartesian3(0, 0, 0),
266
                new Cesium.Matrix4()
267
            )
268
        }),
269
        appearance: new Cesium.MaterialAppearance({
270
            material: Cesium.Material.fromType('Color', {
271
                color: getCesiumColor({
272
                    color,
273
                    opacity
274
                })
275
            }),
276
            faceForward: true
277
        }),
278
        ...(depthFailColor && {
×
279
            depthFailAppearance: new Cesium.MaterialAppearance({
280
                material: Cesium.Material.fromType('Color', {
281
                    color: getCesiumColor({
282
                        color: depthFailColor,
283
                        opacity: depthFailOpacity
284
                    })
285
                }),
286
                faceForward: true
287
            })
288
        }),
289
        allowPicking,
290
        asynchronous: false
291
    });
292
};
293

294
/**
295
 * create a cylinder polyline primitive
296
 */
297
export const createCylinderPolylinePrimitive = ({
1✔
298
    id,
299
    coordinates,
300
    color = '#ff00ffAA',
×
301
    opacity = 1.0,
×
302
    depthFailColor,
303
    depthFailOpacity,
304
    allowPicking = false,
×
305
    length,
306
    radius,
307
    dashLength,
308
    clampToGround,
309
    geodesic,
310
    width,
311
    slices = 128,
×
312
    topRadius,
313
    bottomRadius
314
}) => {
315
    const modelMatrix = Cesium.Matrix4.multiplyByTranslation(
×
316
        Cesium.Transforms.eastNorthUpToFixedFrame(
317
            coordinates
318
        ),
319
        new Cesium.Cartesian3(0, 0, 0),
320
        new Cesium.Matrix4()
321
    );
322
    const positions = CylinderGeometryLibrary.computePositions(
×
323
        length ?? 0.0,
×
324
        topRadius ?? radius,
×
325
        bottomRadius ?? radius,
×
326
        slices,
327
        false
328
    );
329
    const cylinderGeometryCoordinates = Cesium.Cartesian3.unpackArray(positions);
×
330
    return createPolylinePrimitive({
×
331
        coordinates: !length
×
332
            ? [...cylinderGeometryCoordinates.splice(0, Math.ceil(cylinderGeometryCoordinates.length / 2)), cylinderGeometryCoordinates[0]]
333
            : cylinderGeometryCoordinates,
334
        width,
335
        color,
336
        opacity,
337
        depthFailColor,
338
        depthFailOpacity,
339
        dashLength,
340
        clampToGround,
341
        allowPicking,
342
        id,
343
        geodesic,
344
        modelMatrix
345
    });
346
};
347

348
export const clearPrimitivesCollection = (map, primitivesCollection) => {
1✔
349
    if (map?.scene?.primitives && primitivesCollection && !primitivesCollection.isDestroyed()) {
×
350
        primitivesCollection.removeAll();
×
351
        // remove destroys the primitive collection so we don't need to explicitly use primitivesCollection.destroy()
352
        map.scene.primitives.remove(primitivesCollection);
×
353
    }
354
};
355

356
/**
357
 * return a canvas with a circle drawn on it that can be used as billboard image
358
 * @param {object} size size of the canvas
359
 * @param {object} options
360
 * @param {string} options.fill color css string (eg. #ff0000)
361
 * @param {string} options.stroke color css string (eg. #ff0000)
362
 * @param {number} options.strokeWidth width of the stroke in pixel
363
 */
364
export const createCircleMarkerImage = (size, { stroke, strokeWidth = 1, fill = '#ffffff' } = {}) => {
1✔
365
    const fullSize = stroke ? size + strokeWidth * 2 : size;
74✔
366
    const canvas = document.createElement('canvas');
74✔
367
    canvas.setAttribute('width', fullSize);
74✔
368
    canvas.setAttribute('height', fullSize);
74✔
369
    const ctx = canvas.getContext('2d');
74✔
370
    ctx.beginPath();
74✔
371
    if (fill) { ctx.fillStyle = fill; }
74✔
372
    if (stroke) {
74✔
373
        ctx.strokeStyle = stroke;
73✔
374
        ctx.lineWidth = strokeWidth;
73✔
375
    }
376
    ctx.arc(fullSize / 2, fullSize / 2, size / 2, 0, 2 * Math.PI);
74✔
377
    if (fill) { ctx.fill(); }
74✔
378
    if (stroke) { ctx.stroke(); }
74✔
379
    return canvas;
74✔
380
};
381

382

383
/**
384
 * Creates a collection of Cesium ClippingPolygon objects from a GeoJSON polygon
385
 * @param {Object} clippingPolygon - GeoJSON polygon object
386
 * @returns {Array} - Array of Cesium.ClippingPolygon objects
387
 */
388
export const createClippingPolygonsFromGeoJSON = (clippingPolygon) => {
1✔
389
    const polygons = [];
1✔
390
    const coordinates = clippingPolygon?.geometry?.coordinates?.[0] || [];
1!
391
    if (coordinates.length > 0) {
1!
392
        const positions = coordinates.map((coord) => {
1✔
393
            const [lng, lat, height = 0] = coord;
5✔
394
            return Cesium.Cartesian3.fromDegrees(lng, lat, height);
5✔
395
        });
396
        const clippedPolygon = new Cesium.ClippingPolygon({
1✔
397
            positions: positions
398
        });
399
        polygons.push(clippedPolygon);
1✔
400
    }
401

402
    return polygons;
1✔
403
};
404

405
/**
406
 * Applies clipping polygons to a Cesium object (globe or tileset)
407
 * @param {Object} options - Configuration options
408
 * @param {Object} options.target - The target object to apply clipping to (globe or tileset)
409
 * @param {Array} options.polygons - Array of Cesium.ClippingPolygon objects
410
 * @param {Boolean} options.inverse - Whether to inverse the clipping (clip outside instead of inside)
411
 * @param {Object} options.scene - Cesium scene object for rendering
412
 * @param {Object} [options.additionalProperties] - Additional properties to set on the target (optional)
413
 */
414
export const applyClippingPolygons = ({ target, polygons, inverse, scene, additionalProperties = {} }) => {
1!
415
    if (!target || !polygons || !Array.isArray(polygons)) {
×
416
        return;
×
417
    }
418
    if (polygons.length && !target.clippingPolygons) {
×
419
        target.clippingPolygons = new Cesium.ClippingPolygonCollection({
×
420
            polygons: polygons,
421
            enabled: true,
422
            inverse: !!inverse
423
        });
424
    }
425
    if (target.clippingPolygons) {
×
426
        target.clippingPolygons.removeAll();
×
427
        polygons.forEach((polygon) => {
×
428
            target.clippingPolygons.add(polygon);
×
429
        });
430
        target.clippingPolygons.inverse = !!inverse;
×
431
        Object.entries(additionalProperties).forEach(([key, value]) => {
×
432
            target[key] = value;
×
433
        });
434
        if (scene) {
×
435
            scene.requestRender();
×
436
        }
437
    }
438
};
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