Set layer className within event callback in Leaflet - javascript

I'm looking to set a geojson feature's style via setting its className. This works perfectly fine if placed directly on the feature like so:
L.geoJson(geojson, {
onEachFeature: function (feature, layer) {
layer.setStyle({className: 'grid-cell'});
}
}).addTo(map);
with the style defined in a .css file
path.grid-cell{
stroke-opacity: 1;
stroke: #444;
fill: #aaa;
}
However, it does not work if added within a feature's event callback:
L.geoJson(geojson, {
onEachFeature: function (feature, layer) {
layer.on('click', function(e){
this.setStyle({className: 'grid-cell'});
this.bringToFront();
});
}
}).addTo(map);
What's surprising is that setStyle({<style_options>}); work in either case for every other L.path option besides className, e.g. color, fillOpacity, weight, etc.
E.g.
L.geoJson(geojson, {
onEachFeature: function (feature, layer) {
// this works
layer.setStyle({color: '#faa', fillOpacity: 0.4, weight: 1});
// this works too
layer.setStyle({className: 'grid-cell'});
layer.on('click', function(e){
// and this works
layer.setStyle({color: '#afa', fillOpacity: 0.4, weight: 2});
// BUT THIS DOES NOT WORK
this.setStyle({className: 'grid-cell'});
this.bringToFront();
});
}
}).addTo(map);
Any idea what's up here? Here's a plunker illustrating the issue.

For a discussion of this issue, look here: https://github.com/Leaflet/Leaflet/issues/2662. One of the comments:
I don't think setStyle should actually change the className. The class is not really a style property, and the logic necessary to handle leaflet- classes seems like a hack. I think a setClassName() or add/removeClass API would be more appropriate.

Related

Variable and Parameter are declared but never read in jQuery AJAX Request Leaflet and JavaScript

I'm working through trying to plot, style and size cirlceMarker points in Leaflet/JS with a jQuery AJAX request and almost immediately am getting a variable moderateLayer and parameter feature that are declared but never read message. The points draw using the pointToLayer function without styling, but all the subsequent function don't seem to work. No errors display in the console. Any idea what I'm missing here?
const commonStyles = {
weight: 1,
stroke: 1,
fillOpacity: .8
}
// jQuery method using AJAX request for GeoJSON data
// add sviPoint data
$.getJSON("data/cdps_svis_whp_ctr.json", function (sviPoints) {
var moderateLayer = L.geoJson(sviPoints, {
pointToLayer: function (feature, latlng) {
// Draw circle marker based on pointToLayer function
return L.circleMarker(latlng, commonStyles);
},
// filter layer to display only class value of moderate
filter: function (feature) {
if (feature.properties.WHP_CLASS.Moderate) {
return feature;
}
},
// style stroke and fill
style: function (feature) {
return {
color: '#FF4000',
fillColor: '#FF4000',
// call function get radius from overall svi weighted mean
radius: getRadius(feature.properties.OVERALL_WM)
}
},
onEachFeature: function (feature, layer) {
// on mouseover change color to darker shade of orange
layer.on('mouseover', function () {
layer.setStyle({
fillColor: '#401000'
});
});
// return to original fill color when not hovering with mouse
layer.on('mouseout', function () {
layer.setStyle({
fillColor: '#FF4000'
});
});
var popup = `<h4>${feature.properties.CDP_STATE}</h4>
<p><b>Overall SVI:</b> ${feature.properties.OVERALL_WM}<br>
<p><b>Wildfire Hazard Potential:</b> ${feature.properties.WHP_CLASS}<br>`
// Loop through all fuel sources in plants that split atoms
// for (var fuelSource in feature.properties.fuel_source) {
// // View the source
// console.log(fuelSource)
// // Concat all fuel sources
// popup += `<br>${fuelSource}: ${feature.properties.fuel_source[fuelSource]} MW`
// }
layer.bindPopup(popup)
}
}).addTo(map);
});

(Closed) Leaflet.js: How I can Do Editing Geometry On Specific Object I Select Only?

I am very excited and lot of fun playing with Leaflet.JS on gis mapping stuff. I am newbie. I also very appreciate with bro #Grzegorz T. with the kindness and helping me on introduction me to Leaflet.JS.
Now, I am plan to do Editing geometry on specific object I select only. I already think how the flow and the result but the process???(hahahaa..). I also already know regarding DrawItems function on Leaflet.JS but I will use.
Let me describe simple flow before I do it...
A. I just using jsfiddle1 #Grzegorz T.
B. I add DrawItems
...
{ 'DrawLayer': drawnItems }, { position: 'topleft', collapsed: false }).addTo(map);
...
//Add Draw Control//
map.addControl(new L.Control.Draw({
edit: {
featureGroup: drawnItems,
poly: {
allowIntersection: false
}
},
draw: {
polygon: {
allowIntersection: false,
showArea: true
}
}
}));
//Draw Objects//
map.on(L.Draw.Event.CREATED, function (event) {
var layer = event.layer;
drawnItems.addLayer(layer);
});
//Get Leaflet Object ID
drawnItems.on('click', function(event) {
console.log("from drawnItems: " + event.layer._leaflet_id);
});
//Get Geometry from Layer & ID
//Edit Only This Objects
//Save This Editing Back to Original Layer & ID
on this part I saw a Problem is:
1. I don't know how to copy original geometry I selected from layer to DrawLayer.
2. Hide only this geometry(id) on original layer until end of editing or cancel. (maybe change opacity to invisible)
3. After finish editing and while saving How I can save back to original layer and show the result to map.
I hope whos hardcore and whos already found the easy way on Leaflet.JS can help me for this dirty work....
Update 3/3/2022
I found how to restyle the object(geometry) I selecting and this was cover for:
2. Hide only this geometry(id) on original layer until end of editing or cancel. (maybe change opacity to invisible)
Code like below:-
onEachFeature: function (feature, layer) {
//restyle the geom when display
layer.setStyle({
fillColor: "white",
weight: 2,
color: "#eb4034",
fillOpacity: 0.7,
});
layer.on("mouseover", function (e) {
// style
this.setStyle({
fillColor: "#eb4034",
weight: 2,
color: "#eb4034",
fillOpacity: 0.7,
});
});
layer.on("mouseout", function () {
// style
this.setStyle({
fillColor: "#3388ff",
weight: 2,
color: "#3388ff",
fillOpacity: 0.1,
});
});
layer.on("click", function () {
this.setStyle({
fillColor: "transparent",
weight: 0,
color: "transparent",
fillOpacity: 0,
});
});
},
Update 4/3/2022
Anyone can help me how to passing geometry to drawnitems programmatically with editing mode? Let say I have whatever geometry like this below:-
......
{"type":"Feature","geometry":{"type":"Polygon","coordinates":[[[16.3716,54.4414],[16.3946,54.4477],[16.4315,54.487],[16.4797,54.5164],[16.4968,54.5231],[16.5299,54.5407],[16.6145,54.5598],[16.6887,54.5696],[16.6991,54.5692],[16.7126,54.5542],[16.7392,54.5384],[16.7481,54.5276],[16.7392,54.5177],[16.7566,54.4946],[16.764,54.4861],[16.7933,54.4874],[16.8275,54.4648],[16.8211,54.4563],......
........
,"properties":{"id":4,"nazwa":"zachodniopomorskie"},"id":3}
How I can pass this polygon (geometry) into drawnItems layer programmatically?
Please help me and thanks you on advance for read this message.
After struggling a few days finally I found solutions to accomplish my task. It's not pretty but at least I fill ok and all I need are there.
I using
layer.on("click", function(e) {
to get the geometry and id from original layer. The original geometry I pass to drawnItems by using
geojsonLayer = L.geoJson(oriGeom);
geojsonLayer.getLayers()[0].addTo(drawnItems);
to draw back on drawing layer to do editing. On mean while I adjust the style on original geometry to invisible. The editable geometry only can see on this stage where user 'think' this is original geometry he/she do editing right now.
onEachFeature: function (feature, layer) {
//restyle the geom when display
layer.setStyle({
fillColor: "white",
weight: 2,
color: "#eb4034",
fillOpacity: 0.7,
});
layer.on("mouseover", function (e) {
// style
this.setStyle({
fillColor: "#eb4034",
weight: 2,
color: "#eb4034",
fillOpacity: 0.7,
});
});
layer.on("mouseout", function () {
// style
this.setStyle({
fillColor: "#3388ff",
weight: 2,
color: "#3388ff",
fillOpacity: 0.1,
});
});
layer.on("click", function () {
this.setStyle({
fillColor: "transparent",
weight: 0,
color: "transparent",
fillOpacity: 0,
});
After finish the editing I save back to original layer by using Python class by search ID and replace the original coordinates to new coordinates (editGeom) only. :)
Then I delete all object inside drawnItems layer by this trick
document.querySelector(".leaflet-draw-edit-remove").click(); //fake click Delete
document.querySelector("ul li:last-child a").click(); //fake click Save All
That's all.

Leaflet : ordering GeoJSON elements inside a layer

I'm displaying a GeoJSON layer using leaflet, with the pointToLayer function. Everything works ok so far.
But I would like to display my points in a certain order, based on a property of the GeoJSON. This is important because the radiuses of my points varies with this property, and I need to display the smaller circles on top. I hope I make myself clear.
I've tried many things, but here's what I think is my best try :
var pointLayer = L.geoJson(centroids, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, {
fillColor: "#76C551",
color: "#000",
weight: 1,
fillOpacity: 1
});
},
onEachFeature: function (feature, layer) {
var radius = calcPropRadius(feature.properties.nb);
layer.setRadius(radius);
feature.zIndexOffset = 1/feature.properties.nb*1000;
layer.bindPopup(feature.properties.name + " : " + String(feature.zIndexOffset));
}
});
You can notice that the zIndexOffset of features can be read in the popups, and they look ok. But the displaying order of the circles doesn't reflect the zIndexOffset.
I've tried using the setZIndexOffset method, but as I understand it it works only with markers.
Does anyone know how to do this ? Thanks a lot for any insight !
Whereas ghybs answer works perfectly for leaflet 0.7, switching to leaflet 1.0 allows the use of panes which makes for an easier solution :
var pointLayer = L.geoJson(centroids, {
pointToLayer: function (feature, latlng) {
return L.circleMarker(latlng, {
fillColor: "#76C551",
color: "#000",
weight: 1,
fillOpacity: 1
});
},
onEachFeature: function (feature, layer) {
var radius = calcPropRadius(feature.properties.nb);
layer.setRadius(radius);
layer.setStyle({pane: 'pane'+ feature.properties.nb});
var currentPane = map.createPane('pane' + feature.properties.nb);
currentPane.style.zIndex = Math.round(1/feature.properties.nb*10000);
layer.bindPopup(feature.properties.name + " : " + String(feature.zIndexOffset));
}
});
Hope it can be of use to someone else !
As you figured out, the zIndexOffset option is only for L.marker's.
L.circleMarker's go into the overlayPane and you can re-order them one to each other using .bringToFront() and .bringToBack() methods.

Style both points and multipolygons simultaneously Leaflet

I was working on an application in which you can show points on a map. To improve the application, I added the functionality the show multipolygons as well.
I've created a dropdowmenu where you can select a dataset. One is a dataset containing stores (points), and the other contains precincts (multipolygons). Everything works fine and I can show either points or polygons on the map.
Because I build the application to only show points first, I've only styled the points (see code below).
var myStyle = {
"color": "#ff7800",
"weight": 5,
"opacity": 0.65
};
window["mapDataLayer"] = L.geoJson(geojson, {
pointToLayer: function (feature, latlng) {
var markerStyle = {
fillColor: getColor(feature.properties.Fastfoodketen),
color: "#696969",
fillOpacity: 0.6,
opacity: 0.9,
weight: 1,
radius: 8
};
return L.circleMarker(latlng, markerStyle);
},
onEachFeature: function (feature, layer){
layer.on({
click: function showResultsInDiv() {
var d = document.getElementById('tab4');
d.innerHTML = "";
for (prop in feature.properties){
d.innerHTML += prop+": "+feature.properties[prop]+"<br>";
}
$('.nav-tabs a[href="#tab4"]').tab('show');
}
}); },
style: myStyle
}).addTo(map);
Now I want to style the polygons. How can I alter the code above to include polygons as well?
I thought it would be a good way was to include an if/else loop to check if the geometry was a point or a multipolygon and then direct to the appropriate styling. However, I don't know how to check if a geometry is a point/polygon.
This is covered by the Leaflet documentation on GeoJSON options (emphasis mine):
pointToLayer: A Function defining how GeoJSON points spawn Leaflet layers.[...]
style: A Function defining the Path options for styling GeoJSON lines and polygons, [...]
You can see examples of this in the Leaflet tutorials for GeoJSON

'Invalid GeoJSON object.' Using Leaflet and leaflet-ajax

I'm using leaflet to build a map of France, with the regions highlighted, and on-click zoom.
I used this tutorial first: http://leafletjs.com/examples/choropleth.html
First, I've had the geojson in the script.js, but in my case i needed regions in separated geojson files.
So i'm now calling them in the script.js with leaflet-ajax like this :
var BordeauxLayer = new L.GeoJSON.AJAX("src/js/DI_Bordeaux.geojson").addTo(map);
The regions is displayed on the map, but now all the function to zoom-in, highlight etc... that I took on the tutorial don't work anymore.
L.geoJson(BordeauxLayer,{onEachFeature: onEachFeature}).addTo(map);
// HIGHLIGHT FEATURE = MOUSEOVER
function onEachFeature(feature, layer) {
layer.on({
mouseover: highlightFeature,
mouseout: resetHighlight,
click: zoomToFeature
});
}
// HIGHLIGHT FEATURE = MOUSEOVER
function highlightFeature(e) {
var layer = e.target;
layer.setStyle({
weight: 5,
color: '#666',
dashArray: '',
fillOpacity: 0.7
});
};
// HIGHLIGHT FEATURE = MOUSE LEFT
function resetHighlight(e) {
geojson.resetStyle(e.target);
};
// ZOOM TO THE REGION
function zoomToFeature(e) {
map.fitBounds(e.target.getBounds());
}
And now the console says " Uncaught Error: Invalid GeoJSON object. " on the line 8 of leaflet.js.
The problem seems to be on this line :
L.geoJson(BordeauxLayer,{onEachFeature: onEachFeature}).addTo(map);
And I don't get why :(
Edit : Here is my geojson : https://api.myjson.com/bins/3s1ad
Thank's in advance.
Your call to onEachFeature has to be in your AJAX call
var BordeauxLayer = new L.GeoJSON.AJAX("src/js/DI_Bordeaux.geojson", {onEachFeature: onEachFeature}).addTo(map);
You also have to get rid of
L.geoJson(BordeauxLayer,{onEachFeature: onEachFeature}).addTo(map);

Categories

Resources