1.8. Use the map panes#

In Leaflet, map panes can automatically group layers, but developers are not aware that this allows browsers to process multiple layers more efficiently, rather than processing one layer at a time.

Map panes uses z-index CSS property Properties to control some layers to appear on top of other layers. The default order is as follows:

  • TileLayers and GridLayers

  • Paths, like lines, polylines, circles, or GeoJSON layers.

  • Marker shadows

  • Marker icons

  • Popups

That’s why in the Leaflet map, popups are displayed on top of other layers, markers are always displayed on the slice layer, and so on.

Leaflet 1.0.0 has added a new feature (0.7.X not available) to customize the map panes to adjust the default order.

Default value is not always appropriate#

In some special cases, the default layer sorting is not always appropriate. We use the following basemap and an example of a label layer to explain:

<style>
    .tiles img {{
        border: 1px solid #ccc;
        border-radius: 5px;
    }}
</style>

Basemap without annotation

Transparent label layer

Label the layer above the basemap

If we add the above basemap and annotation layer to the Leaflet map, any polygons or markers will appear above the two layers, but it might be better to have the annotation layer appear on top. How should we achieve this?

Show the example

Custom panes#

For basemaps and overlay layers like GeoJSON, we can use their default settings, but for annotation layers we need to customize a pane so that it appears on top of the GeoJSON layer.

Custom map panes are created on top of the original map, so we should first create a map instance of L.Map and the panes it needs:

var map = L.map('map');
map.createPane('labels');

The next step is to set the value of the pane’s z-index. To view the default values for the pane, set the new pane’s z-index value to 650 to have the label layer appear below the popups layer above the markers layer. Use the getPane() method to get the Html element of the pane and then modify its z-index value.

map.getPane('labels').style.zIndex = 650;

One of the problems with having the image layer on top of other layers is that the slice captures events such as clicks or touches. If the user clicks on an area of the map, the browser will think that the user is clicking on the image layer instead of the GeoJSON layer or the markup layer. We can use CSS’s pointer-events property to solve this problem:

map.getPane('labels').style.pointerEvents = 'none';

The new pane has been created, we can add the layer, pay attention to the pane option on the label layer:

var positron = L.tileLayer('http://{{s}}.basemaps.cartocdn.com/light_nolabels/{{z}}/{{x}}/{{y}}.png', {{
    attribution: '©OpenStreetMap, ©CartoDB'
}}).addTo(map);

var positronLabels = L.tileLayer('http://{{s}}.basemaps.cartocdn.com/light_only_labels/{{z}}/{{x}}/{{y}}.png', {{
    attribution: '©OpenStreetMap, ©CartoDB',
    pane: 'labels'
}}).addTo(map);

var geojson = L.geoJson(GeoJsonData, geoJsonOptions).addTo(map);

Finally, add some interactive features to the GeoJSON layer:

geojson.eachLayer(function (layer) {{
layer.bindPopup(layer.feature.properties.name);
}});
map.fitBounds(geojson.getBounds());

This way the example map is complete!