I have a single layer (points) with a bunch of markers. Each point has an attribute that is regularly updated. I understand by Leaflet sorts the layers such that those lower in latitude are on top for aesthetic reasons, but I would like to reorder them such that in areas of clustering, those with higher values in the attribute are on top. Is there any way to do this without having to split up each marker/point into an individual layer and forcing draw order that way?
5 Answers
Until somebody finds a better solution, here what I would do ...
As you noticed, leaflet is using pixel position to set zIndex (in Marker.js)
pos = this._map._latLngToNewLayerPoint(this._latlng, opt.zoom, opt.center).round();
this._zIndex = pos.y + this.options.zIndexOffset;
What I suggest is to undo leaflet zIndex using setZIndexOffset()
Say you want to set zIndex = 100, you would do
var pos = map.latLngToLayerPoint(marker.getLatLng()).round();
marker.setZIndexOffset(100 - pos.y);
There is a bit of a glitch: you have to do it every time the map is zoomed :(
Here is a JSFiddle example (comment the code in adjustZindex() to see the difference)
-
It does look like Leaflet redoes z-indexes every time it's zoomed anyways (at least playing around with it and checking a marker's z-index on chrome's dev tools). So I think this a perfectly fine option, thanks! Commented Oct 3, 2014 at 17:33
-
thanks a lot for sharing the source of the problem. With this knowledge we can also produce alternative workarounds like "zIndexOffset: this.leafletMap.getSize().y*(index+1)x" which works like a charm for me without the on-zoom problem Commented Mar 3, 2023 at 10:52
The current version of leaflet (as of early 2017) has a zIndexOffset property with default = 0 that can be set to atler the z-index of a marker. http://leafletjs.com/reference.html#marker-zindexoffset Works fine with zooming and everthing.
This plugin will do the trick: leaflet.forceZIndex.js
// Force zIndex of Leaflet
(function(global){
var MarkerMixin = {
_updateZIndex: function (offset) {
this._icon.style.zIndex = this.options.forceZIndex ? (this.options.forceZIndex + (this.options.zIndexOffset || 0)) : (this._zIndex + offset);
},
setForceZIndex: function(forceZIndex) {
this.options.forceZIndex = forceZIndex ? forceZIndex : null;
}
};
if (global) global.include(MarkerMixin);
})(L.Marker);
To use:
var aMarker = L.marker([lat, lon], {
icon: icon,
title: title,
forceZIndex: <Value> // This is forceZIndex value
})
forceZIndex declaration will assure that ZIndex will be always set from aMarker.options.forceZIndex
Update it somewhere to re-force ZIndex value
aMarker.setForceZIndex(<New Value>)
Or setForceZIndex(null) to automatical zIndex state:
aMarker.setForceZIndex(null);
By the end of the day, if no forceZIndex option declared, Marker will work with normal behaviour.
The only solutions currently available are hacky and may break in the future, if they decide to use something else than pos.y
or refactor the code heavily.
Here's another simple hacky solution which is easier to use and doesn't require updating the value every time zoom changes. It makes the zIndexOffset
reflect the actual zIndex
:
L.Marker.prototype.__setPos = L.Marker.prototype._setPos;
L.Marker.prototype._setPos = function ()
{
L.Marker.prototype.__setPos.apply(this, arguments);
this._zIndex = this.options.zIndexOffset;
this._resetZIndex();
};
Then if you want to set zIndex to 100, just use:
marker.setZIndexOffset(100);
-
May i just am not an expert, but i don't get where I must put your code to get it Working. May you add a bit more of explonation? Commented Mar 3, 2023 at 7:16
As stated in https://gis.stackexchange.com/a/115954/35248 the source problem, is that markers have a default zIndex
which is build by the y pixel position.
With this knowledge, we can simply overgo this setting the zIndexOffset
over the scale of y by using:
let myMarker = Leaflet.marker(coord, {
zIndexOffset: this.leafletMap.getSize().y*myWishedIndex
})
But here we have also small backdraws:
- we have a (strongly) reduced amount of zIndexes so this solution is not usable for high resolution maps with a lot of markers
- we may run in to a problem if we use frameworks around the map which allow rescaling of the map-container without reinitiating the map Markers