15

I would like to be able to change the order of the layers in Leaflet.

I tried implementing two Leaflet plugins in order to change the layer order.

First I tried leaflet-control-orderlayers, which seems to work, but does not support layerGroups, here is my filed bug.

Next I tried mapbbcode, which does support layerGroups, but does not seem to work or be maintained.

Does anyone have any suggestions for how to implement this feature?

1

5 Answers 5

12

You can do this in a couple ways (at least; this is how I do it):

  1. layer.bringToFront() and layer.bringToBack() but these will only do one layer at a time, and only pushes it to front or back, not in between. So you would need to put your layers in the desired order, start with the layer you want to be on bottom, and call layer.bringToFront() on each layer.

  2. You can also do this by removing and re-adding the layer. This seems sorta wasteful, but since the map must redraw when repositioning layers anyway, it is generally not that big of a deal.

Here is a fairly complex fiddle that illustrates the issue

Basically you remove all layers and then re-add them in ascending z-index order. So loop through all your overlay layers, and call map.removeLayer(layer), then sort them by your desired z-index, then re-add them using layer.addTo(map).

3

You must create Panel in order to control the order.

map.createPane('myPane1');
map.createPane('myPane2');
map.getPane('myPane1').style.zIndex = 400;
map.getPane('myPane2').style.zIndex = 800;

With this you can add the layer to the pane you want.

1
  • You have to also attach your layer to the pane, if its GeoJSON it will look like this: L.geoJson(data, { pane: 'myPane2', (...) }
    – sk1me
    Commented Dec 11, 2021 at 18:59
1

I was having the same issue and I tried the fixZOrder() method, but sadly it did not order the Leaf markers for me as it doesn't seem to affect zindex of Leaflet markers.

Seems like .bringToFront() works only on vector shapes which support this method, on the contrary to the Leaf markers I deployed rendering this solution unworkable, not to mention other code issue I was experiencing with fixZOrder(), but this could be unique to my case.

​​

Instead I simply left the layer ordering to Leaflet (options) in the order I loaded them, which caused issues with marker stacking (OMS Spiderf plugin) when using layer controls, but I managed to resolve these with the help of my own funtion based on setZIndexOffset()

function myBring2Front() {
        //layer1.bringToFront();
        layer1.eachLayer(function (layer) {
          layer.setZIndexOffset(2000);
        });
        layer2.eachLayer(function (layer) {
          layer.setZIndexOffset(1000);
        });
    }

Although my scenario did not require the fixZOrder(layers) in the end anyone else using this method together with stacked markers may need to adopt a similar approach and hence I'm posting this as an additional solution for potential users of the above mentioned layer order method.

​​

CREDIT: ghybs answer on stackoverflow.com/a/37850189/11106279

1
1

You can simply change the order of your layers (variables) in the map script. For example, if you have:

 var layer1 = L.geoJSON(leyer)
 //...
 var layer2 = L.geoJSON(layer)
 //...
 var layer3 = L.geoJSON(layer)

 var overlays = {}

and you want to have layer1 on the top, you just need to change their order across your code

 var layer 2 = L.geoJSON(layer)
 //...
 var layer 3 = L.geoJSON(layer)
 //...
 var layer 1 = L.geoJSON(layer)

 var overlays = {}

then your layer 1 will be brought to the top, as the code is read from the top to the bottom.

2
  • It didn't work for me, unfortunately.
    – sk1me
    Commented Dec 11, 2021 at 18:38
  • Try to change the order also within var overlays = {}
    – Geographos
    Commented Dec 12, 2021 at 15:49
0

Similar to the answer from @nothingisnecessary the following uses layer.bringToFront() to maintain z-order when you have combined layer control and asynchronous data loading.

we don't want to clear the layers and add them back to the map as this will add all layers, instead we want to respect the selected layers in the Layer Control with minimal overheads. bringToFront() can do this but we must only call it on a layer group that has layers (content) within it.

Define the layers:

var dataLayers = {
    Districts: new L.geoJSON(),
    Farms: new L.featureGroup(),
    Paddocks: new L.geoJSON(),
    Surveys: new L.geoJSON()
};

L.control.layers(
    // base maps
    {
        OSM: L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a>', subdomains: ['a', 'b', 'c'] });,
    },
    dataLayers,
    {
        collapsed: false,
        hideSingleBase: true
    }).addTo(map);

Use the following function to enforce the z-order:

/**
 * Ensure that layers are displayed in the correct Z-Order
 * Instead of explicitly defining z-order on the groups the order of the calls to beingToFront controls the resulting zOrder
 * @param {any} dataLayers Object that holds the references to the layer groups toggled by the layer control
 **/
function fixZOrder(dataLayers) {

    // only similar approach is to remove and re-add back to the map
    // use the order in the dataLayers object to define the z-order
    Object.keys(dataLayers).forEach(function (key) {

        // check if the layer has been added to the map, if it hasn't then do nothing
        // we only need to sort the layers that have visible data
        // Note: this is similar but faster than trying to use map.hasLayer()
        var layerGroup = dataLayers[key];
        if (layerGroup._layers
            && Object.keys(layerGroup._layers).length > 0
            && layerGroup._layers[Object.keys(layerGroup._layers)[0]]._path
            && layerGroup._layers[Object.keys(layerGroup._layers)[0]]._path.parentNode)
            layerGroup.bringToFront();
    });
}

When using the Layer control, when a Layer is toggled into view, it will be on top of the other layers, we need to re-apply the order logic when a layer is added. This can be done by binding to the overlayadd event on the map and calling the fixZOrder function:

map.on('overlayadd', function (e) {
    fixZOrder(dataLayers);
}

If you load your data asynchronously, you may also need to call fixZOrder when your data load has completed, new layers added at runtime will be rendered on top of all other layers.

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.