Greatly improve marker/line highlight handling

pull/108/head
Candid Dauth 2018-02-24 20:23:20 +01:00
rodzic 876e297599
commit cb23f068da
9 zmienionych plików z 375 dodań i 180 usunięć

Wyświetl plik

@ -0,0 +1,310 @@
import fm from '../app';
import $ from 'jquery';
import L from 'leaflet';
import ng from 'angular';
import 'leaflet-almostover';
fm.app.factory("fmHighlightableLayers", function(fmUtils) {
class Marker extends L.Marker {
constructor(latLng, options) {
options = Object.assign({
riseOnHover: true
}, options);
super(latLng, options);
this.on("mouseover", () => {
this._mouseover = true;
this._initIcon();
});
this.on("mouseout", () => {
this._mouseover = false;
this._initIcon();
});
}
onAdd() {
fmHighlightableLayers._prepareMap(this._map);
super.onAdd(...arguments);
}
_initIcon() {
this.options.icon = fmUtils.createMarkerIcon(this.options.colour, this.options.size, this.options.symbol, this.options.shape, this.options.padding, this.options.highlight);
super._initIcon(...arguments);
this.setOpacity(this.options.highlight || this._mouseover ? 1 : 0.6);
fmHighlightableLayers._updatePane(this, this.options.highlight ? "fmHighlightMarkerPane" : "markerPane");
}
setStyle(style) {
L.Util.setOptions(this, style);
this._initIcon();
return this;
}
}
class Polyline extends L.Polyline {
constructor(latLngs, options) {
super(latLngs, options);
this.borderLayer = new L.Polyline(latLngs, Object.assign({}, options, { interactive: false }));
}
onAdd() {
fmHighlightableLayers._prepareMap(this._map);
super.onAdd(...arguments);
this._map.addLayer(this.borderLayer);
this._map.almostOver.addLayer(this);
this._updateStyle();
}
onRemove() {
this._map.almostOver.removeLayer(this);
this._map.removeLayer(this.borderLayer);
super.onRemove(...arguments);
}
_updateStyle() {
this.options.opacity = this.borderLayer.options.opacity = this.options.highlight ? 1 : 0.35;
this.borderLayer.options.weight = this.options.weight * 2;
this.borderLayer.options.color = "#"+fmUtils.makeTextColour(this.options.color.replace(/^#/, ""));
fmHighlightableLayers._updatePane(this, this.options.highlight ? "fmHighlightPane" : "overlayPane");
fmHighlightableLayers._updatePane(this.borderLayer, this.options.highlight ? "fmHighlightShadowPane" : "fmShadowPane");
}
redraw() {
this._updateStyle();
super.redraw(...arguments);
this.borderLayer.redraw();
return this;
}
setStyle(style) {
L.Util.setOptions(this, style);
L.Util.setOptions(this.borderLayer, style);
this._updateStyle();
super.setStyle({});
this.borderLayer.setStyle({});
return this;
}
setLatLngs(latLngs) {
super.setLatLngs(...arguments);
this.borderLayer.setLatLngs(...arguments);
return this;
}
fire(type, data, propagate) {
// Ignore DOM mouse events, otherwise we get them double, once by DOM, once by almostOver
if([ "mouseover", "mousemove", "mouseout" ].includes(type) && (!data.type || !data.type.startsWith("almost:")))
return;
return super.fire.apply(this, arguments);
}
}
class Polygon extends L.Polygon {
constructor(latLngs, options) {
super(latLngs, options);
this.borderLayer = new L.Polygon(latLngs, Object.assign({}, options, { interactive: false }));
}
onAdd() {
fmHighlightableLayers._prepareMap(this._map);
super.onAdd(...arguments);
this._map.addLayer(this.borderLayer);
this._updateStyle();
}
onRemove() {
this._map.removeLayer(this.borderLayer);
super.onRemove(...arguments);
}
_updateStyle() {
this.borderLayer.options.fill = this.options.highlight;
this.borderLayer.options.fillColor = "#"+fmUtils.makeTextColour(this.options.color.replace(/^#/, ""));
return Polyline.prototype._updateStyle.apply(this, arguments);
}
redraw() {
this._updateStyle();
super.redraw(...arguments);
this.borderLayer.redraw();
return this;
}
setStyle(style) {
L.Util.setOptions(this, style);
L.Util.setOptions(this.borderLayer, style);
this._updateStyle();
super.setStyle({});
this.borderLayer.setStyle({});
return this;
}
setLatLngs(latLngs) {
super.setLatLngs(...arguments);
this.borderLayer.setLatLngs(...arguments);
return this;
}
}
class GeoJSON extends L.GeoJSON {
addData(geojson) {
var features = Array.isArray(geojson) ? geojson : geojson.features,
i, len, feature;
if (features) {
for (i = 0, len = features.length; i < len; i++) {
// only add this if geometry or geometries are set and not null
feature = features[i];
if (feature.geometries || feature.geometry || feature.features || feature.coordinates) {
this.addData(feature);
}
}
return this;
}
var options = this.options;
if (options.filter && !options.filter(geojson)) { return this; }
var layer = this.geometryToLayer(geojson, options);
if (!layer) {
return this;
}
layer.feature = L.GeoJSON.asFeature(geojson);
layer.defaultOptions = layer.options;
this.resetStyle(layer);
if (options.onEachFeature) {
options.onEachFeature(geojson, layer);
}
return this.addLayer(layer);
}
geometryToLayer(geojson, options) {
var geometry = geojson.type === 'Feature' ? geojson.geometry : geojson,
coords = geometry ? geometry.coordinates : null,
layers = [],
_coordsToLatLng = options && options.coordsToLatLng || L.GeoJSON.coordsToLatLng.coordsToLatLng,
latlng, latlngs, i, len;
if (!coords && !geometry) {
return null;
}
switch (geometry.type) {
case 'Point':
latlng = _coordsToLatLng(coords);
return new fmHighlightableLayers.Marker(latlng, this.options.markerOptions);
case 'MultiPoint':
for (i = 0, len = coords.length; i < len; i++) {
latlng = _coordsToLatLng(coords[i]);
layers.push(new fmHighlightableLayers.Marker(latlng, this.options.markerOptions));
}
return new FeatureGroup(layers);
case 'LineString':
case 'MultiLineString':
latlngs = L.GeoJSON.coordsToLatLngs(coords, geometry.type === 'LineString' ? 0 : 1, _coordsToLatLng);
return new fmHighlightableLayers.Polyline(latlngs, this.options);
case 'Polygon':
case 'MultiPolygon':
latlngs = L.GeoJSON.coordsToLatLngs(coords, geometry.type === 'Polygon' ? 1 : 2, _coordsToLatLng);
return new fmHighlightableLayers.Polygon(latlngs, this.options);
case 'GeometryCollection':
for (i = 0, len = geometry.geometries.length; i < len; i++) {
var layer = this.geometryToLayer({
geometry: geometry.geometries[i],
type: 'Feature',
properties: geojson.properties
}, options);
if (layer) {
layers.push(layer);
}
}
return new L.FeatureGroup(layers);
default:
throw new Error('Invalid GeoJSON object.');
}
}
}
let fmHighlightableLayers = {
_prepareMap(map) {
if(map._fmHighlightableLayersPrepared)
return;
for(let paneName of [ "fmHighlightMarkerPane", "fmHighlightShadowPane", "fmHighlightPane", "fmShadowPane" ])
map.createPane(paneName);
map.almostOver.options.distance = 10;
map.on('almost:over', (e) => {
e.layer.fire('mouseover', e, true);
$(map.getContainer()).addClass("fm-almostover");
});
map.on('almost:out', (e) => {
e.layer.fire('mouseout', e, true);
$(map.getContainer()).removeClass("fm-almostover");
});
map.on('almost:click', (e) => {
e.layer.fire('click', e, true);
});
map.on('almost:move', (e) => {
e.layer.fire('mousemove', e, true);
});
map._fmHighlightableLayersPrepared = true;
},
_updatePane(layer, pane) {
if(layer.options.pane == pane)
return;
layer.options.pane = pane;
if(layer._map)
layer._map.removeLayer(layer).addLayer(layer);
},
Marker,
Polygon,
Polyline,
GeoJSON
};
return fmHighlightableLayers;
});

Wyświetl plik

@ -0,0 +1,19 @@
.leaflet-fmHighlightMarker-pane {
z-index: 621;
}
.leaflet-fmHighlight-pane {
z-index: 620;
}
.leaflet-fmHighlightShadow-pane {
z-index: 619;
}
.leaflet-fmShadow-pane {
z-index: 399;
}
.fm-almostover {
cursor: pointer !important;
}

Wyświetl plik

@ -7,12 +7,11 @@ import {saveAs} from 'file-saver';
import css from './lines.scss';
fm.app.factory("fmMapLines", function(fmUtils, $uibModal, $compile, $timeout, $rootScope) {
fm.app.factory("fmMapLines", function(fmUtils, $uibModal, $compile, $timeout, $rootScope, fmHighlightableLayers) {
return function(map) {
var linesById = { };
let openLine = null;
let openLineHighlight = null;
let linePopupBaseScope = $rootScope.$new();
linePopupBaseScope.persistentSettings = {};
@ -65,33 +64,23 @@ fm.app.factory("fmMapLines", function(fmUtils, $uibModal, $compile, $timeout, $r
return linesUi._deleteLine(line);
if(!linesById[line.id]) {
linesById[line.id] = L.polyline([ ]).addTo(map.map);
map.map.almostOver.addLayer(linesById[line.id]);
linesById[line.id] = (new fmHighlightableLayers.Polyline([ ])).addTo(map.map);
if(line.id != null) { // We don't want a popup for lines that we are drawing right now
linesById[line.id]
.on("click", function(e) {
linesUi.showLineInfoBox(map.client.lines[line.id]);
}.fmWrapApply($rootScope))
.on("fm-almostover", function(e) {
if(!linesById[line.id].getTooltip())
linesById[line.id].bindTooltip("", $.extend({}, map.tooltipOptions, { permanent: true, offset: [ 20, 0 ] }));
.bindTooltip("", $.extend({}, map.tooltipOptions, { sticky: true, offset: [ 20, 0 ] }))
.on("tooltipopen", function(e) {
linesById[line.id].setTooltipContent(fmUtils.quoteHtml(map.client.lines[line.id].name)).openTooltip(e.latlng);
})
.on("fm-almostmove", function(e) {
linesById[line.id].openTooltip(e.latlng);
})
.on("fm-almostout", function() {
linesById[line.id].unbindTooltip();
});
}
}
var style = {
color : '#'+line.colour,
weight : line.width,
opacity : 0.5
weight : line.width
};
// Two points that are both outside of the viewport should not be connected, as the piece in between
@ -100,24 +89,11 @@ fm.app.factory("fmMapLines", function(fmUtils, $uibModal, $compile, $timeout, $r
linesById[line.id].setLatLngs(splitLatLngs).setStyle(style);
if(line.id != null && openLine && line.id == openLine.id) {
openLineHighlight.setLatLngs(splitLatLngs).setStyle(Object.assign(style, {
weight: Math.round(line.width*2),
color: "#"+fmUtils.makeTextColour(line.colour),
opacity: 1
}));
if(!_doNotRerenderPopup)
linesUi.showLineInfoBox(line);
}
if(line.id != null && openLine && line.id == openLine.id && !_doNotRerenderPopup)
linesUi.showLineInfoBox(line);
},
_deleteLine: function(line) {
if(line.id != null && openLine && line.id == openLine.id) {
if(openLineHighlight) {
openLineHighlight.remove();
openLineHighlight = null;
}
openLine.hide();
openLine = null;
}
@ -162,26 +138,15 @@ fm.app.factory("fmMapLines", function(fmUtils, $uibModal, $compile, $timeout, $r
hide: map.infoBox.show(template, scope, () => {
openLine = null;
if(linesById[line.id]) { // Does not exist anymore after line was deleted
linesById[line.id].remove();
linesById[line.id].options.pane = "overlayPane";
linesById[line.id].addTo(map.map);
linesById[line.id].setStyle({ highlight: false });
}
if(openLineHighlight)
openLineHighlight.remove();
scope.$destroy();
}).hide,
id: line.id
};
openLineHighlight = L.polyline([ ], {
pane: "fmHighlightShadowPane",
interactive: false
}).addTo(map.map);
linesById[line.id].remove();
linesById[line.id].options.pane = "fmHighlightPane";
linesById[line.id].addTo(map.map);
linesById[line.id].setStyle({ highlight: true });
linesUi._addLine(line, true); // To render the openLineHighlight

Wyświetl plik

@ -1,7 +1,6 @@
import fm from '../../app';
import $ from 'jquery';
import L from 'leaflet';
import 'leaflet-almostover';
import 'leaflet.locatecontrol';
import 'leaflet.markercluster';
import 'leaflet-mouse-position';
@ -146,10 +145,6 @@ fm.app.directive("facilmap", function(fmUtils, fmMapMessages, fmMapMarkers, $com
this.map = L.map($(".fm-map", $element)[0]);
this.map.createPane("fmHighlightMarkerPane");
this.map.createPane("fmHighlightShadowPane");
this.map.createPane("fmHighlightPane");
this.map._controlCorners.bottomcenter = L.DomUtil.create("div", "leaflet-bottom fm-leaflet-center", this.map._controlContainer);
$scope.$watch("client.padData.clusterMarkers", (clusterMarkers) => {
@ -172,8 +167,6 @@ fm.app.directive("facilmap", function(fmUtils, fmMapMessages, fmMapMarkers, $com
this.markerCluster.addLayer(marker);
});
this.map.almostOver.options.distance = 10;
let locateControl = L.control.locate({
flyTo: true,
icon: "a",
@ -193,24 +186,6 @@ fm.app.directive("facilmap", function(fmUtils, fmMapMessages, fmMapMarkers, $com
position: "bottomcenter"
}).addTo(this.map);
this.map.on('almost:over', (e) => {
e.layer.fire('fm-almostover', e);
$(this.map.getContainer()).addClass("fm-almostover");
});
this.map.on('almost:out', (e) => {
e.layer.fire('fm-almostout', e);
$(this.map.getContainer()).removeClass("fm-almostover");
});
this.map.on('almost:click', (e) => {
e.layer.fire('click', e, true);
});
this.map.on('almost:move', (e) => {
e.layer.fire('fm-almostmove', e);
});
this.startMarkerColour = "00ff00";
this.dragMarkerColour = "ffd700";
this.endMarkerColour = "ff0000";

Wyświetl plik

@ -11,10 +11,6 @@
height: 100%;
}
.fm-almostover {
cursor: pointer !important;
}
.fm-clickHandler {
cursor: crosshair;
position: absolute;
@ -117,15 +113,3 @@
text-shadow: 0 0 3px #fff, 0 0 5px #fff, 0 0 10px #fff;
}
}
.leaflet-fmHighlightMarker-pane {
z-index: 621;
}
.leaflet-fmHighlight-pane {
z-index: 620;
}
.leaflet-fmHighlightShadow-pane {
z-index: 619;
}

Wyświetl plik

@ -3,7 +3,7 @@ import $ from 'jquery';
import L from 'leaflet';
import ng from 'angular';
fm.app.factory("fmMapMarkers", function($uibModal, fmUtils, $compile, $timeout, $rootScope) {
fm.app.factory("fmMapMarkers", function($uibModal, fmUtils, $compile, $timeout, $rootScope, fmHighlightableLayers) {
return function(map) {
var markersById = { };
let openMarker;
@ -30,9 +30,7 @@ fm.app.factory("fmMapMarkers", function($uibModal, fmUtils, $compile, $timeout,
var markersUi = {
_addMarker : function(marker) {
if(!markersById[marker.id]) {
markersById[marker.id] = L.marker([ 0, 0 ], {
icon: fmUtils.createMarkerIcon(marker.colour, marker.size, marker.symbol, marker.shape)
}).addTo(map.markerCluster)
markersById[marker.id] = (new fmHighlightableLayers.Marker([ 0, 0 ])).addTo(map.markerCluster)
.on("click", function(e) {
markersUi.showMarkerInfoBox(map.client.markers[marker.id] || marker);
}.fmWrapApply($rootScope))
@ -44,8 +42,13 @@ fm.app.factory("fmMapMarkers", function($uibModal, fmUtils, $compile, $timeout,
markersById[marker.id]
.setLatLng([ marker.lat, marker.lon ])
.setIcon(fmUtils.createMarkerIcon(marker.colour, marker.size, marker.symbol, marker.shape, null, openMarker && openMarker.id == marker.id))
.setOpacity(0.7);
.setStyle({
colour: marker.colour,
size: marker.size,
symbol: marker.symbol,
shape: marker.shape,
highlight: openMarker && openMarker.id == marker.id
});
if(openMarker && openMarker.id == marker.id)
markersUi.showMarkerInfoBox(marker);
@ -91,11 +94,9 @@ fm.app.factory("fmMapMarkers", function($uibModal, fmUtils, $compile, $timeout,
openMarker = null;
if(markersById[marker.id]) {
markersById[marker.id].remove();
markersById[marker.id].options.pane = "markerPane";
markersById[marker.id].setIcon(fmUtils.createMarkerIcon(marker.colour, marker.size, marker.symbol, marker.shape));
markersById[marker.id].setOpacity(0.7);
markersById[marker.id].addTo(map.map);
markersById[marker.id].setStyle({
highlight: false
});
}
scope.$destroy();
@ -103,11 +104,9 @@ fm.app.factory("fmMapMarkers", function($uibModal, fmUtils, $compile, $timeout,
id: marker.id
};
markersById[marker.id].remove();
markersById[marker.id].options.pane = "fmHighlightMarkerPane";
markersById[marker.id].setIcon(fmUtils.createMarkerIcon(marker.colour, marker.size, marker.symbol, marker.shape, null, true));
markersById[marker.id].setOpacity(1);
markersById[marker.id].addTo(map.map);
markersById[marker.id].setStyle({
highlight: true
});
},
editMarker: function(marker) {
var scope = $rootScope.$new();

Wyświetl plik

@ -5,7 +5,7 @@ import ng from 'angular';
import 'leaflet.elevation';
import {saveAs} from 'file-saver';
fm.app.factory("fmMapRoute", function(fmUtils, $uibModal, $compile, $timeout, $rootScope) {
fm.app.factory("fmMapRoute", function(fmUtils, $uibModal, $compile, $timeout, $rootScope, fmHighlightableLayers) {
return function(map) {
var dragTimeout = 300;
@ -16,7 +16,6 @@ fm.app.factory("fmMapRoute", function(fmUtils, $uibModal, $compile, $timeout, $r
});
let routeLayer = null;
let highlightLayer = null;
let dragMarker = null;
let markers = [ ];
let dragging = false;
@ -50,34 +49,14 @@ fm.app.factory("fmMapRoute", function(fmUtils, $uibModal, $compile, $timeout, $r
// has not been received.
let splitLatLngs = fmUtils.disconnectSegmentsOutsideViewport(trackPoints, map.map.getBounds());
highlightLayer = L.polyline(splitLatLngs, {
pane: "fmHighlightShadowPane",
interactive: false,
color : '#000000',
weight : 10,
opacity : 1
}).on("click", function(e) {
routeUi.showRouteInfoBox();
}.fmWrapApply($rootScope));
routeLayer = L.polyline(splitLatLngs, {
pane: "fmHighlightPane",
routeLayer = (new fmHighlightableLayers.Polyline(splitLatLngs, {
color : '#0000ff',
weight : 5,
opacity : 0.5
}).addTo(map.map).on("click", function(e) {
highlight: !!openInfoBox
})).addTo(map.map).on("click", function(e) {
routeUi.showRouteInfoBox();
}.fmWrapApply($rootScope));
if (openInfoBox) {
highlightLayer.addTo(map.map);
routeLayer.setStyle({
opacity: 1
});
}
map.map.almostOver.addLayer(routeLayer);
dragMarker = fmUtils.temporaryDragMarker(map.map, routeLayer, map.dragMarkerColour, function(marker) {
dragging = true;
@ -154,16 +133,10 @@ fm.app.factory("fmMapRoute", function(fmUtils, $uibModal, $compile, $timeout, $r
function clearRoute() {
if(routeLayer) {
map.map.almostOver.removeLayer(routeLayer);
routeLayer.remove();
routeLayer = null;
}
if(highlightLayer) {
highlightLayer.remove();
highlightLayer = null;
}
if(dragMarker) {
dragMarker();
dragMarker = null;
@ -221,20 +194,17 @@ fm.app.factory("fmMapRoute", function(fmUtils, $uibModal, $compile, $timeout, $r
let template = $(require("./view-route.html"));
highlightLayer.addTo(map.map);
routeLayer.setStyle({
opacity: 1
highlight: true
});
openInfoBox = map.infoBox.show(template, scope, () => {
scope.$destroy();
openInfoBox = null;
if(highlightLayer)
highlightLayer.remove();
if(routeLayer) {
routeLayer.setStyle({
opacity: 0.5
highlight: false
});
}
});

Wyświetl plik

@ -6,7 +6,7 @@ import 'jquery-ui';
import 'jquery-ui/ui/widgets/resizable';
import css from './search-query.scss';
fm.app.directive("fmSearchQuery", function($rootScope, $compile, fmUtils, $timeout, $q, fmSearchFiles, fmSearchImport) {
fm.app.directive("fmSearchQuery", function($rootScope, $compile, fmUtils, $timeout, $q, fmSearchFiles, fmSearchImport, fmHighlightableLayers) {
return {
require: "^fmSearch",
scope: true,
@ -270,25 +270,20 @@ fm.app.directive("fmSearchQuery", function($rootScope, $compile, fmUtils, $timeo
result.layer.remove();
result.layer = null;
}
if(result.highlightLayer) {
result.highlightLayer.remove();
result.highlightLayer = null;
}
if(result.marker) {
result.marker.remove();
result.marker = null;
}
if(!result.lat || !result.lon || (result.geojson && result.geojson.type != "Point")) { // If the geojson is just a point, we already render our own marker
result.layer = L.geoJson(result.geojson, {
pane: highlight ? "fmHighlightPane" : "overlayPane",
pointToLayer: function(geoJsonPoint, latlng) {
return L.marker(latlng, {
icon: fmUtils.createMarkerIcon("ff0000", 35, null, null, null, highlight),
pane: highlight ? "fmHighlightMarkerPane" : "markerPane"
});
result.layer = (new fmHighlightableLayers.GeoJSON(result.geojson, {
highlight,
markerOptions: {
colour: "ff0000",
size: 35,
highlight
}
})
}))
.on("click", function(e) {
renderResult(query, results, result, true, layerGroup, onOpen, onClose, true);
onOpen && onOpen();
@ -296,36 +291,15 @@ fm.app.directive("fmSearchQuery", function($rootScope, $compile, fmUtils, $timeo
.bindTooltip(result.display_name, $.extend({}, map.tooltipOptions, { sticky: true, offset: [ 20, 0 ] }));
layerGroup.addLayer(result.layer);
if(highlight) {
result.highlightLayer = L.geoJson(result.geojson, {
pane: "fmHighlightShadowPane",
pointToLayer: function(geoJsonPoint, latlng) {
return L.marker(latlng, {
icon: fmUtils.createMarkerIcon("ff0000", 35, null, null, null, highlight),
pane: highlight ? "fmHighlightMarkerPane" : "markerPane"
});
},
style: (feature) => ({
color: '#000000',
fill: false,
weight: 6
})
})
.on("click", function(e) {
renderResult(query, results, result, true, layerGroup, onOpen, onClose, true);
onOpen && onOpen();
}.fmWrapApply($rootScope));
layerGroup.addLayer(result.highlightLayer);
}
}
if(result.lat != null && result.lon != null) {
result.marker = L.marker([ result.lat, result.lon ], {
pane: highlight ? "fmHighlightMarkerPane" : "markerPane",
icon: fmUtils.createMarkerIcon(map.searchMarkerColour, 35, result.icon, null, null, highlight)
})
result.marker = (new fmHighlightableLayers.Marker([ result.lat, result.lon ], {
highlight,
colour: map.searchMarkerColour,
size: 35,
symbol: result.icon
}))
.on("click", function(e) {
renderResult(query, results, result, true, layerGroup, onOpen, onClose, true);
onOpen && onOpen();

Wyświetl plik

@ -3,7 +3,6 @@ import $ from 'jquery';
import L from 'leaflet';
import ng from 'angular';
import 'leaflet-geometryutil';
import 'leaflet-almostover';
import linkifyStr from 'linkifyjs/string';
import Clipboard from 'clipboard';
import SimpleGraticule from 'leaflet-simple-graticule';
@ -319,7 +318,7 @@ fm.app.factory("fmUtils", function($parse, fmIcons) {
temporaryHoverMarker.remove();
}
line.on("fm-almostover", _over).on("fm-almostmove", _move).on("fm-almostout", _out);
line.on("mouseover", _over).on("mousemove", _move).on("mouseout", _out);
function makeTemporaryHoverMarker() {
temporaryHoverMarker = L.marker([0,0], Object.assign({
@ -341,7 +340,7 @@ fm.app.factory("fmUtils", function($parse, fmIcons) {
makeTemporaryHoverMarker();
return function() {
line.off("fm-almostover", _over).off("fm-almostmove", _move).off("fm-almostout", _out);
line.off("mouseover", _over).off("mousemove", _move).off("mouseout", _out);
temporaryHoverMarker.remove();
};
};