10

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 5

10

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)

2
  • 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
4

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.

2

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.

1

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);
1
  • 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
1

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.