Leaflet: How to toggle GeoJSON feature properties from a single collection? - leaflet

I have a single GeoJSON object that contains over 2000+ features and each feature is part of one category (i.e. "Electrical", "Military", etc). There are a total of about 38 categories.
Here's the schema example of my collection:
{"type":"Feature","properties":{"category":"Electrical","Name":"Plant No 1"},"geometry":{"type":"Point","coordinates":[81.73828125,62.59334083012024]}},{"type":"Feature","properties":{"category":"Electrical","Name":"Plane No 2"},"geometry":{"type":"Point","coordinates":[94.5703125,58.722598828043374]}},{"type":"Feature","properties":{"category":"Military","Name":"Base 1"},"geometry":{"type":"Point","coordinates":[104.4140625,62.91523303947614]}}
Here's my L.geoJson function that iterates through the
collection:
var allPoints = L.geoJson(myCollection, {
onEachFeature: function(feature, layer){
layer.bindPopup(L.Util.template(popTemplate, feature.properties));
},
"stylel": function(feature){
return { "color": legend[feature.properties.category]
}
}}).addTo(map);
How can I assign each category property to my L.control function so the user can toggle on/off the various categories from the collection? I could do this if I made each category a dataset and an individual geoJSOn layer, but that's too much work to do all 38 categories.
My attempt:
L.control.layers({
'Street Map': L.mapbox.tileLayer('mapbox.streets').addTo(map)
},{
'Electrical': myCollection[feature.properties.category["Electrical"]],
'Military': myCollection[feature.properties.category["Military"]]
});
Is there a better way to do this? Thanks!

You can simply assign your layers within the onEachFeature function. You could even automate the creation of Layer Groups for each category.
Result:
var categories = {},
category;
function onEachFeature(feature, layer) {
layer.bindPopup(L.Util.template(popTemplate, feature.properties));
category = feature.properties.category;
// Initialize the category array if not already set.
if (typeof categories[category] === "undefined") {
categories[category] = [];
}
categories[category].push(layer);
}
// Use function onEachFeature in your L.geoJson initialization.
var overlays = {},
categoryName,
categoryArray;
for (categoryName in categories) {
categoryArray = categories[categoryName];
overlays[categoryName] = L.layerGroup(categoryArray);
}
L.control.layers(basemaps, overlays).addTo(map);
EDIT: replaced overlays to be a mapping instead of an array.

Iterate your GeoJSON collection and create multiple L.GeoJSON layers, one per category and add them as overlays to your L.Control.Layers instance.
var controlLayers = L.control.layers({
'Street Map': L.mapbox.tileLayer('mapbox.streets').addTo(map)
}).addTo(map);
// Object to store category layers
var overlays = {};
// Iterate the collection
collection.features.forEach(function (feature) {
var category = feature.properties.category;
// Check if there's already an overlay for this category
if (!overlays[category]) {
// Create and store new layer in overlays object
overlays[category] = new L.GeoJSON(null, {
'onEachFeature': function () {},
'style': function () {}
});
// Add layer/title to control
controlLayers.addOverlay(overlays[category], category);
}
// Add feature to corresponding layer
overlays[category].addData(feature);
});
Here's an example on Plunker: http://plnkr.co/edit/Zv2xwv?p=preview

Related

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().

How to update geojson markers periodically

What I am trying to do is to use Leaflet with OSM map,
and load data from PHP in GeoJSON format + update periodically.
I can manage to display a map, load data, but do not know how to update points instead of still adding a new ones.
function update_position() {
$.getJSON('link_to_php', function(data) {
//get data into object
var geojsonFeature = JSON.parse(data);
// how to remove here old markers???
//add new layer
var myLayer = L.geoJSON().addTo(mymap);
//add markers to layet
myLayer.addData(geojsonFeature);
setTimeout(update_position, 1000);
});
}
update_position();
have tried mymap.removeLayer("myLayer"); but this seems to now work inside of function. Please help
L.geoJSON extends from LayerGroup which provide a function named clearLayers(docs), so you call that to clear markers from the layer.
Also, it is recommended that you put the layer variable outside the function:
var geoJSONLayer = L.geoJSON().addTo(mymap);
function update_position() {
$.getJSON('link_to_php', function(data) {
//get data into object
var geojsonFeature = JSON.parse(data);
geoJSONLayer.clearLayers();
//add markers to layet
geoJSONLayer.addData(geojsonFeature);
setTimeout(update_position, 1000);
});
}
update_position();

How to get selected layers in control.layers?

Is there a way to select all selected layers in the control.layers with leaflet api?
I can do it with the help of jquery like this :
$('.leaflet-control-layers-selector:checked')
But maybe there is an api?
Thanks
There is no API for that but you could easily create one yourself:
// Add method to layer control class
L.Control.Layers.include({
getActiveOverlays: function () {
// Create array for holding active layers
var active = [];
// Iterate all layers in control
this._layers.forEach(function (obj) {
// Check if it's an overlay and added to the map
if (obj.overlay && this._map.hasLayer(obj.layer)) {
// Push layer to active array
active.push(obj.layer);
}
});
// Return array
return active;
}
});
var control = new L.Control.Layers(...),
active = control.getActiveOverlays();
Based on iH8's answer
L.Control.Layers.include({
getOverlays: function() {
// create hash to hold all layers
var control, layers;
layers = {};
control = this;
// loop thru all layers in control
control._layers.forEach(function(obj) {
var layerName;
// check if layer is an overlay
if (obj.overlay) {
// get name of overlay
layerName = obj.name;
// store whether it's present on the map or not
return layers[layerName] = control._map.hasLayer(obj.layer);
}
});
return layers;
}
});
Now you can use
var control = new L.Control.Layers(...)
control.getOverlays(); // { Truck 1: true, Truck 2: false, Truck 3: false }
I find this a little more useful because
all the layers are included
the key is the name of the layer
if the layer is showing, it has a value of of true, else false

Leaflet Draw not taking properties when converting FeatureGroup to GeoJson

I'm unable to convert my Layer properties into the properties of the GEOJson object using Leaflet(0.7.7)/Leaflet.Draw(latest). My workflow is:
1 Create Map: var map = L.map('#map', options);
2 Create a FeatureGroup: features= new L.FeatureGroup();
3 Add to the Leaflet Map: map.addLayer(features);
4 On the draw:created event, I'm capturing e.layer and adding a bunch of properties:
var layer = e.layer;
layer.properties = { Title: 'Hello' };
features.addLayer(layer);
geo_features = features.toGeoJSON();
However, my geo_features always have empty property attributes in each of the features and I can't figure it out!
iH8's initial answer was almost correct.
To specify properties that will appear in a vector layer's GeoJSON export (i.e. through its .toGeoJSON() method), you have to fill its feature.type and feature.properties members:
var myVectorLayer = L.rectangle(...) // whatever
var feature = myVectorLayer.feature = myVectorLayer.feature || {};
feature.type = "Feature";
feature.properties = feature.properties || {};
feature.properties["Foo"] = "Bar";
Now myVectorLayer.toGeoJSON() returns a valid GeoJSON feature object represented by:
{
"type": "Feature",
"properties": {
"Foo": "Bar"
// More properties that may be pre-filled.
},
"geometry": // The vector geometry
}
A (kind of ugly workaround) is using a L.GeoJSON layer and add the drawn layer's GeoJSON to it by using it's addData method. Afterwards grab the last layer in the L.GeoJSON layer's _layers object. At that point the layer has a valid GeoJSON feature property you can edit:
var geojson = new L.GeoJSON().addTo(map);
var drawControl = new L.Control.Draw({
edit: {
featureGroup: geojson
}
}).addTo(map);
map.on('draw:created', function (e) {
geojson.addData(e.layer.toGeoJSON());
var layers = geojson._layers,
keys = Object.keys(layers),
key = keys[keys.length - 1],
layer = layers[key];
layer.feature.properties = {
'Foo': 'Bar'
};
});
For your L.GeoJSON call include feature callback onEachFeature to options
L.GeoJSON(featureData,{onEachFeature:function(feature,layer){
//console.log(feature,layer);
// do something like
feature.setStyle( convertLayerOptionsFromFeatureProperties( feature.properties ) );
}} )

Cannot set style on leaflet L.geoJSON layer

I am having a bit of trouble setting the style on a L.geoJSON based layer,my code looks like this:
var addProperties = function addProperties(prop,map)
{
//the API does not seem to support adding properties to an existing feature,
//the idea here is simple:
//(1) currentFeature.toGeoJson() needs to be called to obtain a json representation
//(2) set the properties on the geojson
//(3) create a new feature based on the geojson
//(4) remove res and add the new feature as res
var style = function style(feature){
var markerStyle = {
draggable: 'true',
icon: L.AwesomeMarkers.icon({
icon: 'link',
prefix: 'glyphicon',
markerColor: 'red',
spin: true
})
};
if(feature.geometry.type==='Point')
return markerStyle;
};
var onEachFeature = function onEachFeature(feature,layer){
console.log("Inside on each feature,checking to see if feature was passed to it ",feature);
layer.on('click',function(e){
//open display sidebar
console.log("Checking to see if setupTabs exists ",setupTabs);
setupTabs('#display-feature-tabs');
console.log("Checking to see if featureInfo exists ",featureInfo);
var featureInfoAPI =featureInfo('feature-properties');
featureInfoAPI.swap(feature.properties);
setTimeout(function() {
sidebar.show();
}, 100);
});
};
console.log("Inside add properties");
var geoJSON,feature;
if(res != null)
{
geoJSON = res.toGeoJSON();
geoJSON.properties = prop;
console.log(geoJSON);
feature = L.geoJson(geoJSON,{style:style,onEachFeature:onEachFeature});
console.log("The new feature that has been created ",feature);
removeFeature(map);
addFeature(feature);
feature.addTo(map);
}
};
I have also tried the style method as well,I am looking to add styles to the active layer for points(styles will also be added for lines and polylines by type).
To style point features, you should use pointToLayer option instead.