Search code examples
javascriptrshinyleafletr-leaflet

How to prevent BringToBack from reversing layers order of FeatureGroup in Leaflet


I have a Leaflet Map on a Shiny web-app. I have 3 Base Groups containing different circles, and 1 Overlay Group containing polylines crossing each other. The polylines are sorted so that the more important lines are on top of the less important one. The circles are added afterwards to be on top of the lines. The problem is : when unchecking then checking the overlay group, the lines are put on top of the circles. The circle can be put on top of the lines when the base layer is changed. However, I would like to prevent the line from coming on top of the circles. To do that I used the OnRender function of htmltools :

output$map <- renderLeaflet(
    leaflet() %>%
      addProviderTiles(providers$CartoDB.Positron) %>%
      setView(
        lng = 48.80,
        lat = 2.30,
        zoom = 10
      ) %>%
      addControl("Title", position = "bottomleft", className = "map-title")
    %>% onRender(
      "function(el, x) {
      this.on('overlayadd', function(e) {
        e.layer.bringToBack();
      })
    }"
    )

And, well, yeah it does put the line behind the circles, but what it also does is reversing the order of the lines (of the whole FeatureGroup), so now the less important lines are on top of the more important. From what I get, bringToBack() take the first drawn line, bring it to the back, then the second drawn line, put it to the back (under the first one) and so on, so the first drawn (the least important) will be put to the front while the last drawn (the most important) will be put to the back.

So I tried a few modification of the function :

function(el, x) {
      this.on('overlayadd', function(e) {
        var layers = e.layer.getLayers();
        e.layer.clearLayers();
        layers.reverse();
        layers.forEach(l => e.layer.addLayer(l));
        e.layer.bringToBack();
      })

And it didn't work. So now I don't get it, because technically now the first drawn is the one that I want to be last drawn, and therefore, the reversing of the bringToBack should do it. But no, the order is still the same as the other function. And,I tried without the reverse, it is still the same result. I also tried setZIndex, putting the base layer at a high Z-Index, while the Overlay is brought back to a low Z-Index, but it didn't had any effect (lines on top of the circles).

And I tried to change the autoZIndex and sortLayers options of layerControlOptions and it had no effect (lines on top of the circles).

So what I would like is : either an explanation of the magic behind bringToBack, or a js function that will do the trick, or an R solution that will keep the order how I want it.


Solution

  • The function addMapPane in library(leaflet) can be used to fix this problem without custom JS code.

    From the description:

    map panes can be created by supplying a name and a zIndex to control layer ordering. We recommend a zIndex value between 400 (the default overlay pane) and 500 (the default shadow pane). You can then use this pane to render overlays (points, lines, polygons) by setting the pane argument in leafletOptions. This will give you control over the order of the layers, e.g. points always on top of polygons. If two layers are provided to the same pane, overlay will be determined by order of adding. See examples below. See https://leafletjs.com/reference-1.3.4.html#map-pane for details.

    See the example in the help. Type ?addMapPane in the console