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

geosolutions-it / MapStore2 / 14534587011

18 Apr 2025 11:41AM UTC coverage: 76.977% (-0.02%) from 76.993%
14534587011

Pull #11037

github

web-flow
Merge f22d700f6 into 48d6a1a15
Pull Request #11037: Remove object assign pollyfills

30792 of 47937 branches covered (64.23%)

446 of 556 new or added lines in 94 files covered. (80.22%)

8 existing lines in 4 files now uncovered.

38277 of 49725 relevant lines covered (76.98%)

36.07 hits per line

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

49.6
/web/client/components/map/leaflet/DrawSupport.jsx
1
/*
2
 * Copyright 2017, 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
import PropTypes from 'prop-types';
9
import React from 'react';
10
import {last, isNil } from 'lodash';
11
import L from 'leaflet';
12

13
import 'leaflet-draw';
14
import 'leaflet-draw/dist/leaflet.draw.css';
15

16
L.Draw.Polygon.prototype._calculateFinishDistance = function(t) {
1✔
17
    if (this._markers.length > 0) {
×
18
        const firstPoint = this._map.latLngToContainerPoint(this._markers[0].getLatLng());
×
19
        const lastPoint = this._map.latLngToContainerPoint(this._markers[this._markers.length - 1].getLatLng());
×
20

21
        const clickedMarker = new L.Marker(t, {
×
22
            icon: this.options.icon,
23
            zIndexOffset: 2 * this.options.zIndexOffset
24
        });
25
        const clicked = this._map.latLngToContainerPoint(clickedMarker.getLatLng());
×
26
        return Math.min(firstPoint.distanceTo(clicked), lastPoint.distanceTo(clicked));
×
27
    }
28
    return 1 / 0;
×
29
};
30

31
import {isSimpleGeomType, getSimpleGeomType} from '../../../utils/MapUtils';
32
import {boundsToOLExtent} from '../../../utils/leaflet/DrawSupportUtils';
33

34
const {reproject, reprojectBbox, calculateCircleCoordinates, reprojectGeoJson} = require('../../../utils/CoordinatesUtils');
1✔
35

36
const {pointToLayer} = require('../../../utils/leaflet/Vector');
1✔
37

38
const DEG_TO_RAD = Math.PI / 180.0;
1✔
39
/**
40
 * Converts the leaflet circle into the projected circle (usually in 3857)
41
 * @param  {number} mRadius leaflet radius of circle
42
 * @param  {array} center  The center point in EPSG:4326. Array [lng,lat]
43
 * @return {object}        center and radius of the projected circle
44
 */
45
const toProjectedCircle = (mRadius, center, projection) => {
1✔
46
    if (projection === "EPSG:4326") {
×
47
        return {
×
48
            center,
49
            srs: projection,
50
            radius: mRadius
51
        };
52
    }
53

54
    // calculate
55
    const lonRadius = (mRadius / 40075017) * 360 / Math.cos(DEG_TO_RAD * (center[1]));
×
56
    const projCenter = reproject(center, "EPSG:4326", projection);
×
57
    if (lonRadius) {
×
58
        const checkPoint = reproject([center[0] + lonRadius, center[1]], "EPSG:4326", projection);
×
59
        return {
×
60
            center: projCenter,
61
            srs: projection,
62
            radius: Math.sqrt(Math.pow(projCenter.x - checkPoint.x, 2) + Math.pow(projCenter.y - checkPoint.y, 2))
63
        };
64
    }
65
    return {
×
66
        center: projCenter,
67
        srs: projection,
68
        radius: mRadius
69
    };
70

71
};
72

73
/**
74
 * From projected circle into leaflet circle.
75
 * @param  {number} radius                   Projected radius
76
 * @param  {object} center                   `{lng: {number}, lat: {number}}`
77
 * @param  {String} [projection="EPSG:4326"] projection from where to convert
78
 * @return {object}                          center and radius of leaflet circle
79
 */
80
const toLeafletCircle = (radius, center, projection = "EPSG:4326") => {
1!
81
    if (projection === "EPSG:4326" || radius === undefined) {
×
82
        return {
×
83
            center,
84
            projection,
85
            radius
86
        };
87
    }
88
    const leafletCenter = reproject({x: center.lng, y: center.lat}, projection, "EPSG:4326");
×
89
    if (radius === undefined) {
×
90
        return {
×
91
            center: leafletCenter,
92
            projection,
93
            radius
94
        };
95
    }
96
    const checkPoint = reproject([center.lng + radius, center.lat], projection, "EPSG:4326");
×
97

98
    const lonRadius = Math.sqrt(Math.pow(leafletCenter.x - checkPoint.x, 2) + Math.pow(leafletCenter.y - checkPoint.y, 2));
×
99
    const mRadius = lonRadius * Math.cos(DEG_TO_RAD * leafletCenter.y) * 40075017 / 360;
×
100
    return {
×
101
        center: leafletCenter,
102
        projection: "EPSG:4326",
103
        radius: mRadius
104
    };
105
};
106
/**
107
 * Component that allows to draw and edit geometries as (Point, LineString, Polygon, Rectangle, Circle, MultiGeometries)
108
 * @class DrawSupport
109
 * @memberof components.map
110
 * @prop {object} map the map usedto drawing on
111
 * @prop {string} drawOwner the owner of the drawn features
112
 * @prop {string} drawStatus the status that allows to do different things. see UNSAFE_componentWillReceiveProps method
113
 * @prop {string} drawMethod the method used to draw different geometries. can be Circle,BBOX, or a geomType from Point to MultiPolygons
114
 * @prop {object} options it contains the params used to enable the interactions or simply stop the DrawSupport after a ft is drawn
115
 * @prop {object[]} features an array of geojson features used as a starting point for drawing new shapes or edit them
116
 * @prop {func} onChangeDrawingStatus method use to change the status of the DrawSupport
117
 * @prop {func} onGeometryChanged when a features is edited or drawn this methos is fired
118
 * @prop {func} onDrawStopped action fired if the DrawSupport stops
119
 * @prop {func} onEndDrawing action fired when a shape is drawn
120
 * @prop {object} messages the localized messages that can be used to customize the tooltip text
121
*/
122
class DrawSupport extends React.Component {
123
    static displayName = 'DrawSupport';
1✔
124

125
    static propTypes = {
1✔
126
        map: PropTypes.object,
127
        drawOwner: PropTypes.string,
128
        drawStatus: PropTypes.string,
129
        drawMethod: PropTypes.string,
130
        options: PropTypes.object,
131
        features: PropTypes.array,
132
        onChangeDrawingStatus: PropTypes.func,
133
        onGeometryChanged: PropTypes.func,
134
        onDrawStopped: PropTypes.func,
135
        onEndDrawing: PropTypes.func,
136
        messages: PropTypes.object,
137
        style: PropTypes.object
138
    };
139

140
    static defaultProps = {
1✔
141
        map: null,
142
        drawOwner: null,
143
        drawStatus: null,
144
        drawMethod: null,
145
        features: null,
146
        options: {
147
            stopAfterDrawing: true
148
        },
149
        onChangeDrawingStatus: () => {},
150
        onGeometryChanged: () => {},
151
        onDrawStopped: () => {},
152
        onEndDrawing: () => {},
153
        style: {
154
            color: '#ffcc33',
155
            opacity: 1,
156
            weight: 3,
157
            fillColor: '#ffffff',
158
            fillOpacity: 0.2,
159
            clickable: false,
160
            editing: {
161
                fill: 1
162
            }
163
        }
164
    };
165

166
    /**
167
     * Inside this lyfecycle method the status is checked to manipulate the behaviour of the DrawSupport.<br>
168
     * Here is the list of all status:<br>
169
     * create allows to create features<br>
170
     * start allows to start drawing features<br>
171
     * drawOrEdit allows to start drawing or editing the passed features or both<br>
172
     * stop allows to stop drawing features<br>
173
     * replace allows to replace all the features drawn by Drawsupport with new ones<br>
174
     * clean it cleans the drawn features and stop the drawsupport
175
     * endDrawing as for 'replace' action allows to replace all the features in addition triggers end drawing action to store data in state
176
     * @memberof components.map.DrawSupport
177
     * @function UNSAFE_componentWillReceiveProps
178
    */
179
    UNSAFE_componentWillReceiveProps(newProps) {
180
        let drawingStrings = this.props.messages || this.context.messages ? this.context.messages.drawLocal : false;
22!
181
        if (drawingStrings) {
22!
182
            L.drawLocal = drawingStrings;
×
183
        }
184
        if (this.props.drawStatus !== newProps.drawStatus || newProps.drawStatus === "replace" || this.props.drawMethod !== newProps.drawMethod || this.props.features !== newProps.features) {
22✔
185
            switch (newProps.drawStatus) {
21!
186
            case "create": this.addGeojsonLayer({features: newProps.features, projection: newProps.options && newProps.options.featureProjection || "EPSG:4326",
1✔
187
                style: newProps.style && newProps.style[newProps.drawMethod] || newProps.style}); break;
1✔
188
            case "start": this.addDrawInteraction(newProps); break;
1✔
189
            case "drawOrEdit": this.addDrawOrEditInteractions(newProps); break;
14✔
190
            case "stop": {
191
                this.removeAllInteractions();
1✔
192
                break;
1✔
193
            }
194
            case "replace": this.replaceFeatures(newProps); break;
1✔
195
            case "clean": this.cleanAndStop(); break;
1✔
196
            case "endDrawing": this.endDrawing(newProps); break;
2✔
197
            default :
198
                return;
×
199
            }
200
        }
201
    }
202

203
    onDrawStart = () => {
10✔
204
        this.drawing = true;
7✔
205
    };
206

207
    onDrawCreated = (evt) => {
10✔
208
        this.drawing = false;
×
209
        const layer = evt.layer;
×
210
        // let drawn geom stay on the map
211
        let geoJesonFt = layer.toGeoJSON();
×
212
        let bounds;
213
        if (evt.layerType === "marker") {
×
214
            bounds = L.latLngBounds(geoJesonFt.geometry.coordinates, geoJesonFt.geometry.coordinates);
×
215
        } else {
216
            if (!layer._map) {
×
217
                layer._map = this.props.map;
×
218
                layer._renderer = this.props.map.getRenderer(layer);
×
219
                layer._project();
×
220
            }
221
            bounds = layer.getBounds();
×
222
        }
223
        let extent = boundsToOLExtent(bounds);
×
224
        let center = bounds.getCenter();
×
225
        center = [center.lng, center.lat];
×
226
        let coordinates = geoJesonFt.geometry.coordinates;
×
227
        let projection = "EPSG:4326";
×
228
        let type = geoJesonFt.geometry.type;
×
229
        let radius = layer.getRadius ? layer.getRadius() : 0;
×
230
        if (evt.layerType === "circle") {
×
231
            // Circle needs to generate path and needs to be projected before
232
            // When GeometryDetails update circle it's in charge to generete path
233
            // but for first time we need to do this!
234
            geoJesonFt.projection = "EPSG:4326";
×
235
            projection = "EPSG:3857";
×
236
            extent = reprojectBbox(extent, "EPSG:4326", projection);
×
237
            const projCircle = toProjectedCircle(layer._mRadius, center, projection);
×
238
            center = projCircle.center;
×
239
            radius = projCircle.radius;
×
240
            coordinates = calculateCircleCoordinates(center, radius, 100);
×
241
            geoJesonFt.radius = layer.getRadius ? layer.getRadius() : 0;
×
242
            center = [center.x, center.y];
×
243
            type = "Polygon";
×
244
        }
245
        // We always draw geoJson feature
246
        this.drawLayer.addData(geoJesonFt);
×
247
        // Geometry respect query form panel needs
248
        let geometry = {
×
249
            type,
250
            extent,
251
            center,
252
            coordinates,
253
            radius,
254
            projection
255
        };
256
        if (this.props.options && this.props.options.stopAfterDrawing) {
×
257
            this.props.onChangeDrawingStatus('stop', this.props.drawMethod, this.props.drawOwner);
×
258
        }
259
        const newGeoJsonFt = this.convertFeaturesToGeoJson(evt.layer, this.props);
×
260
        this.props.onEndDrawing(geometry, this.props.drawOwner);
×
261
        this.props.onGeometryChanged([newGeoJsonFt], this.props.drawOwner, this.props.options && this.props.options.stopAfterDrawing ? "enterEditMode" : "");
×
262
    };
263

264
    onUpdateGeom = (features, props) => {
10✔
265
        const newGeoJsonFt = this.convertFeaturesToGeoJson(features, props);
×
266
        props.onGeometryChanged([newGeoJsonFt], props.drawOwner);
×
267
    };
268

269
    render() {
270
        return null;
32✔
271
    }
272

273
    addLayer = (newProps) => {
10✔
274
        this.clean();
5✔
275
        const vector = L.geoJson(null, {
5✔
276
            pointToLayer: function(feature, latLng) {
277
                const {center, radius} = toLeafletCircle(feature.radius, latLng, feature.projection);
×
278
                return L.circle(center, radius || 5);
×
279
            },
280
            style: (feature) => newProps.style && newProps.style[feature.geometry.type] || {
×
281
                color: '#ffcc33',
282
                opacity: 1,
283
                weight: 3,
284
                fillColor: '#ffffff',
285
                fillOpacity: 0.2,
286
                clickable: false
287
            }
288
        });
289
        this.props.map.addLayer(vector);
5✔
290
        // Immediately draw passed features
291
        if (newProps.features && newProps.features.length > 0) {
5✔
292
            vector.addData(this.convertFeaturesPolygonToPoint(newProps.features, this.props.drawMethod));
4✔
293
        }
294
        this.drawLayer = vector;
5✔
295
    };
296

297
    addGeojsonLayer = ({features, projection, style}) => {
10✔
298
        this.clean();
16✔
299
        let geoJsonLayerGroup = L.geoJson(features, {style: (f) => {
16✔
300
            return f.style || style;
3✔
301
        }, pointToLayer: (f, latLng) => {
302
            let center = reproject({x: latLng.lng, y: latLng.lat}, projection, "EPSG:4326");
×
303
            return pointToLayer(L.latLng(center.y, center.x), f, style);
×
304
        }});
305

306
        // (toGeoJSON())
307
        this.drawLayer = geoJsonLayerGroup.addTo(this.props.map);
16✔
308
        // this.drawLayer = tempLayer.addTo(this.props.map);
309
    };
310

311

312
    replaceFeatures = (newProps) => {
10✔
313
        if (!this.drawLayer) {
3!
314
            this.addGeojsonLayer({features: newProps.features, projection: newProps.options && newProps.options.featureProjection || "EPSG:4326",
3✔
315
                style: newProps.style && newProps.style[newProps.drawMethod] || newProps.style});
9✔
316
        } else {
317
            this.drawLayer.clearLayers();
×
318
            if (this.props.drawMethod === "Circle") {
×
319
                this.drawLayer.options.pointToLayer = (feature, latLng) => {
×
320
                    const {center, radius} = toLeafletCircle(feature.radius, latLng, feature.projection);
×
321
                    return L.circle(center, radius || 5);
×
322
                };
323
                this.drawLayer.options.style = {
×
324
                    color: '#ffcc33',
325
                    opacity: 1,
326
                    weight: 3,
327
                    fillColor: '#ffffff',
328
                    fillOpacity: 0.2,
329
                    clickable: false
330
                };
331
            } else {
332
                this.drawLayer.options.pointToLayer = (f, latLng) => {
×
333
                    let center = reproject({x: latLng.lng, y: latLng.lat}, newProps.options && newProps.options.featureProjection || "EPSG:4326", "EPSG:4326");
×
334
                    return pointToLayer(L.latLng(center.y, center.x), f, newProps.style);
×
335
                };
336
            }
337
            this.drawLayer.addData(this.convertFeaturesPolygonToPoint(newProps.features, this.props.drawMethod));
×
338
        }
339
    };
340

341
    endDrawing = (newProps) => {
10✔
342
        this.replaceFeatures(newProps);
2✔
343
        const geometry = last(newProps.features);
2✔
344
        if (this.props.drawMethod === "Circle" && geometry && !isNil(geometry.center) && !isNil(geometry.radius)) {
2✔
345
            this.props.onEndDrawing({...geometry, coordinates: calculateCircleCoordinates(geometry.center, geometry.radius, 100)}, this.props.drawOwner);
1✔
346
        } else if (geometry) {
1!
347
            this.props.onEndDrawing(geometry, this.props.drawOwner);
×
348
        }
349
    };
350

351
    addDrawInteraction = (newProps) => {
10✔
352
        this.removeAllInteractions();
7✔
353
        if (newProps.drawMethod === "Point" || newProps.drawMethod === "MultiPoint") {
7✔
354
            this.addGeojsonLayer({
2✔
355
                features: newProps.features,
356
                projection: newProps.options && newProps.options.featureProjection || "EPSG:4326",
6✔
357
                style: newProps.style && newProps.style[newProps.drawMethod] || newProps.style
6✔
358
            });
359
        } else {
360
            this.addLayer(newProps);
5✔
361
        }
362
        this.props.map.on('draw:created', this.onDrawCreated, this);
7✔
363
        this.props.map.on('draw:drawstart', this.onDrawStart, this);
7✔
364

365
        if (newProps.drawMethod === 'LineString' || newProps.drawMethod === 'Bearing' || newProps.drawMethod === 'MultiLineString') {
7✔
366
            this.drawControl = new L.Draw.Polyline(this.props.map, {
2✔
367
                shapeOptions: {
368
                    color: '#000000',
369
                    weight: 2,
370
                    fillColor: '#ffffff',
371
                    fillOpacity: 0.2
372
                },
373
                showLength: false,
374
                repeatMode: true,
375
                icon: new L.DivIcon({
376
                    iconSize: new L.Point(8, 8),
377
                    className: 'leaflet-div-icon leaflet-editing-icon'
378
                }),
379
                touchIcon: new L.DivIcon({
380
                    iconSize: new L.Point(8, 8),
381
                    className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
382
                })
383
            });
384
        } else if (newProps.drawMethod === 'Polygon' || newProps.drawMethod === 'MultiPolygon') {
5✔
385
            this.drawControl = new L.Draw.Polygon(this.props.map, {
2✔
386
                shapeOptions: {
387
                    color: '#000000',
388
                    weight: 2,
389
                    fillColor: '#ffffff',
390
                    fillOpacity: 0.2,
391
                    dashArray: [5, 5],
392
                    guidelineDistance: 5
393
                },
394
                allowIntersection: false,
395
                showLength: false,
396
                showArea: false,
397
                repeatMode: true,
398
                icon: new L.DivIcon({
399
                    iconSize: new L.Point(8, 8),
400
                    className: 'leaflet-div-icon leaflet-editing-icon'
401
                }),
402
                touchIcon: new L.DivIcon({
403
                    iconSize: new L.Point(8, 8),
404
                    className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
405
                })
406
            });
407
        } else if (newProps.drawMethod === 'BBOX') {
3!
408
            this.drawControl = new L.Draw.Rectangle(this.props.map, {
×
409
                draw: false,
410
                shapeOptions: {
411
                    color: '#000000',
412
                    weight: 2,
413
                    fillColor: '#ffffff',
414
                    fillOpacity: 0.2,
415
                    dashArray: [5, 5]
416
                },
417
                repeatMode: true,
418
                icon: new L.DivIcon({
419
                    iconSize: new L.Point(8, 8),
420
                    className: 'leaflet-div-icon leaflet-editing-icon'
421
                }),
422
                touchIcon: new L.DivIcon({
423
                    iconSize: new L.Point(8, 8),
424
                    className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
425
                })
426
            });
427
        } else if (newProps.drawMethod === 'Circle') {
3✔
428
            this.drawControl = new L.Draw.Circle(this.props.map, {
1✔
429
                shapeOptions: {
430
                    color: '#000000',
431
                    weight: 2,
432
                    fillColor: '#ffffff',
433
                    fillOpacity: 0.2,
434
                    dashArray: [5, 5]
435
                },
436
                showRadius: false,
437
                repeatMode: true
438
            });
439
        } else if (newProps.drawMethod === 'Point' || newProps.drawMethod === 'MultiPoint') {
2!
440
            this.drawControl = new L.Draw.Marker(this.props.map, {
2✔
441
                shapeOptions: {
442
                    color: '#000000',
443
                    weight: 2,
444
                    fillColor: '#ffffff',
445
                    fillOpacity: 0.2
446
                },
447
                repeatMode: true
448
            });
449
        }
450

451
        // start the draw control
452
        if (this.props.map.doubleClickZoom) {
7!
453
            this.props.map.doubleClickZoom.disable();
7✔
454
        }
455
        this.drawControl.enable();
7✔
456
    };
457

458
    addDrawOrEditInteractions = (newProps) => {
10✔
459
        let newFeatures = [];
14✔
460

461
        newProps.features.map(ft => {
14✔
462
            let newFs;
463
            if (ft && ft.geometry && ft.geometry.type && !isSimpleGeomType(ft.geometry.type)) {
1!
464
                if (ft.geometry.type === "GeometryCollection") {
×
465
                    newFs = ft.geometry.geometries.map(g => {
×
466
                        return g.coordinates.map((coords, idx) => {
×
467
                            return {
×
468
                                type: 'Feature',
469
                                properties: {...ft.properties},
470
                                id: g.type + idx,
471
                                geometry: {
472
                                    coordinates: coords,
473
                                    type: getSimpleGeomType(g.type)
474
                                }
475
                            };
476
                        });
477
                    });
478

479
                    newFeatures.push({type: "FeatureCollection", features: newFs});
×
480
                } else {
481
                    newFs = ft.geometry.coordinates.map((coords, idx) => {
×
482
                        return {
×
483
                            type: 'Feature',
484
                            properties: {...ft.properties},
485
                            id: ft.geometry.type + idx,
486
                            geometry: {
487
                                coordinates: coords,
488
                                type: getSimpleGeomType(ft.geometry.type)
489
                            }
490
                        };
491
                    });
492

493
                    newFeatures.push({type: "FeatureCollection", features: newFs});
×
494
                }
495
            }
496
        });
497

498
        const props = Object.assign({}, newProps, {features: newFeatures.length >  0 ? newFeatures : [{}]});
14!
499
        if (!this.drawLayer) {
14✔
500
            /* Reprojection is needed to implement circle initial visualization after querypanel geometry reload (on reload the 100 points polygon is shown)
501
             *
502
             * We should, for the future draw a circle also on reload.
503
             * NOTE: after some center or radius changes (e.g. )
504
            */
505
            this.addGeojsonLayer({
3✔
506
                features: newProps.features && newProps.options.featureProjection && newProps.options.featureProjection !== "EPSG:4326"
9!
507
                    ? newProps.features.map(f => reprojectGeoJson(f, newProps.options.featureProjection, "EPSG:4326") )
×
508
                    : newProps.features,
509
                projection: newProps.options && newProps.options.featureProjection || "EPSG:4326",
9✔
510
                style: newProps.style && newProps.style[newProps.drawMethod] || newProps.style});
9✔
511

512
        } else {
513
            this.drawLayer.clearLayers();
11✔
514
            this.drawLayer.addData(this.convertFeaturesPolygonToPoint(props.features, props.drawMethod));
11✔
515
        }
516
        if (newProps.options.editEnabled) {
14✔
517
            this.addEditInteraction(props);
7✔
518
        }
519
        if (newProps.options.drawEnabled) {
14✔
520
            this.addDrawInteraction(props);
6✔
521
        }
522
    };
523

524
    addEditInteraction = (newProps) => {
10✔
525
        this.clean();
7✔
526

527
        this.addGeojsonLayer({
7✔
528
            features: newProps.features,
529
            projection: newProps.options && newProps.options.featureProjection || "EPSG:4326",
21✔
530
            style: Object.assign({}, newProps.style, {
531
                poly: {
532
                    icon: new L.DivIcon({
533
                        iconSize: new L.Point(8, 8),
534
                        className: 'leaflet-div-icon leaflet-editing-icon'
535
                    }),
536
                    touchIcon: new L.DivIcon({
537
                        iconSize: new L.Point(8, 8),
538
                        className: 'leaflet-div-icon leaflet-editing-icon leaflet-touch-icon'
539
                    })
540
                }
541
            })
542
        });
543

544
        let allLayers = this.drawLayer.getLayers();
7✔
545

546
        setTimeout(() => {
7✔
547
            allLayers.forEach(l => {
7✔
548
                if (l.getLayers && l.getLayers() && l.getLayers().length) {
×
549
                    l.getLayers().forEach((layer) => {
×
550
                        layer.on('edit', (e) => this.onUpdateGeom(e.target, newProps));
×
551
                        layer.on('moveend', (e) => this.onUpdateGeom(e.target, newProps));
×
552
                        if (layer.editing) {
×
553
                            layer.editing.enable();
×
554
                        }
555
                    });
556
                } else {
557
                    l.on('edit', (e) => this.onUpdateGeom(e.target, newProps));
×
558
                    l.on('moveend', (e) => this.onUpdateGeom(e.target, newProps));
×
559
                    if (l.editing) {
×
560
                        l.editing.enable();
×
561
                    }
562
                }
563
            });
564
        }, 0);
565

566
        this.editControl = new L.Control.Draw({
7✔
567
            edit: {
568
                featureGroup: this.drawLayer,
569
                poly: {
570
                    allowIntersection: false
571
                },
572
                edit: true
573
            },
574
            draw: {
575
                polygon: {
576
                    allowIntersection: false,
577
                    showArea: true
578
                }
579
            }
580
        });
581
        if (this.props.map.doubleClickZoom) {
7!
582
            this.props.map.doubleClickZoom.disable();
7✔
583
        }
584
    }
585

586
    removeAllInteractions = () => {
10✔
587
        this.removeEditInteraction();
9✔
588
        this.removeDrawInteraction();
9✔
589
        // this.props.onDrawStopped();
590
    }
591

592
    removeDrawInteraction = () => {
10✔
593
        if (this.drawControl !== null && this.drawControl !== undefined) {
37✔
594
            // Needed if missing disable() isn't warking
595
            if (this.props.options && this.props.options.stopAfterDrawing) {
5!
596
                this.drawControl.setOptions({repeatMode: false});
×
597
                this.props.onDrawStopped();
×
598
            }
599
            this.drawControl.disable();
5✔
600
            this.drawControl = null;
5✔
601
            this.props.map.off('draw:created', this.onDrawCreated, this);
5✔
602
            this.props.map.off('draw:drawstart', this.onDrawStart, this);
5✔
603
            if (this.props.map.doubleClickZoom) {
5!
604
                this.props.map.doubleClickZoom.enable();
5✔
605
            }
606
        }
607
    };
608

609
    removeEditInteraction = () => {
10✔
610
        if (this.drawLayer) {
37✔
611
            let allLayers = this.drawLayer.getLayers();
20✔
612
            allLayers.forEach(l => {
20✔
613
                if (l.getLayers && l.getLayers() && l.getLayers().length) {
×
614
                    l.getLayers().forEach((layer) => {
×
615
                        layer.off('edit');
×
616
                        layer.off('moveend');
×
617
                        if (layer.editing) {
×
618
                            layer.editing.disable();
×
619
                        }
620
                    });
621
                } else {
622
                    l.off('edit');
×
623
                    l.off('moveend');
×
624
                    if (l.editing) {
×
625
                        l.editing.disable();
×
626
                    }
627
                }
628
            });
629
            this.editControl = null;
20✔
630
        }
631
        if (this.props.map.doubleClickZoom) {
37!
632
            this.props.map.doubleClickZoom.enable();
37✔
633
        }
634
    };
635

636
    cleanAndStop = () => {
10✔
637
        this.removeAllInteractions();
1✔
638

639
        if (this.drawLayer) {
1!
640
            this.drawLayer.clearLayers();
1✔
641
            this.props.map.removeLayer(this.drawLayer);
1✔
642
            this.drawLayer = null;
1✔
643
        }
644
    };
645

646
    clean = () => {
10✔
647
        this.removeEditInteraction();
28✔
648
        this.removeDrawInteraction();
28✔
649

650
        if (this.drawLayer) {
28✔
651
            this.drawLayer.clearLayers();
13✔
652
            this.props.map.removeLayer(this.drawLayer);
13✔
653
            this.drawLayer = null;
13✔
654
        }
655
    };
656

657
    convertFeaturesPolygonToPoint = (features, method) => {
10✔
658
        return method === 'Circle' ? features.map((f) => {
15!
659
            const {center, projection, radius} = ((f.center !== undefined && f.radius !== undefined) ? toLeafletCircle(f.radius, {lat: f.center.y, lng: f.center.x}, f.projection) : f);
×
660
            return {
×
661
                ...f,
662
                coordinates: center ? [center.x, center.y] : f.coordinates,
×
663
                center: center || f.center,
×
664
                projection: projection || f.projection,
×
665
                radius: radius !== undefined ? radius : f.radius,
×
666
                type: "Point"
667
            };
668
        }) : features;
669

670
    };
671

672
    convertFeaturesToGeoJson = (featureEdited, props) => {
10✔
673
        let geom;
674
        if (!isSimpleGeomType(props.drawMethod)) {
×
675
            if (props.drawMethod === "GeometryCollection") {
×
676
                let geometries = this.drawLayer.getLayers().map(f => f.toGeoJSON());
×
677
                return {
×
678
                    type: "GeometryCollection",
679
                    geometries: geometries.map(g => {
680
                        if (g.type === "FeatureCollection") {
×
681
                            return {
×
682
                                type: "Multi" + g.features[0].geometry.type,
683
                                coordinates: g.features.map((feat) => {
684
                                    return feat.geometry.coordinates;
×
685
                                })
686
                            };
687
                        }
688
                        return {
×
689
                            type: g.geometry.type,
690
                            coordinates: g.geometry.coordinates
691
                        };
692
                    })
693
                };
694
            }
695
            let newFeatures = this.drawLayer.getLayers().map(f => f.toGeoJSON());
×
696
            geom = {
×
697
                type: props.drawMethod,
698
                coordinates: newFeatures.reduce((p, c) => {
699
                    return p.concat([c.geometry.coordinates]);
×
700
                }, [])
701
            };
702
        } else {
703
            geom = featureEdited.toGeoJSON().geometry;
×
704
        }
NEW
705
        return Object.assign({}, featureEdited.toGeoJSON(), {geometry: geom});
×
706
    };
707
}
708

709

710
export default DrawSupport;
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