How to Hide/Toggle Legends Based on Addlayercontrol() in Leaflet for R

How to hide/toggle legends based on addLayerControl() in Leaflet for R

In case anyone is looking at this in the future, there's a new viable method.

If you're using the development/Github version of the leaflet package, addLegend() now supports the group and layerId arguments.

(Run the command devtools::install_github('rstudio/leaflet') to install)

Even the latest CRAN version (1.1.0) supports layerID.

Assuming you go with the development version, the following code should work:

leaflet() %>%
#Polygon Layers
addPolygons(data = exampleDataOne, group = "Group A") %>%
addPolygons(data = exampleDataTwo, group = "Group B") %>%

#Legend Layers
addLegend(values = exampleValuesOne, group = "Group A",
position = "bottomright") %>%
addLegend(values = exampleValuesTwo, group = "Group B",
position = "bottomright") %>%

#Layers Control
addLayersControl(overlayGroups = c("Group A","Group B"),
options = layersControlOptions(collapsed = FALSE))

This should have the desired effect of only displaying a legend when the corresponding layer is active.

One potential issue is that by default, all leaflet layers are active. If you have overlapping polygons, this can lead to a less visually appealing map. And, if you have multiple legends, this will also be an issue.

What you can do is use the hideGroup() function.

If you wanted to have only Group A from the above example active at start, you could add

%>% hideGroup("Group B")

to the end of the code block above.

How to show/hide legend with control layer panel with leaflet?

Code

Unfortunately, you did not provide a reprex, so I show it with a made up example:

library(leaflet)

cities1 <- data.frame(City = factor(c("Boston", "Hartford",
"New York City", "Philadelphia", "Pittsburgh", "Providence")),
Lat = c(42.3601, 41.7627, 40.7127, 39.95, 40.4397, 41.8236),
Long = c(-71.0589, -72.6743, -74.0059, -75.1667, -79.9764, -71.4222),
Pop = c(645966L, 125017L, 8406000L, 1553000L, 305841L, 177994L),
Type = factor(c("C", "D", "A", "A", "B", "C")))

cities2 <- data.frame(City = factor(c("Baltimore", "Ithaca", "Wareham")),
Lat = c(39.299236, 42.443962, 41.761452),
Long = c(-76.609383, -76.501884, -70.719734),
Pop = c(609032L, 30569L, 22666L),
Type = factor(letters[1:3]))

pal1 <- colorFactor("viridis", domain = cities1$Type)
pal2 <- colorFactor("Set1", domain = cities2$Type)

leaflet(cities1) %>%
addTiles() %>%
addCircles(data = cities1, lng = ~Long, lat = ~Lat, weight = 1, group="one",
radius = ~sqrt(Pop) * 30, popup = ~City, color = ~pal1(Type), opacity = .9
) %>%
addLegend(pal = pal1, values = ~Type, group = "one", layerId = "one") %>%
addCircles(data = cities2, lng = ~Long, lat = ~Lat, weight = 1, group = "two",
radius = ~sqrt(Pop) * 30, popup = ~City, color = ~pal2(Type), opacity = .9

) %>%
addLegend(pal = pal2, values = ~Type, data = cities2, group = "two", layerId = "two") %>%
addLayersControl(
baseGroups = c("one", "two"),
options = layersControlOptions(collapsed = FALSE),
position = "topleft"
) %>%
htmlwidgets::onRender("
function() {
var map = this;
var legends = map.controls._controlsById;
function addActualLegend() {
var sel = $('.leaflet-control-layers-base').find('input[type=\"radio\"]:checked').siblings('span').text().trim();
$.each(map.controls._controlsById, (nm) => map.removeControl(map.controls.get(nm)));
map.addControl(legends[sel]);
}
$('.leaflet-control-layers-base').on('click', addActualLegend);
addActualLegend();
}")

Explanation

You can define some custom JavaScript which reacts upon the changes of the radio buttons. When they change, I basically delete all controls and add the selected. In order for this to work, I need to save a copy of the controls first. This is of course a bit hackish (especially since I am accessing the "private" _controlsById slot of the map), but from my quick scan of the leaflet API I did not find a better entry point.

Screenshot

Dynamic Legend

Is it possible to switch between multiple legends when switching between base groups?

It seems legends in baseGroups won't remove/re-added as if in overlayGroups, which can be further proved by the persistence of legends even after calling hideGroup("var1").

A crude workaround can be adding an event handler to hide/unhide legends according to the current selected group of baseGroups using group = "<groupName>" as a key, and nothing else should need to be changed. For example:

htmlwidgets::onRender("
function(el, x) {
var updateLegend = function () {
var selectedGroup = document.querySelectorAll('input:checked')[0].nextSibling.innerText.substr(1);

document.querySelectorAll('.legend').forEach(a => a.hidden=true);
document.querySelectorAll('.legend').forEach(l => {
if (l.children[0].children[0].innerText == selectedGroup) l.hidden=false;
});
};
updateLegend();
this.on('baselayerchange', e => updateLegend());
}")

Demo

demo of workaround

Source of Demo

require(leaflet)

data <- data.frame(long = c(-93.2, -93, -93.5), lat = c(44.9, 45, 44.9),
var1 = c(1,2,3), var2 = c(10, 9, 1))

pal1 <- colorNumeric(palette = "Blues", domain = data$var1)
pal2 <- colorNumeric(palette = "Reds", domain = data$var2)

leaflet(data) %>%
addCircleMarkers(color = ~pal1(var1), group = "var1") %>%
addCircleMarkers(color = ~pal2(var2), group = "var2") %>%
addLegend(pal = pal1, values = ~var1, group = "var1") %>%
addLegend(pal = pal2, values = ~var2, group = "var2") %>%
addLayersControl(baseGroups = c("var1", "var2"),
position = "topleft",
options = layersControlOptions(collapsed=F)) %>%
htmlwidgets::onRender("
function(el, x) {
var updateLegend = function () {
var selectedGroup = document.querySelectorAll('input:checked')[0].nextSibling.innerText.substr(1);

document.querySelectorAll('.legend').forEach(a => a.hidden=true);
document.querySelectorAll('.legend').forEach(l => {
if (l.children[0].children[0].innerText == selectedGroup) l.hidden=false;
});
};
updateLegend();
this.on('baselayerchange', e => updateLegend());
}")

Making selectizeInput reactive to show/hide markers on Leaflet Shiny R

Try this

library(leaflet)
library(shiny)
library(shinydashboard)
library(dplyr)

#Data Sample

long <- c(-96.72363, -96.72880, -96.72700)
lat <- c(17.06167, 17.06200, 17.06170 )
name <- c("jim", "grant", "pablo")
food <- c("tacos", "burger", "elote")

df <- data.frame(long, lat, name, food)

#subsets

palette <- colorFactor(palette = c("blue", "green", "black"),
domain = df$food)
tacos <- dplyr::filter(df, grepl("tacos", food))

burger <- dplyr::filter(df, grepl("burger", food))

elote <- dplyr::filter(df, grepl("elote", food))

ui <-fluidPage(
titlePanel(title = "Street Food Oaxaca"),
sidebarLayout(
sidebarPanel(
selectizeInput("food","Select a Type:",
choices = unique(df$food),
selected = food[1],
multiple = TRUE)),
mainPanel(h3("Map"), leafletOutput("m", width = "800", height = "600"))
)
)

#server
server <- function(input, output, session){

df1 <- eventReactive(input$food, {
df %>% dplyr::filter(food %in% input$food)
})

output$m = renderLeaflet({
leaflet(data = df1()) %>%
setView(lng = -96.725, lat = 17.0618, zoom =14)%>%
addTiles() %>%
addCircleMarkers( # layerId = input$food,
# data = input$food,
~long,
~lat,
#group = input$food,
#popup = ~as.character(name),
radius = 2,
color = ~palette(input$food),
fillOpacity = 0.5) %>%
# addCircleMarkers( layerId = burger,
# data = burger,
# ~long,
# ~lat,
# group = 'burger',
# popup = ~as.character(name),
# radius = 2,
# color = ~palette(food),
# fillOpacity = 0.5) %>%
# addCircleMarkers( layerId = elote,
# data = elote,
# ~long,
# ~lat,
# group = 'elote',
# popup = ~as.character(name),
# radius = 2,
# color = ~palette(food),
# fillOpacity = 0.5) %>%
# addPolygons(data = poly,
# ~long,
# ~lat,
# weight = 3,
# color = "red",
# group = "Restricted Zone" ,
# popup = "Restricted Zone")
addLayersControl(
overlayGroups = c("tacos","burger","elote", "Restricted Zone"),
options = layersControlOptions(collapsed = TRUE)
)


})
}

#Run the application
shinyApp(ui = ui , server = server)

R leaflet - Show/Hide addControl() element with group layers

Alright, now I think i understand your problem. Below is another example, which shows only the legend and control of the active groups. For that, I created 2 html_legends for group A and for group B.

library(shiny)
library(leaflet)

html_legend_A <- "<img src='http://leafletjs.com/docs/images/leaf-green.png'>green<br/>"
html_legend_B <- "<img src='http://leafletjs.com/docs/images/leaf-red.png'>red<br/>"

ui <- fluidPage(
leafletOutput("map")
)
server <- function(input, output, session){
output$map <- renderLeaflet({
map <- leaflet(data = quakes) %>% addTiles() %>%
addMarkers(~long, ~lat, icon = leafIcons, group = "Group A", layerId = "A") %>%
addMarkers(~long, ~lat, icon = leafIcons, group = "Group B", layerId = "B") %>%
addLayersControl(position = "topleft", overlayGroups = c("Group A","Group B"))
map
})

observe({
map <- leafletProxy("map") %>% clearControls()
if (any(input$map_groups %in% "Group A")) {
map <- map %>%
addControl(html = html_legend_A, layerId = "A", position = "bottomleft") %>%
addLegend(title="Group A", position="bottomright", opacity=1, colors="green",labels = "Group A")}
if (any(input$map_groups %in% "Group B")) {
map <- map %>%
addControl(html = html_legend_B, layerId = "B", position = "bottomleft") %>%
addLegend(title="Group B", position="bottomright", opacity=1,colors="red",labels = "Group B")}
})
}

shinyApp(ui, server)

When using the LayerId argument, it only shows 1 marker per group. If you want to see all markers, the LayerId argument should not be given. I made you another example. I think this should be right now :) I also create 2 icons and I am filtering the quakes data, based on the mag-column inside the renderLeaflet function, as you do in the icon assignment.

library(shiny)
library(leaflet)

data(quakes)
quakes <- quakes[1:10,]

leafIcons_A <- icons(
iconUrl = "https://leafletjs.com/examples/custom-icons/leaf-green.png",
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94)
leafIcons_B <- icons(
iconUrl = "https://leafletjs.com/examples/custom-icons/leaf-red.png",
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94)

html_legend_A <- "<img src='https://leafletjs.com/examples/custom-icons/leaf-green.png'>green<br/>"
html_legend_B <- "<img src='https://leafletjs.com/examples/custom-icons/leaf-red.png'>red<br/>"

ui <- fluidPage(
leafletOutput("map")
)
server <- function(input, output, session){
output$map <- renderLeaflet({
dataA <- quakes[quakes$mag < 4.6,]
dataB <- quakes[quakes$mag > 4.6,]

map <- leaflet() %>% addTiles() %>%
addMarkers(dataA$long, dataA$lat, icon = leafIcons_A, group = "Group A") %>%
addMarkers(dataB$long, dataB$lat, icon = leafIcons_B, group = "Group B") %>%
addLayersControl(position = "topleft", overlayGroups = c("Group A","Group B"))
map
})

observe({
map <- leafletProxy("map") %>% clearControls()
if (any(input$map_groups %in% "Group A")) {
map <- map %>%
addControl(html = html_legend_A, position = "bottomleft") %>%
addLegend(title="Group A", position="bottomright", opacity=1, colors="green",labels = "Group A")}
if (any(input$map_groups %in% "Group B")) {
map <- map %>%
addControl(html = html_legend_B, position = "bottomleft") %>%
addLegend(title="Group B", position="bottomright", opacity=1,colors="red",labels = "Group B")}
})
}

shinyApp(ui, server)

How to turn off the layer display of addLayersControl first when launching the shiny app

If I understand correctly you need to add %>% hideGroup("Group B") after addLayersControl(position = "topleft", overlayGroups = c("Group A","Group B")) as below:

library(shiny)
library(leaflet)

data(quakes)
quakes <- quakes[1:10,]

leafIcons_A <- icons(
iconUrl = "https://leafletjs.com/examples/custom-icons/leaf-green.png",
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94)
leafIcons_B <- icons(
iconUrl = "https://leafletjs.com/examples/custom-icons/leaf-red.png",
iconWidth = 38, iconHeight = 95,
iconAnchorX = 22, iconAnchorY = 94)

html_legend_A <- "<img src='https://leafletjs.com/examples/custom-icons/leaf-green.png'>green<br/>"
html_legend_B <- "<img src='https://leafletjs.com/examples/custom-icons/leaf-red.png'>red<br/>"

ui <- fluidPage(
leafletOutput("map")
)
server <- function(input, output, session){
output$map <- renderLeaflet({
dataA <- quakes[quakes$mag < 4.6,]
dataB <- quakes[quakes$mag > 4.6,]

map <- leaflet() %>% addTiles() %>%
addMarkers(dataA$long, dataA$lat, icon = leafIcons_A, group = "Group A") %>%
addMarkers(dataB$long, dataB$lat, icon = leafIcons_B, group = "Group B") %>%
addLayersControl(position = "topleft", overlayGroups = c("Group A","Group B")) %>%
hideGroup("Group B")
map
})

observe({
map <- leafletProxy("map") %>% clearControls()
if (any(input$map_groups %in% "Group A")) {
map <- map %>%
addControl(html = html_legend_A, position = "bottomleft") %>%
addLegend(title="Group A", position="bottomright", opacity=1, colors="green",labels = "Group A")}
if (any(input$map_groups %in% "Group B")) {
map <- map %>%
addControl(html = html_legend_B, position = "bottomleft") %>%
addLegend(title="Group B", position="bottomright", opacity=1,colors="red",labels = "Group B")}
})
}

shinyApp(ui, server)


Related Topics



Leave a reply



Submit