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

geographika / cpsi-mapview / 6470466651

10 Oct 2023 02:05PM UTC coverage: 22.909% (-1.2%) from 24.104%
6470466651

push

github

geographika
Make a bbox filter optional if required for WFS layers

421 of 2282 branches covered (0.0%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

1236 of 4951 relevant lines covered (24.96%)

0.99 hits per line

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

18.34
/app/controller/button/DrawingButtonController.js
1
/**
2
 * This class is the controller for the DrawingButton.
3
 */
4
Ext.define('CpsiMapview.controller.button.DrawingButtonController', {
1✔
5
    extend: 'Ext.app.ViewController',
6
    requires: [
7
        'BasiGX.util.Map',
8
        'BasiGX.util.Layer',
9
        'Ext.menu.Menu',
10
        'GeoExt.component.FeatureRenderer',
11
        'GeoExt.data.store.Features',
12
        'CpsiMapview.controller.button.TracingMixin'
13
    ],
14

15
    alias: 'controller.cmv_drawing_button',
16

17
    mixins: [
18
        'CpsiMapview.controller.button.TracingMixin'
19
    ],
20

21
    /**
22
     * The OpenLayers map. If not given, will be auto-detected
23
     */
24
    map: null,
25

26
    /**
27
     * The BasiGX mapComponent. If not given, will be auto-detected
28
     */
29
    mapComponent: null,
30

31
    /**
32
     * Temporary vector layer used while drawing points, lines or polygons
33
     */
34
    drawLayer: null,
35

36
    /**
37
     * OpenLayers draw interaction for drawing of lines and polygons
38
     */
39
    drawInteraction: null,
40

41
    /**
42
     * OpenLayers modify interaction
43
     * Used in polygon and point draw mode
44
     */
45
    modifyInteraction: null,
46

47
    /**
48
     * OpenLayers snap interaction for allowing easier tracing
49
     */
50
    snapInteraction: null,
51

52
    /**
53
     * If user has started to edit a line, this means the first point of a line is already set
54
     */
55
    currentlyDrawing: false,
56

57
    /**
58
     * Stores event listener keys to be un-listened to on destroy or button toggle
59
     */
60
    listenerKeys: [],
61

62
    /**
63
     * Determines if event handling is blocked.
64
     */
65
    //blockedEventHandling: false,
66

67
    constructor: function () {
68
        var me = this;
10✔
69
        me.handleDrawStart = me.handleDrawStart.bind(me);
10✔
70
        me.handleDrawEnd = me.handleDrawEnd.bind(me);
10✔
71
        me.handleModifyEnd = me.handleModifyEnd.bind(me);
10✔
72
        me.handleKeyPress = me.handleKeyPress.bind(me);
10✔
73
        me.callParent(arguments);
10✔
74
    },
75

76
    /**
77
     * Set the layer to store features drawn by the editing
78
     * tools
79
     * @param {any} layer
80
     */
81
    setDrawLayer: function (layer) {
82
        var me = this;
×
83

84
        if (!me.map) {
×
85
            return;
×
86
        }
87

88
        if (me.drawLayer) {
×
89
            me.map.removeLayer(me.drawLayer);
×
90
        }
91

92
        me.drawLayer = layer;
×
93
        me.setDrawInteraction(layer);
×
94
        me.setModifyInteraction(layer);
×
95
        me.setSnapInteraction(layer);
×
96
    },
97

98
    /**
99
     * Set the map drawing interaction
100
     * which will allow features to be added to the drawLayer
101
     * @param {any} layer
102
     */
103
    setDrawInteraction: function (layer) {
104

105
        var me = this;
×
106
        var view = me.getView();
×
107

108
        if (me.drawInteraction) {
×
109
            me.map.removeInteraction(me.drawInteraction);
×
110
        }
111

112
        var drawCondition = function (evt) {
×
113
            // the draw interaction does not work with the singleClick condition.
114
            return ol.events.condition.primaryAction(evt) && ol.events.condition.noModifierKeys(evt);
×
115
        };
116

117
        var source = layer.getSource();
×
118
        var collection = source.getFeaturesCollection();
×
119
        var drawInteractionConfig = {
×
120
            type: 'LineString',
121
            features: collection,
122
            condition: drawCondition,
123
            style: me.getDrawStyleFunction(),
124
            snapTolerance: view.getDrawInteractionSnapTolerance()
125
        };
126

127
        me.drawInteraction = new ol.interaction.Draw(drawInteractionConfig);
×
128
        me.drawInteraction.on('drawstart', me.handleDrawStart);
×
129
        me.drawInteraction.on('drawend', me.handleDrawEnd);
×
130

131
        me.map.addInteraction(me.drawInteraction);
×
132
    },
133

134
    /**
135
     * Prepare the styles retrieved from config.
136
     */
137
    prepareDrawingStyles: function () {
138
        var me = this;
×
139
        var view = me.getView();
×
140

141
        // ensure styles are applied at right conditions
142
        view.getDrawBeforeEditingPoint().setGeometry(function (feature) {
×
143
            var geom = feature.getGeometry();
×
144
            if (!me.currentlyDrawing) {
×
145
                return geom;
×
146
            }
147
        });
148
        view.getDrawStyleStartPoint().setGeometry(function (feature) {
×
149
            var geom = feature.getGeometry();
×
150
            var coords = geom.getCoordinates();
×
151
            var firstCoord = coords[0];
×
152
            return new ol.geom.Point(firstCoord);
×
153
        });
154
        view.getDrawStyleEndPoint().setGeometry(function (feature) {
×
155
            var coords = feature.getGeometry().getCoordinates();
×
156
            if (coords.length > 1) {
×
157
                var lastCoord = coords[coords.length - 1];
×
158
                return new ol.geom.Point(lastCoord);
×
159
            }
160
        });
161

162
        // ensure snap styles are always on top
163
        view.getSnappedNodeStyle().setZIndex(Infinity);
×
164
        view.getSnappedEdgeStyle().setZIndex(Infinity);
×
165
    },
166

167
    /**
168
     * Creates the style function for the drawn feature.
169
     *
170
     * @returns {Function} The style function for the drawn feature.
171
     */
172
    getDrawStyleFunction: function () {
173
        var me = this;
×
174
        var view = me.getView();
×
175

176
        return function (feature) {
×
177
            var coordinate = feature.getGeometry().getCoordinates();
×
178
            var pixel = me.map.getPixelFromCoordinate(coordinate);
×
179

180
            // remember if we have hit a referenced layer
181

182
            var node, edge, polygon, self;
183

184
            me.map.forEachFeatureAtPixel(pixel, function (foundFeature, layer) {
×
185
                if (layer) {
×
186
                    var key = layer.get('layerKey');
×
187
                    if (key === view.getNodeLayerKey()) {
×
188
                        node = foundFeature;
×
189
                    } else if (key === view.getEdgeLayerKey()) {
×
190
                        edge = foundFeature;
×
191
                    } else if (key === view.getPolygonLayerKey()) {
×
192
                        polygon = foundFeature;
×
193
                    } else if (me.drawLayer === layer) {
×
194
                        // snapping to self drawn feature
195
                        self = foundFeature;
×
196
                    }
197
                }
198
            });
199

200
            if (node) {
×
201
                return view.getSnappedNodeStyle();
×
202
            } else if (edge) {
×
203
                if (view.getShowVerticesOfSnappedEdge()) {
×
204
                    // Prepare style for vertices of snapped edge
205
                    // we create a MultiPoint from the edge's vertices
206
                    // and set it as geometry in our style function
207
                    var geom = edge.getGeometry();
×
208
                    var coords = [];
×
209
                    if (geom.getType() === 'MultiLineString') {
×
210
                        // use all vertices of containing LineStrings
211
                        var lineStrings = geom.getLineStrings();
×
212
                        Ext.each(lineStrings, function (lineString) {
×
213
                            var lineStringCoords = lineString.getCoordinates();
×
214
                            coords = coords.concat(lineStringCoords);
×
215
                        });
216
                    } else {
217
                        coords = geom.getCoordinates();
×
218
                    }
219
                    var verticesMultiPoint = new ol.geom.MultiPoint(coords);
×
220
                    var snappedEdgeVertexStyle = view.getSnappedEdgeVertexStyle().clone();
×
221
                    snappedEdgeVertexStyle.setGeometry(verticesMultiPoint);
×
222

223
                    // combine style for snapped point and vertices of snapped edge
224
                    return [
×
225
                        snappedEdgeVertexStyle,
226
                        view.getSnappedEdgeStyle()
227
                    ];
228
                } else {
229
                    return view.getSnappedEdgeStyle();
×
230
                }
231
            } else if (polygon) {
×
232
                return view.getSnappedPolygonStyle();
×
233
            } else if (self) {
×
234
                return view.getModifySnapPointStyle();
×
235
            } else {
236
                return me.defaultDrawStyle;
×
237
            }
238
        };
239
    },
240

241
    /**
242
     * Set the modify interaction, used to modify
243
     * existing features created in the drawLayer
244
     * We cannot however simply stop and start redrawing the line, adding directly to the vertices
245
     * https://stackoverflow.com/questions/45836955/openlayers-3-continue-drawing-the-initial-line-after-drawend-triggered-with-do/45859390#45859390
246
     * @param {any} layer
247
     */
248
    setModifyInteraction: function (layer) {
249

250
        var me = this;
×
251

252
        if (me.modifyInteraction) {
×
253
            me.map.removeInteraction(me.modifyInteraction);
×
254
        }
255

256
        var condition = function (evt) {
×
257
            // only allow modifying when the CTRL key is pressed, otherwise we cannot add new line
258
            // segments once the first feature is drawn
259
            return ol.events.condition.primaryAction(evt) && ol.events.condition.platformModifierKeyOnly(evt);
×
260
        };
261

262
        // create the modify interaction
263
        var modifyInteractionConfig = {
×
264
            features: layer.getSource().getFeaturesCollection(),
265
            condition: condition,
266
            // intentionally pass empty style, because modify style is
267
            // done in the draw interaction
268
            style: new ol.style.Style({})
269
        };
270
        me.modifyInteraction = new ol.interaction.Modify(modifyInteractionConfig);
×
271
        me.map.addInteraction(me.modifyInteraction);
×
272
        me.modifyInteraction.on('modifyend', me.handleModifyEnd);
×
273

274
    },
275

276
    /**
277
     * Set the snap interaction used to snap to features
278
     * @param {any} layer
279
     */
280
    setSnapInteraction: function (drawLayer) {
281

282
        var me = this;
10✔
283

284
        if (me.snapInteraction) {
10✔
285
            me.map.removeInteraction(me.snapInteraction);
1✔
286
        }
287

288
        // unbind any previous layer event listeners
289
        me.unBindLayerListeners();
10✔
290

291
        var snapCollection = new ol.Collection([], {
10✔
292
            unique: true
293
        });
294

295
        var fc = drawLayer.getSource().getFeaturesCollection();
10✔
296

297
        fc.on('add', function (evt) {
10✔
298
            snapCollection.push(evt.element);
×
299
        });
300

301
        fc.on('remove', function (evt) {
10✔
302
            snapCollection.remove(evt.element);
×
303
        });
304

305
        // Adds Features to a Collection, catches and ignores exceptions thrown
306
        // by the Collection if trying to add a duplicate feature, but still maintains
307
        // a unique collection of features. Used as an alternative to .extend but ensures
308
        // any potential errors related to unique features are handled / suppressed.
309
        var addUniqueFeaturesToCollection = function (collection, features) {
10✔
310
            Ext.Array.each(features, function (f) {
14✔
311
                // eslint-disable-next-line
312
                try { collection.push(f); } catch (e) { }
27✔
313
            });
314
        };
315

316
        // Checks if a feature exists in layers other than the current layer
317
        var isFeatureInOtherLayers = function (allLayers, currentLayer, feature) {
10✔
318
            var found = false;
4✔
319
            Ext.Array.each(allLayers, function(layer) {
4✔
320
                if(layer !== currentLayer) {
8✔
321
                    if(layer.getSource().hasFeature(feature)) {
4✔
322
                        found = true;
2✔
323
                    }
324
                }
325
            });
326
            return found;
4✔
327
        };
328

329
        // get the layers to snap to
330
        var view = me.getView();
10✔
331
        var layerKeys = view.getSnappingLayerKeys();
10✔
332
        var allowSnapToHiddenFeatures = view.getAllowSnapToHiddenFeatures();
10✔
333
        var layers = Ext.Array.map(layerKeys, function (key) {
10✔
334
            return BasiGX.util.Layer.getLayersBy('layerKey', key)[0];
14✔
335
        });
336

337
        Ext.Array.each(layers, function (layer) {
10✔
338
            var feats = layer.getSource().getFeatures(); // these are standard WFS layers so we use getSource without getFeaturesCollection here
14✔
339
            // add inital features to the snap collection, if the layer is visible
340
            // or if allowSnapToHiddenFeatures is enabled
341
            if (layer.getVisible() || allowSnapToHiddenFeatures) {
14✔
342
                addUniqueFeaturesToCollection(snapCollection, feats);
12✔
343
            }
344

345
            // Update the snapCollection on addfeature or removefeature
346
            var addFeatureKey = layer.getSource().on('addfeature', function (evt) {
14✔
347
                if (layer.getVisible() || allowSnapToHiddenFeatures) {
1!
348
                    addUniqueFeaturesToCollection(snapCollection, [evt.feature]);
1✔
349
                }
350
            });
351

352
            var removefeatureKey = layer.getSource().on('removefeature', function (evt) {
14✔
353
                if (!isFeatureInOtherLayers(layers, layer, evt.feature)) {
2✔
354
                    snapCollection.remove(evt.feature);
1✔
355
                }
356
            });
357

358
            // Update the snapCollection on layer visibility change
359
            // only handle layer visible change event if snapping to hidden features is disabled
360
            if (!allowSnapToHiddenFeatures) {
14✔
361
                var changeVisibleKey = layer.on('change:visible', function () {
12✔
362
                    var features = layer.getSource().getFeatures();
2✔
363
                    if (layer.getVisible()) {
2✔
364
                        addUniqueFeaturesToCollection(snapCollection, features);
1✔
365
                    } else {
366
                        Ext.Array.each(features, function (f) {
1✔
367
                            if (!isFeatureInOtherLayers(layers, layer, f)) {
2✔
368
                                snapCollection.remove(f);
1✔
369
                            }
370
                        });
371
                    }
372
                });
373
            }
374

375
            me.listenerKeys.push(addFeatureKey, removefeatureKey, changeVisibleKey);
14✔
376
        });
377

378
        // vector tile sources cannot be used for snapping as they
379
        // do not provide a getFeatures function
380
        // see https://openlayers.org/en/latest/apidoc/module-ol_source_VectorTile-VectorTile.html
381

382
        me.snapInteraction = new ol.interaction.Snap({
10✔
383
            features: snapCollection
384
        });
385
        me.map.addInteraction(me.snapInteraction);
10✔
386

387
    },
388

389
    getSnappedFeatureId: function (coord, searchLayer) {
390

391
        var me = this;
×
392
        var extent = ol.extent.boundingExtent([coord]); // still a single point
×
393

394
        var buffer = me.map.getView().getResolution() * 3; // use a 3 pixel tolerance for snapping
×
395

396
        extent = ol.extent.buffer(extent, buffer); // buffer the point as it may have snapped to a different feature than the nodes/edges
×
397

398
        var featureIds = [];
×
399

400
        // find all intersecting node points
401
        // https://openlayers.org/en/latest/apidoc/module-ol_source_Vector-VectorSource.html
402
        searchLayer.getSource().forEachFeatureIntersectingExtent(extent, function (feat) {
×
403
            //<debug>
404
            // this requires all GeoJSON features used for the layer to have an id property
405
            Ext.Assert.truthy(feat.getId());
×
406
            //</debug>
407
            featureIds.push(feat.getId());
×
408
        });
409

410
        // cases where the same feature is loaded into the layer leading to duplicated Ids
411
        // might be fixed now?
412
        featureIds = Ext.Array.unique(featureIds);
×
413

414
        if (featureIds.length === 1) {
×
415
            return featureIds[0];
×
416
        } else {
417
            if (featureIds.length > 1) {
×
418
                // TODO show layerKey instead of featureIds
419
                Ext.Logger.warn('Multiple features found at ' + coord + ':' + featureIds);
×
420
            }
421
            return null;
×
422
        }
423
    },
424

425
    /**
426
     * Rather than simply creating a new feature each time, attempt to
427
     * merge contiguous linestrings together if the end of the old line
428
     * matches the start of the new line
429
     * @param {any} origGeom
430
     * @param {any} newGeom
431
     */
432
    mergeLineStrings: function (origGeom, newGeom) {
433
        var newGeomFirstCoord = newGeom.getFirstCoordinate();
×
434
        var matchesFirstCoord = Ext.Array.equals(origGeom.getFirstCoordinate(), newGeomFirstCoord);
×
435
        var matchesLastCoord = Ext.Array.equals(origGeom.getLastCoordinate(), newGeomFirstCoord);
×
436

437
        if (matchesFirstCoord || matchesLastCoord) {
×
438
            var origCoords = origGeom.getCoordinates();
×
439
            // if drawing in continued from the start point of the original,
440
            // the original needs to be reversed to we end up with correct
441
            // start and end points
442
            if (matchesFirstCoord) {
×
443
                origCoords.reverse();
×
444
            }
445
            var newCoords = newGeom.getCoordinates();
×
446
            newGeom.setCoordinates(origCoords.concat(newCoords));
×
447
        } else {
448
            Ext.log('Start / End coordinates differ');
×
449
            Ext.log('origGeom start/end coords: ', origGeom.getFirstCoordinate(), origGeom.getLastCoordinate());
×
450
            Ext.log('newGeom start coord: ', newGeom.getFirstCoordinate());
×
451
        }
452

453
        return newGeom;
×
454
    },
455

456
    /**
457
     * Handles the drawstart event
458
     */
459
    handleDrawStart: function () {
460
        var me = this;
×
461
        me.currentlyDrawing = true;
×
462
    },
463

464
    /**
465
     * Handles the drawend event
466
     * @param {ol.interaction.Draw.Event} evt The OpenLayers draw event containing the features
467
     */
468
    handleDrawEnd: function (evt) {
469
        var me = this;
×
470
        var feature = evt.feature;
×
471
        var newGeom = feature.getGeometry();
×
472

473
        var drawSource = me.drawLayer.getSource();
×
474
        var currentFeature = drawSource.getFeaturesCollection().item(0);
×
475

476
        if (currentFeature) {
×
477
            // merge all linestrings to a single linestring
478
            // this is done in place
479
            me.mergeLineStrings(currentFeature.getGeometry(), newGeom);
×
480
        }
481

482
        me.calculateLineIntersections(feature);
×
483

484
        // clear all previous features so only the last drawn feature remains
485
        drawSource.getFeaturesCollection().clear();
×
486

487
        me.currentlyDrawing = false;
×
488
    },
489

490
    /**
491
    * Handles the modifyend event
492
    * @param {ol.interaction.Draw.Event} evt The OpenLayers draw event containing the features
493
    */
494
    handleModifyEnd: function (evt) {
495
        var me = this;
×
496
        var feature = evt.features.item(0);
×
497
        me.calculateLineIntersections(feature);
×
498
    },
499

500
    /**
501
     * Calculate where the geometry intersects other parts of the network
502
     * @param {any} newGeom
503
     */
504
    calculateLineIntersections: function (feature) {
505

506
        var me = this;
×
507
        var view = me.getView();
×
508

509
        var newGeom = feature.getGeometry();
×
510

511
        var startCoord = newGeom.getFirstCoordinate();
×
512
        var endCoord = newGeom.getLastCoordinate();
×
513

514
        var foundFeatAtStart = false;
×
515
        var foundFeatAtEnd = false;
×
516

517
        // get any nodes that the line snaps to
518

519
        var nodeLayerKey = view.getNodeLayerKey();
×
520
        var startNodeId = null;
×
521
        var endNodeId = null;
×
522

523
        if (nodeLayerKey) {
×
524
            var nodeLayer = BasiGX.util.Layer.getLayersBy('layerKey', nodeLayerKey)[0];
×
525
            startNodeId = me.getSnappedFeatureId(startCoord, nodeLayer);
×
526
            endNodeId = me.getSnappedFeatureId(endCoord, nodeLayer);
×
527

528
            foundFeatAtStart = startNodeId ? true : false;
×
529
            foundFeatAtEnd = endNodeId ? true : false;
×
530
        }
531

532
        // now check for any edges at both ends, but only in the case
533
        // where there are no start and end nodes
534

535
        var edgeLayerKey = view.getEdgeLayerKey();
×
536
        var startEdgeId = null;
×
537
        var endEdgeId = null;
×
538

539
        if (edgeLayerKey) {
×
540
            var edgesLayer = BasiGX.util.Layer.getLayersBy('layerKey', edgeLayerKey)[0];
×
541

542
            if (!foundFeatAtStart) {
×
543
                startEdgeId = me.getSnappedFeatureId(startCoord, edgesLayer);
×
544
                foundFeatAtStart = startEdgeId ? true : false;
×
545
            }
546

547
            if (!foundFeatAtEnd) {
×
548
                endEdgeId = me.getSnappedFeatureId(endCoord, edgesLayer);
×
549
                foundFeatAtEnd = endEdgeId ? true : false;
×
550
            }
551
        }
552

553
        // finally we will check if we have snapped to a polygon edge
554
        // this will allow us to create continua based on points around the polygon edge
555

556
        var polygonLayerKey = view.getPolygonLayerKey();
×
557
        var startPolygonId = null;
×
558
        var endPolygonId = null;
×
559

560
        if (polygonLayerKey) {
×
561
            var polygonsLayer = BasiGX.util.Layer.getLayersBy('layerKey', polygonLayerKey)[0];
×
562

563
            if (!foundFeatAtStart) {
×
564
                startPolygonId = me.getSnappedFeatureId(startCoord, polygonsLayer);
×
565
            }
566

567
            if (!foundFeatAtEnd) {
×
568
                endPolygonId = me.getSnappedFeatureId(endCoord, polygonsLayer);
×
569
            }
570
        }
571

572
        var result = {
×
573
            startNodeId: startNodeId,
574
            endNodeId: endNodeId,
575
            startCoord: startCoord,
576
            endCoord: endCoord,
577
            startEdgeId: startEdgeId,
578
            endEdgeId: endEdgeId,
579
            startPolygonId: startPolygonId,
580
            endPolygonId: endPolygonId
581
        };
582

583
        // set the node ids on the edge feature itself
584
        // as these can be used by a polygon tool / grid
585
        // the "magic" number -2 indicates a new node should be created
586
        // for the line, rather than snapping to an existing node
587
        feature.set('startNodeId', startNodeId ? startNodeId : -2);
×
588
        feature.set('endNodeId', endNodeId ? endNodeId : -2);
×
589

590
        // fire an event when the drawing is complete
591
        var drawSource = me.drawLayer.getSource();
×
592
        drawSource.dispatchEvent({ type: 'localdrawend', result: result });
×
593
    },
594

595
    handleKeyPress: function (evt) {
596

597
        var me = this; // bound to the controller in the constructor
×
598

599
        // use DEL to remove last point
600
        if (evt.keyCode == 46) {
×
601
            if (evt.shiftKey === true) {
×
602
                // or set focus just on the map as per https://stackoverflow.com/questions/59453895/add-keyboard-event-to-openlayers-map
603
                // or if the delete key is used in a form it will also remove a point
604
                me.drawInteraction.removeLastPoint();
×
605
            }
606
        }
607

608
        // use ESC to cancel drawing mode
609
        if (evt.keyCode == 27) {
×
610
            me.drawInteraction.finishDrawing();
×
611
        }
612
    },
613

614
    /**
615
     * Main handler which activates or deactivates the interactions and listeners
616
     * @param {Ext.button.Button} btn The button that has been pressed
617
     * @param {boolean} pressed The toggle state of the button
618
     */
619
    onToggle: function (btn, pressed) {
620
        var me = this;
×
621
        var view = me.getView();
×
622

623
        // guess the map if not given
624
        if (!me.map) {
×
625
            me.map = BasiGX.util.Map.getMapComponent().map;
×
626
        }
627

628
        // use draw layer set in the view
629
        //<debug>
630
        Ext.Assert.truthy(view.drawLayer);
×
631
        //</debug>
632
        if (!me.drawLayer) {
×
633
            if (view.drawLayer) {
×
634
                me.drawLayer = view.drawLayer;
×
635
            }
636
        }
637

638
        me.prepareDrawingStyles();
×
639

640
        // set initial style for drawing features
641
        me.defaultDrawStyle = [
×
642
            view.getDrawBeforeEditingPoint(),
643
            view.getDrawStyleStartPoint(),
644
            view.getDrawStyleLine(),
645
            view.getDrawStyleEndPoint(),
646
        ];
647
        me.drawLayer.setStyle(me.defaultDrawStyle);
×
648

649
        me.setDrawInteraction(me.drawLayer);
×
650
        me.setModifyInteraction(me.drawLayer);
×
651
        me.setSnapInteraction(me.drawLayer);
×
652

653
        var viewPort = me.map.getViewport();
×
654

655
        var tracingLayerKeys = view.getTracingLayerKeys();
×
656

657
        if (pressed) {
×
658

659
            me.initTracing(
×
660
                tracingLayerKeys,
661
                me.drawInteraction
662
            );
663
            me.drawInteraction.setActive(true);
×
664
            me.modifyInteraction.setActive(true);
×
665
            me.snapInteraction.setActive(true);
×
666
            viewPort.addEventListener('contextmenu', me.contextHandler);
×
667
            document.addEventListener('keydown', me.handleKeyPress);
×
668
        } else {
669
            me.cleanupTracing();
×
670
            me.drawInteraction.setActive(false);
×
671
            me.modifyInteraction.setActive(false);
×
672
            me.snapInteraction.setActive(false);
×
673
            viewPort.removeEventListener('contextmenu', me.contextHandler);
×
674
            document.removeEventListener('keydown', me.handleKeyPress);
×
675
        }
676
    },
677

678
    /**
679
     * Called when new tracing coordinates are available.
680
     *
681
     * @param {ol.coordinate.Coordinate[]} appendCoords The new coordinates
682
     */
683
    handleTracingResult: function (appendCoords) {
684
        var me = this;
×
685
        me.drawInteraction.removeLastPoint();
×
686
        me.drawInteraction.appendCoordinates(appendCoords);
×
687
    },
688

689
    /**
690
     * Method shows the context menu on mouse right click
691
     * @param {Event} evt The browser event
692
     */
693
    showContextMenu: function (evt) {
694
        // suppress default browser behaviour
695
        evt.preventDefault();
×
696

697
        var me = this.scope;
×
698

699
        var menuItems = [{
×
700
            text: 'Clear All',
701
            handler: function () {
702
                try {
×
703
                    me.drawLayer.getSource().getFeaturesCollection().clear();
×
704
                } catch (error) {
705
                    // sometimes get an error here when trying to clear the features collection
706
                    // Cannot read properties of null (reading 'findIndexBy')
707
                    // TODO debug - seems to occur after the layer is reloaded, so we may need to
708
                    // update the collection on reload? the source still has the same ol_uid
709
                    Ext.log.error(error);
×
710
                }
711
            }
712
        }];
713

714
        var menu = Ext.create('Ext.menu.Menu', {
×
715
            width: 100,
716
            plain: true,
717
            renderTo: Ext.getBody(),
718
            items: menuItems
719
        });
720
        menu.showAt(evt.pageX, evt.pageY);
×
721
    },
722

723
    /**
724
     * Remove the interaction when this component gets destroyed
725
     */
726
    onBeforeDestroy: function () {
727

728
        var me = this;
×
729
        var btn = me.getView();
×
730

731
        // detoggle button
732
        me.onToggle(btn, false);
×
733

734
        // fire the button's toggle event so that the defaultClickEnabled property
735
        // is updated in CpsiMapview.util.ApplicationMixin to re-enable clicks
736
        btn.pressed = false;
×
737
        btn.fireEvent('toggle');
×
738

739

740
        if (me.drawInteraction) {
×
741
            me.map.removeInteraction(me.drawInteraction);
×
742
        }
743

744
        if (me.modifyInteraction) {
×
745
            me.map.removeInteraction(me.modifyInteraction);
×
746
        }
747

748
        if (me.snapInteraction) {
×
749
            me.map.removeInteraction(me.snapInteraction);
×
750
        }
751

752
        if (me.drawLayer) {
×
753
            me.map.removeLayer(me.drawLayer);
×
754
        }
755

756
        me.unBindLayerListeners();
×
757
        me.cleanupTracing();
×
758
    },
759

760
    /**
761
     * Remove event listeners by key, for each key in the listenerKeys array
762
     *
763
     */
764
    unBindLayerListeners: function () {
765
        Ext.Array.each(this.listenerKeys, function (key) {
10✔
766
            ol.Observable.unByKey(key);
×
767
        });
768
        this.listenerKeys = [];
10✔
769
    },
770

771
    init: function () {
772

773
        var me = this;
9✔
774

775
        // create an object for the contextmenu eventhandler
776
        // so it can be removed correctly
777
        me.contextHandler = {
9✔
778
            handleEvent: me.showContextMenu,
779
            scope: me
780
        };
781
    }
782
});
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