Delete feature from multiple layers with Leaflet.draw - leaflet

There are multiple layers displayed in a map and I'd like to have a delete button to delete selected markers from these layers.
I am using Leaflet.draw plugin to implement that, but it seems that the draw control has to be bind to a single layer whereas I have multiple layers.
I tried to copy all features in layers to a FeatureGroup and bind the FeatureGroup with drawing control to accomplish that, but it broke the Layers control behavior since I cannot control the visibility of layers anymore. (Duplicated features in FeatureGroup).
Is there any way to enable multiple layer feature deleting without breaking layer visibility control?
http://jsfiddle.net/mdqvpLvx/7/
var layerControl = new L.Control.Layers({}, {});
var geojsonLayer = L.geoJson(data);
geojsonLayer.getLayers()[0].addTo(drawnItems);
var geojsonLayer2 = L.geoJson(data2);
geojsonLayer2.getLayers()[0].addTo(drawnItems);
map.addControl(layerControl);
layerControl.addOverlay(geojsonLayer, 'layer1');
layerControl.addOverlay(geojsonLayer2, 'layer2');

I think I still have to keep copies of the markers in FeatureCollection layer and delete them accordingly from particular layers in drawDelete event. And add/remove markers from FeatureCollection layer when layers visibility changes from LayerControl.
// Draw Control
var editableFeatures = L.featureGroup();
map.addLayer(editableFeatures);
var drawControl = new L.Control.Draw({
draw: false,
edit: {
edit: false,
featureGroup: editableFeatures
}
});
map.on('draw:deleted', function(e) {
var layers = e.layers;
layers.eachLayer(function(feature) {
map.eachLayer(function(mapLayer) {
if (mapLayer.hasLayer) {
mapLayer.removeLayer(feature);
}
});
});
});
map.addControl(drawControl);
//LayerControl
var layerControl = new L.Control.Layers({}, {});
map.addControl(layerControl);
map.on('overlayadd', function(layer, name) {
layer.layer.eachLayer(function(feature) {
editableFeatures.addLayer(feature);
});
});
map.on('overlayremove', function(layer, name) {
layer.layer.eachLayer(function(feature) {
editableFeatures.removeLayer(layer);
});
});

Related

sorting only the overlays in the control layer dialog in leafletjs

L.control.layers takes up to three parameters - baselayers, overlays and options. I'd like to be able to sort the overlays as they appear in the layer control but am not sure how to do that.
I used https://leafletjs.com/examples/layers-control/example.html as my starting point. So that example has this line:
var layerControl = L.control.layers(baseLayers, overlays).addTo(map);
I replaced that with this:
var options = {
autoZIndex: false,
sortLayers: true,
sortFunction: function(layerA, layerB, nameA, nameB) {
return -('' + nameA).localeCompare(nameB);
}
};
var layerControl = L.control.layers(baseLayers, overlays, options).addTo(map);
Problem is that that sorts both the overlays and the layers. I'd like to just sort the overlays.
Any ideas?
My JS fiddle:
https://jsfiddle.net/7e84rh06/1/

Changing instance of Feature on click

Im trying to find a way to change the instance of a Leaflet geoJson Feature after it is added to the map.
This is what I want to achieve:
Importing data with L.GeoJson and I am using pointToLayer to change the marker to L.CircleMarker
Now I want
layer.on('click', function (e) {
e.target //Do something here to change it from L.CircleMarker to L.Marker
});
Any idea how to achieve this?
var group = L.geoJSON(); // Your geojson group on importing
layer.on('click', function (e) {
var circlemarker = e.target //Do something here to change it from L.CircleMarker to L.Marker
var marker = L.marker(circlemarker.getLatLng()).addTo(group);
marker.feature = circlemarker.feature
circlemarker.removeFrom(group)
// Then add the same events to the layer as in pointToLayer
});

Trigger event on multiple features when hovering over any of those in leaflet

I have a layer with two polylines and polylineDecorators. I would like to highlight both polylines and polylineDecorators when I hover on any of these. Right now I'm able to highlight only one at the time when hovering on it.
Here is my code:
var layer_migration = L.layerGroup({
layerName: 'layer_migration',
pane: 'linesPane',
});
function onEachFeature_migration (feature, layer) {
var polyline = L.polyline(layer.getLatLngs(),{
color: "#8B0000",weight: 5,opacity: 0.4,dashArray: '8,8',
dashOffset: 0
}).addTo(layer_migration);
var PLdecorator1 = L.polylineDecorator(polyline, {
patterns: [{
offset: '104%',
repeat: 100,
symbol: L.Symbol.arrowHead({pixelSize: 16,
pathOptions: {color: "#8B0000",fillOpacity: 0.6,weight: 0
}
})
}]
}).addTo(layer_migration)
var myfeatures = L.featureGroup([polyline,PLdecorator1]).addTo(layer_migration);
myfeatures.on('mouseover', function(e) {
var layer = e.target;
layer.setStyle({color: '#8B0000',opacity: 1,fillOpacity:1
});
});
}
Any help super appreciated.
Thanks,
P
In your mouseover callback, I think that e.target will just refer to the individual layer (polyline or decorator) that triggered the event, not the collection of layers that make up the feature group. I've not tested it, but according to the docs, you ought to be able to get the effect you want by calling .setLayer() on the feature group itself:
myfeatures.on('mouseover', function(e) {
myfeatures.setStyle({color: '#8B0000',opacity: 1,fillOpacity:1});
});
Also, if the two polylines are created by two separate calls to onEachFeature_migration(), then they will end up as two separate feature groups. To get around this, you might need to assign an empty featureGroup to myfeatures outside the function, then add the new polylines to it inside the function using myfeatures.addLayer().

Getting the bounds of loaded tile of leaflet

Using leaflet is there any way I can get the bounds (NorthEast, SouthWest) of a loaded tile? I want to request the server to load the markers only for a particular tile which is loaded, so that when user is panning/dragging the map he can easily see the new markers on new area.
What you really want to do is a subclass of L.GridLayer. This will allow fine control over loaded/unloaded tiles, and is the best way to use the private L.GridLayer._tileCoordsToBounds() method.
With some basic handling of loaded markers, it should look like:
L.GridLayer.MarkerLoader = L.GridLayer.extend({
onAdd: function(map){
// Add a LayerGroup to the map, to hold the markers
this._markerGroup = L.layerGroup().addTo(map);
L.GridLayer.prototype.onAdd.call(this, map);
// Create a tilekey index of markers
this._markerIndex = {};
},
onRemove: function(map){
this._markergroup.removeFrom(map);
L.GridLayer.prototype.onRemove.call(this, map);
};
createTile: function(coords, done) {
var tileBounds = this._tileCoordsToBounds(coords);
var tileKey = this._tileCoordsToKey(coords);
var url = ...; // Compute right url using tileBounds & coords.z
fetch(url).then(function(res){
if (!key in this._markerIndex) { this._markerIndex[key] = []; }
// unpack marker data from result, instantiate markers
// Loop as appropiate
this._markerGroup.addLayer(marker);
this._markerIndex[key] = marker;
done(); // Signal that the tile has been loaded successfully
});
},
_removeTile: function (key) {
for (var i in this._markerIndex[key]) {
this._markerGroup.remove(this._markerIndex[key][i]);
}
L.GridLayer.prototype._removeTile.call(this, key);
}
});
Please note that zooming might be a source of bugs and graphical glitches (markers being removed because a tile unloads, before the markers at the new zoom level are loaded). Beware of that.

MapBox - Add a clusterGroup clickable with Layer Control

I'm still learning and I'm a bit stuck. I may be trying to do to much at once. I have a MapBox map working great with a clickable layer menu taken from examples on the MapBox site. I also have a MarkerClusterGroup which also works and is always visible on the map. Is there a way I could somehow have the MarkerClusterGroup clickable on/off just like layers identified in var overlays = { ...
Below is the code that I think needs the help:
var layers = {
Streets: L.mapbox.tileLayer('mapbox.streets').addTo(map),
Satellite: L.mapbox.tileLayer('mapbox.satellite'),
Light: L.mapbox.tileLayer('mapbox.light'),
};
var overlays = {
DataA: L.mapbox.featureLayer().loadURL('/data/ctsnew.geojson'),
DataB: L.mapbox.featureLayer().loadURL('/data/selectZipcodes.geojson'),
};
// Since featureLayer is an asynchronous method, we use the `.on('ready'`
// call to only use its marker data once we know it is actually loaded.
Markers: L.mapbox.featureLayer('examples.map-h61e8o8e').on('ready', function(e) {
// The clusterGroup gets each marker in the group added to it
// once loaded, and then is added to the map
var clusterGroup = new L.MarkerClusterGroup();
e.target.eachLayer(function(layer) {
clusterGroup.addLayer(layer);
});
map.addLayer(clusterGroup);
});
Could be something as simple as misuse of brackets. Thanks in advance.
You have to include your Marker Cluster Group in your overlays object. For example you could instantiate it just before defining overlays, even if your Cluster Group is empty for now.
Then you fill it once it has downloaded its data.
var layers = {
Streets: L.mapbox.tileLayer('mapbox.streets').addTo(map),
Satellite: L.mapbox.tileLayer('mapbox.satellite'),
Light: L.mapbox.tileLayer('mapbox.light'),
};
var clusterGroup = L.markerClusterGroup();
var overlays = {
DataA: L.mapbox.featureLayer().loadURL('/data/ctsnew.geojson'),
DataB: L.mapbox.featureLayer().loadURL('/data/selectZipcodes.geojson'),
Markers: clusterGroup
};
// Since featureLayer is an asynchronous method, we use the `.on('ready'`
// call to only use its marker data once we know it is actually loaded.
L.mapbox.featureLayer('examples.map-h61e8o8e').on('ready', function(e) {
// The clusterGroup gets each marker in the group added to it
// once loaded, and then is added to the map
e.target.eachLayer(function(layer) {
clusterGroup.addLayer(layer);
});
map.addLayer(clusterGroup); // use that line if you want to automatically add the cluster group to the map once it has downloaded its data.
});