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

geosolutions-it / MapStore2 / 19760243687

27 Nov 2025 09:59AM UTC coverage: 76.665% (-0.3%) from 76.929%
19760243687

Pull #11119

github

web-flow
Fix: #11712 Support for template format on vector layers to visualize embedded conent (#11720)
Pull Request #11119: Layer Selection Plugin on ArcGIS, WFS & WMS layers

32266 of 50209 branches covered (64.26%)

7 of 13 new or added lines in 2 files covered. (53.85%)

3017 existing lines in 248 files now uncovered.

40157 of 52380 relevant lines covered (76.66%)

37.82 hits per line

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

83.7
/web/client/utils/openlayers/DrawGeometryInteraction.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 Draw from 'ol/interaction/Draw';
10
import VectorSource from 'ol/source/Vector';
11
import VectorLayer from 'ol/layer/Vector';
12
import GeoJSON from 'ol/format/GeoJSON';
13
import { Point, Polygon, LineString, Circle } from 'ol/geom';
14
import {circular} from 'ol/geom/Polygon';
15
import {getDistance} from 'ol/sphere';
16
import {transform} from 'ol/proj';
17
import Feature from 'ol/Feature';
18
import { squaredDistance } from 'ol/coordinate';
19
import Style from 'ol/style/Style';
20
import CircleStyle from 'ol/style/Circle';
21
import Fill from 'ol/style/Fill';
22
import Stroke from 'ol/style/Stroke';
23
import tinycolor from 'tinycolor2';
24
import { never } from 'ol/events/condition';
25
import { transformLineToArcs, reproject } from '../CoordinatesUtils';
26
import { generateEditingStyle } from '../DrawUtils';
27

28
const geoJSON = new GeoJSON();
1✔
29

30
const geodesicGeometryFunction = {
1✔
31
    'Circle': ({ map, onDrawing }) => (coordinates, geometry, projection) => {
1✔
32
        let _geometry = geometry;
3✔
33
        if (!_geometry) {
3✔
34
            _geometry = new Polygon([]);
2✔
35
            _geometry.set('@geometry', new Point([]));
2✔
36
        }
37
        const projectionCode = projection ? projection.getCode() : map.getView().getProjection().getCode();
3!
38
        const center = transform(coordinates[0], projectionCode, 'EPSG:4326');
3✔
39
        const last = transform(coordinates[1], projectionCode, 'EPSG:4326');
3✔
40
        const radius = getDistance(center, last);
3✔
41
        const circle = circular(center, radius, 128);
3✔
42
        circle.transform('EPSG:4326', projectionCode);
3✔
43
        _geometry.setCoordinates(circle.getCoordinates());
3✔
44
        _geometry.get('@geometry').setCoordinates(coordinates[0]);
3✔
45
        _geometry.set('@properties', { radius, geodesic: true });
3✔
46
        onDrawing({
3✔
47
            coordinates: [...coordinates]
48
        });
49
        return _geometry;
3✔
50
    },
51
    'LineString': ({ map, onDrawing }) => (coordinates, geometry, projection) => {
1✔
52
        let _geometry = geometry;
5✔
53
        if (!_geometry) {
5✔
54
            _geometry = new LineString([]);
1✔
55
            _geometry.set('@geometry', new LineString([]));
1✔
56
        }
57
        const projectionCode = projection ? projection.getCode() : map.getView().getProjection().getCode();
5!
58
        const geodesicCoordinates = transformLineToArcs(coordinates.map(c => {
5✔
59
            const point = reproject(c, projectionCode, 'EPSG:4326');
12✔
60
            return [point.x, point .y];
12✔
61
        }));
62
        _geometry.setCoordinates(geodesicCoordinates.map(c => {
5✔
63
            const point = reproject(c, 'EPSG:4326', projectionCode);
400✔
64
            return [point.x, point .y];
400✔
65
        }));
66
        _geometry.get('@geometry').setCoordinates(coordinates);
5✔
67
        _geometry.set('@properties', { geodesic: true });
5✔
68
        onDrawing({
5✔
69
            coordinates: [...coordinates]
70
        });
71
        return _geometry;
5✔
72
    },
73
    'Polygon': ({ map, onDrawing }) => (coordinates, geometry, projection) => {
1✔
74
        let _geometry = geometry;
7✔
75
        if (!_geometry) {
7✔
76
            _geometry = new Polygon([]);
1✔
77
            _geometry.set('@geometry', new Polygon([]));
1✔
78
        }
79
        const projectionCode = projection ? projection.getCode() : map.getView().getProjection().getCode();
7!
80
        let _coordinates = coordinates[0].length
7!
81
            ? [[...coordinates[0], coordinates[0][0]]]
82
            : [];
83
        const geodesicCoordinates = transformLineToArcs(_coordinates[0].map(c => {
7✔
84
            const point = reproject(c, projectionCode, 'EPSG:4326');
28✔
85
            return [point.x, point .y];
28✔
86
        }));
87
        _geometry.setCoordinates([geodesicCoordinates.map(c => {
7✔
88
            const point = reproject(c, 'EPSG:4326', projectionCode);
1,600✔
89
            return [point.x, point .y];
1,600✔
90
        })]);
91
        _geometry.get('@geometry').setCoordinates(_coordinates);
7✔
92
        _geometry.set('@properties', { geodesic: true });
7✔
93
        onDrawing({
7✔
94
            coordinates: [..._coordinates]
95
        });
96
        return _geometry;
7✔
97
    }
98
};
99

100
const defaultGeometryFunction = {
1✔
101
    'Point': ({ onDrawing }) => (coordinates, geometry) => {
2✔
102
        let _geometry = geometry;
2✔
103
        if (!_geometry) {
2!
104
            _geometry = new Point([]);
2✔
105
        }
106
        _geometry.setCoordinates(coordinates);
2✔
107
        onDrawing({
2✔
108
            coordinates: [...coordinates]
109
        });
110
        return _geometry;
2✔
111
    },
112
    'Circle': ({ onDrawing }) => (coordinates, geometry) => {
1✔
113
        let _geometry = geometry;
3✔
114
        if (!_geometry) {
3✔
115
            _geometry = new Circle([]);
2✔
116
            _geometry.set('@geometry', new Point([]));
2✔
117
        }
118
        const squaredLength = squaredDistance( coordinates[0], coordinates[1] );
3✔
119
        const radius = Math.sqrt(squaredLength);
3✔
120
        _geometry.setCenterAndRadius(coordinates[0], radius);
3✔
121
        _geometry.get('@geometry').setCoordinates(coordinates[0]);
3✔
122
        _geometry.set('@properties', { radius });
3✔
123
        onDrawing({
3✔
124
            coordinates: [...coordinates]
125
        });
126
        return _geometry;
3✔
127
    },
128
    'LineString': ({ onDrawing }) => (coordinates, geometry) => {
2✔
129
        let _geometry = geometry;
11✔
130
        if (!_geometry) {
11✔
131
            _geometry = new LineString([]);
2✔
132
        }
133
        _geometry.setCoordinates(coordinates);
11✔
134
        onDrawing({
11✔
135
            coordinates: [...coordinates]
136
        });
137
        return _geometry;
11✔
138
    },
139
    'Polygon': ({ onDrawing }) => (coordinates, geometry) => {
2✔
140
        let _geometry = geometry;
13✔
141
        if (!_geometry) {
13✔
142
            _geometry = new Polygon([]);
2✔
143
        }
144
        let _coordinates = coordinates[0].length
13!
145
            ? [[...coordinates[0], coordinates[0][0]]]
146
            : [];
147
        _geometry.setCoordinates(_coordinates);
13✔
148
        onDrawing({
13✔
149
            coordinates: [..._coordinates]
150
        });
151
        return _geometry;
13✔
152
    }
153
};
154

155
function getColor(color, opacity) {
NEW
156
    if (opacity === undefined) {
×
NEW
157
        return color;
×
158
    }
NEW
159
    const { r, g, b } = tinycolor(color).toRgb();
×
NEW
160
    return `rgba(${[r, g, b, opacity].join(', ')})`;
×
161
}
162

163
/**
164
 * Class to manage all the drawing interaction of OpenLayers library
165
 * @param {string} options.type type of drawing, one of: `Point`, `LineString`, `Polygon` or `Circle`
166
 * @param {object} options.map a Cesium map instance
167
 * @param {number} options.coordinatesLength maximum count of drawing coordinates
168
 * @param {object} options.style style for drawing geometries, see the `web/client/DrawUtils.js` file
169
 * @param {boolean} options.geodesic if true the geometries height will be forced to the ellipsoid at 0 height
170
 * @param {function} options.onDrawStart triggered on draw start
171
 * @param {function} options.onDrawing triggered while drawing
172
 * @param {function} options.onDrawEnd triggered when the drawing event is completed (double click)
173
 */
174
class OpenLayersDrawGeometryInteraction {
175
    constructor(options = {}) {
×
176
        const geometryType = options.type;
10✔
177
        const coordinatesLength = options.coordinatesLength;
10✔
178
        const geodesic = !!options.geodesic;
10✔
179
        const onDrawStart = options.onDrawStart ? options.onDrawStart : () => {};
10!
180
        const onDrawing = options.onDrawing ? options.onDrawing : () => {};
10!
181
        const onDrawEnd = options.onDrawEnd ? options.onDrawEnd : () => {};
10!
182
        this._map = options.map;
10✔
183
        const style = generateEditingStyle(options.style);
10✔
184
        const source = new VectorSource({ wrapX: false });
10✔
185
        this._layer = new VectorLayer({
10✔
186
            source: source,
187
            zIndex: Infinity,
188
            style: (olFeature) => {
189
                const olGeometryType = olFeature.getGeometry().getType();
×
190
                const lineStyle = new Stroke({
×
191
                    color: getColor(style?.line?.color, style?.line?.opacity),
192
                    width: style?.line?.width,
193
                    ...(style?.line?.dashLength && { lineDash: [style.line.dashLength, style.line.dashLength] })
×
194
                });
195
                const areaStyle = new Fill({
×
196
                    color: getColor(style?.area?.color, style?.area?.opacity)
197
                });
198

199
                if (olGeometryType === 'Point') {
×
200
                    return new Style({
×
201
                        image: new CircleStyle({
202
                            stroke: new Stroke({
203
                                color: getColor(style?.coordinatesNode?.color, style?.coordinatesNode?.opacity),
204
                                width: style?.coordinatesNode?.width
205
                            }),
206
                            radius: style?.coordinatesNode?.radius
207
                        })
208
                    });
209
                }
210
                if (olGeometryType === 'LineString') {
×
211
                    return new Style({
×
212
                        stroke: lineStyle
213
                    });
214
                }
215
                return new Style({
×
216
                    stroke: lineStyle,
217
                    fill: areaStyle
218
                });
219
            }
220
        });
221

222
        const geometryFunction = geodesic && geodesicGeometryFunction[geometryType]
10✔
223
            ? geodesicGeometryFunction[geometryType]
224
            : defaultGeometryFunction[geometryType];
225

226
        this._draw = new Draw({
10✔
227
            source: source,
228
            type: geometryType,
229
            maxPoints: coordinatesLength,
230
            stopClick: true,
231
            freehandCondition: never,
232
            geometryFunction: geometryFunction ? geometryFunction({ map: this._map, onDrawing }) : undefined,
10!
233
            style: (olFeature) => {
234
                const olGeometryType = olFeature.getGeometry().getType();
×
235
                const lineDrawingStyle = new Stroke({
×
236
                    color: getColor(style?.lineDrawing?.color, style?.lineDrawing?.opacity),
237
                    width: style?.lineDrawing?.width,
238
                    ...(style?.lineDrawing?.dashLength && { lineDash: [style.lineDrawing.dashLength, style.lineDrawing.dashLength] })
×
239
                });
240
                const areaDrawingStyle = new Fill({
×
241
                    color: getColor(style?.areaDrawing?.color, style?.areaDrawing?.opacity)
242
                });
243

244
                if (olGeometryType === 'Point') {
×
245
                    return new Style({
×
246
                        image: new CircleStyle({
247
                            stroke: new Stroke({
248
                                color: getColor(style?.cursor?.color, style?.cursor?.opacity),
249
                                width: style?.cursor?.width
250
                            }),
251
                            radius: style?.cursor?.radius
252
                        })
253
                    });
254
                }
255
                if (olGeometryType === 'LineString' && geometryType === 'LineString') {
×
256
                    return new Style({
×
257
                        stroke: lineDrawingStyle
258
                    });
259
                }
NEW
260
                if ((olGeometryType === 'Polygon' && ['Polygon', 'Circle'].includes(geometryType))
×
261
                || olGeometryType === 'Circle') {
262
                    return new Style({
×
263
                        stroke: lineDrawingStyle,
264
                        fill: areaDrawingStyle
265
                    });
266
                }
267
                return null;
×
268
            }
269
        });
270
        this._draw.on('drawstart', () => {
10✔
271
            onDrawStart();
12✔
272
        });
273
        this._draw.on('drawend', (event) => {
10✔
274
            const properties = event.feature.getGeometry().get('@properties') || { geodesic };
10✔
275
            const feature = event.feature.getGeometry().get('@geometry')
10✔
276
                ? new Feature({ geometry: event.feature.getGeometry().get('@geometry') })
277
                : event.feature;
278
            onDrawEnd({
10✔
279
                feature: {
280
                    ...geoJSON.writeFeatureObject(feature, {
281
                        featureProjection: this._map.getView().getProjection().getCode(),
282
                        dataProjection: 'EPSG:4326'
283
                    }),
284
                    properties
285
                }
286
            });
287
        });
288
        this._map.addInteraction(this._draw);
10✔
289
        this._map.addLayer(this._layer);
10✔
290
    }
291
    getCoordinates() {
292
        return [];
10✔
293
    }
294
    remove() {
295
        if (this._draw) {
10!
296
            this._map.removeInteraction(this._draw);
10✔
297
            this._draw = null;
10✔
298
        }
299
        if (this._layer) {
10!
300
            this._map.removeLayer(this._layer);
10✔
301
            this._layer = null;
10✔
302
        }
303
    }
304
}
305

306
export default OpenLayersDrawGeometryInteraction;
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

© 2026 Coveralls, Inc