sorting only the overlays in the control layer dialog in leafletjs - leaflet

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/

Related

Mapbox: Filtering out markers in a Leaflet Omnivore KML layer

I am exporting Google Directions routes as KML and displaying them on a Mapbox map by reading them with Omnivore and adding them to the map,
The Google KML stores each route as two Places (the start and end points) and one LineString (the route). In Mapbox I would like to show only the routes, that is to filter out the markers somehow. I'm displaying markers out of my own database and the Google markers clutter it up.
Here is my code. I change the styling of the LineStrings just to show that I can, but do not know what magic call(s) to make to not display the Points.
Thanks.
runLayer = omnivore.kml('data/xxxx.kml')
.on('ready', function() {
var llBnds = runLayer.getBounds();
map.fitBounds(llBnds);
this.eachLayer(function (layer) {
if (layer.feature.geometry.type == 'LineString') {
layer.setStyle({
color: '#4E3508',
weight: 4
});
}
if (layer.feature.geometry.type == 'Point') {
//
// Do something useful here to not display these items!!
//
}
});
})
.addTo(map);
Welcome to SO!
Many possible solutions:
Most straight forward from the code you provided, just use the removeLayer method on your runLayer Layer Group when you get a 'Point' feature.
Cleaner solution would be to filter out those features before they are even converted into Leaflet layers, through a custom GeoJSON Layer Group passed as 3rd argument of omnivore.kml, with a specified filter option:
var customLayer = L.geoJSON(null, {
filter: function(geoJsonFeature) {
// my custom filter function: do not display Point type features.
return geoJsonFeature.geometry.type !== 'Point';
}
}).addTo(map);
var runLayer = omnivore.kml('data/xxxx.kml', null, customLayer);
You can also use the style and/or onEachFeature options on customLayer to directly apply your desired style on your LineString.

Integrating Pane TileLayers with LayerGroup Control (V1.0)

I am trying to find a method of integrating the layer group control with the method of having 2 tile layers visible to enable the labels to sit above the polygons I generate.
http://leafletjs.com/examples/layers-control.html - layergroup guide
http://leafletjs.com/examples/map-panes.html - panes guide
My aim is to have the often used dark and light cartodb maps as options - but still making use of the panes functionality.
I have attempted to have something like the below however I don't believe leaflet is capable of handling it in this way.
Has anyone found a method yet to integrate this correctly?
var darkTile = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_nolabels/{z}/{x}/{y}.png', {
attribution: '©OpenStreetMap, ©CartoDB'
}).addTo(map);
var darkLabels = L.tileLayer('http://{s}.basemaps.cartocdn.com/dark_only_labels/{z}/{x}/{y}.png', {
attribution: '©OpenStreetMap, ©CartoDB',
pane: 'labels'
}).addTo(map);
var lightTile = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_nolabels/{z}/{x}/{y}.png', {
attribution: '©OpenStreetMap, ©CartoDB'
});//.addTo(map);
var lightLabels = L.tileLayer('http://{s}.basemaps.cartocdn.com/light_only_labels/{z}/{x}/{y}.png', {
attribution: '©OpenStreetMap, ©CartoDB',
pane: 'labels'
});//.addTo(map);
var light = {
lightTile,
lightLabels
};
var dark = {
darkTile,
darkLabels
};
var baseMaps = {
"Light" : light,
"Dark" : dark
};
L.control.layers(baseMaps).addTo(map);
Make sure you create your pane explicitly:
map.createPane("labels");
Create Layer Groups to gather your Tile Layers (basemap and labels) instead of plain JS objects. Leaflet will not use your plain objects.
var light = L.layerGroup([
lightTile,
lightLabels
]);
var dark = L.layerGroup([
darkTile,
darkLabels
]).addTo(map); // Add the group to map, rather than its individual layers.
Demo: http://jsfiddle.net/3v7hd2vx/45/

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.
});

Delete feature from multiple layers with Leaflet.draw

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);
});
});

Layer order changing when turning layer on/off

I have two geoJson layers being loaded - both layers are the same data for testing purposes, but being drawn from two different json files. When I turn the layers on and off in the layer controller, the draw order of the layers change.
Any ideas why this is happening?
I have put my code into a JSFiddle: http://jsfiddle.net/lprashad/ph5y9/10/ and the JS is below:
//styling for watersheds_copy
var Orange = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
var Water_Orange = L.geoJson(watersheds_copy, {
style: Orange
});
Water_Orange.addData(watersheds_copy);
//these are blue
var Water_blue = L.geoJson(watersheds, {});
Water_blue.addData(watersheds);
//This sets the inital order - last in layer list being on top. Except minimal - tile layer is always on bottom
var map = L.map('map', {
center: [41.609, -74.028],
zoom: 8,
layers: [minimal, Water_Orange, Water_blue]
});
var baseLayers = {
"Minimal": minimal,
"Night View": midnight
};
//This controls the order in the layer switcher. This does not change draw order
var overlays = {
"Water_Orange": Water_Orange,
"Water_blue": Water_blue
};
L.control.layers(baseLayers, overlays).addTo(map);
LP
While searching I happened upon this site that shows some of the Leaflet code:
http://ruby-doc.org/gems/docs/l/leaflet-js-0.7.0.3/lib/leaflet/src/control/Control_Layers_js.html
In it I found this condition for the application of autoZIndex:
if (this.options.autoZIndex && layer.setZIndex) {
this._lastZIndex++;
layer.setZIndex(this._lastZIndex);
}
TileLayer is the only layer type that has a setZIndex function, so apparently autoZIndex only works there.
I'm not sure which annoys me more. This incredible limitation or the fact that Leafet documentation doesn't point it out.
At least on 0.7.2, I had to use bringToFront in the callback of map.on('overlayadd'). autoZIndex: false did not work in my case neither. A comment on this issue may explain the reason.
It's not specific to L.GeoJson layers. As far as I can tell, it's true of all Leaflet layers with layer control. The last layer turned on is simply on top. I don't think this is a bug either. It's predictable behavior which I use and depend on when I'm designing maps with layer control...