1

I would like to add an opacity slider to a leaflet map generated in r. It should change the opacity of the uppermost visible or all visible layers (either of the two, I don't mind). I found helpful advice in this thread: Add opacity slider in R leaflet

This blog: https://bookdown.org/nicohahn/making_maps_with_r5/docs/leaflet.html

and tried the following two packages, but none of the two seems to work:

leaflet.multiopacity

leaflet.opacity

So I figured out, this should be possible with htmltools. However, I struggle with the following

  1. Dragging the slider moves the map too (while changing the opacity of the labels), as pointed out in Laurens comment here: Add opacity slider in R leaflet

  2. I would like to change the opacity of the selected overlay layers. However I neither manage to get those values nor to change their opacity value.

Not helpful in this cause is, that I have almost no command of Javascript. Here is an example on how far I got. Would appreciate anyone pointing me into the right direction. I don't even know how the access variables filled by htmltools javascript commands.

library(leaflet)
library(mapview) #to get the franconia dataset
library(htmltools)

colors <- colorFactor(palette = c("Red", "Green", "Blue"),
                      levels = c("Oberfranken","Mittelfranken", "Unterfranken"))


franconia %>% leaflet() %>% 
  addProviderTiles("CartoDB.Positron", group = "CartoDB.Positron") %>% 
  addPolygons(fillColor = ~colors(district),weight =  1, group = "Districts") %>%
  addPolygons(label = ~NAME_ASCI,weight =  1, group = "Names", fillColor = "Grey") %>%
  addLayersControl(baseGroups = "CartoDB.Positron",overlayGroups = c("Districts", "Names"),position = "topleft") %>%
  addControl(html = "<input id=\"slide\" type=\"range\" min=\"0\" max=\"1\" step=\"0.1\" value=\"0.5\">") %>%   # Add Slider
  htmlwidgets::onRender("function(el,x,data){
                     var map = this;
                     var evthandler = function(e){
                        var layers = map.layerManager.getVisibleGroups();
                        Object.keys(layer).forEach(function(el){
                             layer[el]._container.style.opacity = +e.target.value;
                             });
                     };
              $('#slide').on('mousemove',L.DomEvent.stopPropagation);
              $('#slide').on('input', evthandler)}
          ")

enter image description here

Beni
  • 191
  • 9

1 Answers1

1

This seems to be working. Any improvements very welcome!

library(leaflet)
library(htmltools)

colors <- colorFactor(palette="viridis",
                      domain=gadmCHE@data$NAME_1, na.color="transparent")

map <- gadmCHE %>% leaflet() %>% 
  addProviderTiles("CartoDB.Positron", group = "CartoDB.Positron") %>% 
  addPolygons(fillColor = ~colors(NAME_1), fillOpacity = 1, group="Colors") %>%
  addPolygons(label = ~NAME_1,weight =  1, group = "Names", fillColor = "Grey") %>%
  addLayersControl(baseGroups = "CartoDB.Positron",overlayGroups = c("Colors", "Names"),position = "topleft") %>%
  addControl(html = "<input id=\"OpacitySlide\" type=\"range\" min=\"0\" max=\"1\" step=\"0.1\" value=\"0.5\">") %>%   # Add Slider
  htmlwidgets::onRender(
    "function(el,x,data){
                     var map = this;
                     var evthandler = function(e){
                        var layers = map.layerManager.getVisibleGroups();
                        console.log('VisibleGroups: ', layers); 
                        console.log('Target value: ', +e.target.value);
                        layers.forEach(function(group) {
                          var layer = map.layerManager._byGroup[group];
                          console.log('currently processing: ', group);
                          Object.keys(layer).forEach(function(el){
                            if(layer[el] instanceof L.Polygon){;
                            console.log('Change opacity of: ', group, el);
                             layer[el].setStyle({fillOpacity:+e.target.value});
                            }
                          });
                          
                        })
                     };
              $('#OpacitySlide').mousedown(function () { map.dragging.disable(); });
              $('#OpacitySlide').mouseup(function () { map.dragging.enable(); });
              $('#OpacitySlide').on('input', evthandler)}
          ")

map

changing the opacity was done by setStyle({fillOpacity:+e.target.value})

To stop the panning was resolved by the command $('#OpacitySlide').mousedown(function () { map.dragging.disable(); })

This code checks for every element in every visible group layer, whether it is a polygon if(layer[el] instanceof L.Polygon). If it is, it then changes the opacity. I wouldn't know how to do that on a group basis.

The code throws messages to the JS-Console. Might be good for other newbies. Thanks to https://plotly-r.com/json.html

enter image description here

Beni
  • 191
  • 9
  • This is really cool @Beni. Is it relatively simple way to extend this example to add two different sliders and adjust the opacity for two different layers? Let me know if you think so, and I'll make a question! – Rich Pauloo Apr 28 '22 at 20:15
  • Well, I guess it is rather easy (but that's always how it seems before you start). However, what two layers would you mean, like named ones or like all point vs. all polygon layers? I can't promise anything, but it's likely I'll extend this code to handle raster layers too. – Beni Apr 29 '22 at 15:23
  • Hey @Beni, thanks for your comment and interest! I was thinking about multiple raster layers, specifically. If you're familiar with Google Earth Engine, it's common to visualize multiple rasters and to adjust the opacity of each. – Rich Pauloo Apr 30 '22 at 22:16
  • 1
    Did you try out the packages mentioned in the question? They might serve your purpose (if they work). From the UX-perspective I think multiple sliders with each of those for one layer are hard to understand as they are not labeled or similar... – Beni May 03 '22 at 05:17
  • Thanks for pointing that out Beni. {{ leaflet.multiopacity }} does exactly this! – Rich Pauloo May 03 '22 at 06:25