7

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

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! – wowohweewah Oct 3 '14 at 17:33
2

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

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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