21

How can I force a new layer added to the map in Leaflet to be the first over the basemap?

I could not find a method to easily change the order of the layers, which is a very basic GIS feature. Am I missing something?

Yaroslav
  • 6,476
  • 10
  • 48
  • 89
user1249791
  • 1,241
  • 4
  • 14
  • 33

5 Answers5

13

A Leaflet map consists of a collection of "Panes" whose view order is controlled using z-index. Each pane contains a collection of Layers The default pane display order is tiles->shadows->overlays->markers->popups. Like Etienne described, you can control the display order of Paths within the overlays pane by calling bringToFront() or bringToBack(). L.FeatureGroup also has these methods so you can change the order of groups of overlays at once if you need to.

If you want to change the display order of a whole pane then you just change the z-index of the pane using CSS.

If you want to add a new Map pane...well I'm not sure how to do that yet.

http://leafletjs.com/reference.html#map-panes

http://leafletjs.com/reference.html#featuregroup

Rob
  • 117
  • 11
InPursuit
  • 3,245
  • 24
  • 20
7

According to Leaflet API, you can use bringToFront or bringToBack on any layers to brings that layer to the top or bottom of all path layers.

Etienne

Prisoner
  • 27,391
  • 11
  • 73
  • 102
Etienne Desgagné
  • 3,102
  • 1
  • 29
  • 36
  • 2
    Leaflet has also recently added the setZIndex method that can be used on TileLayer to manually manage the tiles layers order with CSS – Etienne Desgagné Jan 15 '13 at 21:51
5

For a bit more detail, Bobby Sudekum put together a fantastic demo showing manipulation of pane z-index. I use it as a starting point all the time.

Here's the key code:

var topPane = L.DomUtil.create('div', 'leaflet-top-pane', map.getPanes().mapPane);
var topLayer = L.mapbox.tileLayer('bobbysud.map-3inxc2p4').addTo(map);
topPane.appendChild(topLayer.getContainer());
topLayer.setZIndex(7);
meetar
  • 7,443
  • 8
  • 42
  • 73
Bill Morris
  • 589
  • 8
  • 16
  • 3
    Link only answers are discouraged in SO, try to answer it with your own words and if you fell necessary post the link as a reference. Moreover, the link may broke eventually, which would make your answer pointless. – Math Sep 23 '13 at 20:07
  • The third command only appears to work with mapbox **tileLayers**, there is no `getContainer()` method for featuregroups – raphael Jan 29 '16 at 20:25
  • @RobertSiemer Good point! I think this is long-since superseded by the built-in functions indicated in the upper answers. – Bill Morris Apr 02 '20 at 01:08
2

Had to solve this recently, but stumbled upon this question.

Here is a solution that does not rely on CSS hacks and works with layer groups. It essentially removes and re-adds layers in the desired order.

I submit this as a better "best practice" than the current answer. It shows how to manage the layers and re-order them, which is also useful for other contexts. The current method uses the layer Title to identify which layer to re-order, but you can easily modify it to use an index or a reference to the actual layer object.

Improvements, comments, and edits are welcome and encouraged.

JS Fiddle: http://jsfiddle.net/ob1h4uLm/

Or scroll down and click "Run code snippet" and play with it. I set the initial zoom level to a point that should help illustrate the layerGroup overlap effect.

function LeafletHelper() {

    // Create the map
    var map = L.map('map').setView([39.5, -0.5], 4);

    // Set up the OSM layer
    var baseLayer = L.tileLayer(
        'http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        maxZoom: 18
    }).addTo(map);

    var baseLayers = {
        "OSM tiles": baseLayer
    };

    this.map = map;

    this.BaseLayers = {
        "OSM tiles": baseLayer
    };
    this.LayersControl = L.control.layers(baseLayers).addTo(map);

    this.Overlays = [];
    this.AddOverlay = function (layerOptions, markers) {
        var zIndex = this.Overlays.length;
        var layerGroup = L.layerGroup(markers).addTo(map);
        this.LayersControl.addOverlay(layerGroup, layerOptions.title);
        this.Overlays.push({
            zIndex: zIndex,
            LeafletLayer: layerGroup,
            Options: layerOptions,
            InitialMarkers: markers,
            Title: layerOptions.title
        });
        return layerGroup;
    }
    this.RemoveOverlays = function () {
        for (var i = 0, len = this.Overlays.length; i < len; i++) {
            var layer = this.Overlays[i].LeafletLayer;
            this.map.removeLayer(layer);
            this.LayersControl.removeLayer(layer);
        }
        this.Overlays = [];
    }
    this.SetZIndexByTitle = function (title, zIndex) {

        var _this = this;

        // remove overlays, order them, and re-add in order
        var overlays = this.Overlays; // save reference
        this.RemoveOverlays();
        this.Overlays = overlays; // restore reference

        // filter overlays and set zIndex (may be multiple if dup title)
        overlays.forEach(function (item, idx, arr) {
            if (item.Title === title) {
                item.zIndex = zIndex;
            }
        });

        // sort by zIndex ASC
        overlays.sort(function (a, b) {
            return a.zIndex - b.zIndex;
        });

        // re-add overlays to map and layers control
        overlays.forEach(function (item, idx, arr) {
            item.LeafletLayer.addTo(_this.map);
            _this.LayersControl.addOverlay(item.LeafletLayer, item.Title);
        });
    }
}

window.helper = new LeafletHelper();

AddOverlays = function () {
    // does not check for dups.. for simple example purposes only
    helper.AddOverlay({
        title: "Marker A"
    }, [L.marker([36.83711, -2.464459]).bindPopup("Marker A")]);
    helper.AddOverlay({
        title: "Marker B"
    }, [L.marker([36.83711, -3.464459]).bindPopup("Marker B")]);
    helper.AddOverlay({
        title: "Marker C"
    }, [L.marker([36.83711, -4.464459]).bindPopup("Marker c")]);
    helper.AddOverlay({
        title: "Marker D"
    }, [L.marker([36.83711, -5.464459]).bindPopup("Marker D")]);
}

AddOverlays();

var z = helper.Overlays.length;

ChangeZIndex = function () {
    helper.SetZIndexByTitle(helper.Overlays[0].Title, z++);
}

ChangeZIndexAnim = function () {
    StopAnim();
    var stuff = ['A', 'B', 'C', 'D'];
    var idx = 0;
    var ms = 200;
    window.tt = setInterval(function () {
        var title = "Marker " + stuff[idx++ % stuff.length];
        helper.SetZIndexByTitle(title, z++);
    }, ms);
}

StopAnim = function () {
    if (window.tt) clearInterval(window.tt);
}
#map {
    height: 400px;
}
<link rel="stylesheet" type="text/css" href="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.css">
<script type='text/javascript' src="http://cdn.leafletjs.com/leaflet-0.6.4/leaflet.js"></script>
<div id="map"></div>
<input type='button' value='Remove overlays' onclick='helper.RemoveOverlays();' />
<input type='button' value='Add overlays' onclick='AddOverlays();' />
<input type='button' value='Move bottom marker to top' onclick='ChangeZIndex();' />
<input type='button' value='Change z Index (Animated)' onclick='ChangeZIndexAnim();' />
<input type='button' value='Stop animation' onclick='StopAnim();' />
nothingisnecessary
  • 6,099
  • 36
  • 60
1

I've found this fix (css):

.leaflet-map-pane {
    z-index: 2 !important;
}

.leaflet-google-layer {
    z-index: 1 !important;
}

found it here: https://gis.stackexchange.com/questions/44598/leaflet-google-map-baselayer-markers-not-visible

Community
  • 1
  • 1
Mahmood Dehghan
  • 7,761
  • 5
  • 54
  • 71