radioButtons in shiny app? - r

I have a radioButtons input with Yes and No :
radioButtons("rd", "3 rotor diameter circles:",list("Yes", "No"))
How should I modify my output in server.R, in a way that if the choice is YES, it consider the addCircles command and if is No ignore that line ?
output$mymap <- renderLeaflet({
infile=input$File
if (is.null(infile))
return(NULL)
df.20 <- Coor1
getColor <- function(Layout) {
sapply(Layout$j1, function(j1) {
if(j1 < 1) {
"red"
} else {
"green"
} })
}
icons <- awesomeIcons(
spin=TRUE,
icon = 'star',
iconColor = 'lightgray',
library = 'fa',
markerColor = getColor(df.20)
)
leaflet() %>%
addProviderTiles("OpenTopoMap", group = "MapQuestOpen.Aerial") %>%
addAwesomeMarkers(data = df.20,~long, ~lat, icon=icons, popup = ~as.character(mag), label = ~as.character(Name))%>%
addCircles(data = df.20,lng=~long, lat=~lat,radius=~rad,weight = 1,fill = FALSE)%>%
addMeasure(primaryLengthUnit='meters',primaryAreaUnit='sqmeters')
})

You should use leaflet's proxy method to do that, and add/remove circles in an observer, consider this example :
library("shiny")
library("leaflet")
df.20 <- quakes[1:20,]
ui <- fluidPage(
radioButtons(inputId = "add_circles", label = "Add circles", choices = c("Yes", "No")),
leafletOutput(outputId = "mymap")
)
server <- function(input, output, session) {
# your initial map
output$mymap <- renderLeaflet({
leaflet(df.20) %>% addTiles() %>% addCircles()
})
# add or remove circles when clicking the radio button
observeEvent(input$add_circles, {
if (input$add_circles == "Yes") {
leafletProxy(mapId = "mymap", data = df.20) %>% addCircles()
} else if (input$add_circles == "No") {
leafletProxy(mapId = "mymap") %>% clearShapes()
}
}, ignoreInit = TRUE)
}
shinyApp(ui = ui, server = server)
Look at https://rstudio.github.io/leaflet/shiny.html
Note : here I used clearShapes, it remove also polygons, rectangles and polylines, you can remove only circles by defining layerId in addCircles and use removeShape.

You could try this:
leaflet() %>%
addProviderTiles("OpenTopoMap", group = "MapQuestOpen.Aerial") %>%
addAwesomeMarkers(data = df.20,~long, ~lat, icon=icons, popup = ~as.character(mag), label = ~as.character(Name))%>%
{if(input$rd == "Yes")addCircles(data = df.20,lng=~long, lat=~lat,radius=~rad,weight = 1,fill = FALSE)}%>%
addMeasure(primaryLengthUnit='meters',primaryAreaUnit='sqmeters')
i have used if statement inside of the leaflet code, saying that if input$rd == "Yes", then addCircles(...) should be considered.

Related

R Shiny Leaflet. Assigning and toggling markers assigned to multiple groups

I am trying to create a Shiny app that uses leaflet to display map markers. I want the user to be able to toggle between groups of markers using the UI. With some help from StackOverflow, this has been achieved as follows:
library(shiny)
library(shinyjs)
library(leaflet)
library(dplyr)
ui <- fluidPage(
uiOutput("quakesToggle"),
leafletOutput("mymap",height = '100vh')
)
server <- function(input, output) {
data(quakes)
output$mymap <- renderLeaflet({
leaflet(data = quakes) %>%
setView(lng = quakes[1, 'long'],
lat = quakes[1, 'lat'],
zoom=5) %>%
addTiles() %>%
addCircleMarkers(data = quakes[quakes$mag>=5, ],
~long,
~lat,
radius=3,
group = 'quakes1',
stroke=FALSE,
fillOpacity=0.5,
color = 'red') %>%
addCircleMarkers(data = quakes[quakes$mag<5, ],
~long,
~lat,
radius=3,
group = 'quakes2',
stroke=FALSE,
fillOpacity=0.5,
color = 'green')
})
output$quakesToggle <- renderUI({
checkboxGroupInput('quakesToggle', 'Select magnitude:',
choices = c('Quakes1','Quakes2'),
selected = c('Quakes1','Quakes2')
)
})
observeEvent(input$quakesToggle, {
if('Quakes1' %in% input$quakesToggle) {
leafletProxy('mymap', data = quakes) %>%
showGroup('quakes1')
} else {
leafletProxy('mymap', data = quakes) %>%
hideGroup('quakes1')
}
if('Quakes2' %in% input$quakesToggle) {
leafletProxy('mymap', data = quakes) %>%
showGroup('quakes2')
} else {
leafletProxy('mymap', data = quakes) %>%
hideGroup('quakes2')
}
})
}
shinyApp(ui, server)
However, I would like to add additional toggles for the user to select further groups. In this example say quakes$depth >=350 and also quakes$depth<350.
I have achieved rough functionality by extending the list of options in input$quakesToggle and adding the additional group designations as new addCircleMarkers assignments.
I would prefer to create a new checkboxGroupInput for the new groups though. In doing so I create a new observeEvent but things start to fall down here given the competing conditions.
I recognise that the problem is that I am attempting to assign points to multiple groups which, if I understand thigs correctly, isn't possible.
How then can I create the functionality required?
Some pointers on how/if this can be achieved would be appreciated!
Further Elaboration
The below shows how I already have color assigned to a variable selection, in this instance station (I know this is a silly assignment but it works for the example). Given that color is already assigned as conditional on input$circle_color. How then would the filter and grouping that #gdevaux mentions below work?
library(shiny)
library(shinyjs)
library(leaflet)
library(dplyr)
ui <- fluidPage(
uiOutput('quakesToggle'),
uiOutput('circle_color'),
leafletOutput("mymap",height = '100vh')
)
server <- function(input, output) {
data(quakes)
output$circle_color <- renderUI({
selectInput('circle_color', label = 'Variable to colour by:',
choices = c('stations'), selected = 'stations')
})
output$quakesToggle <- renderUI({
checkboxGroupInput('quakesToggle', 'Select magnitude:',
choices = c('Quakes1','Quakes2'),
selected = c('Quakes1','Quakes2')
)
})
output$mymap <- renderLeaflet({
leaflet(data = quakes) %>%
setView(lng = quakes[1, 'long'],
lat = quakes[1, 'lat'],
zoom=5) %>%
addTiles()
})
observe({
if (is.null(input$circle_color)){return('stations')}
colorBy <- input$circle_color
colorData <- quakes[[input$circle_color]]
pal1 <- c('green', 'yellow1','orange','red1','purple')
color <- colorBin(pal1, colorData)
leafletProxy('mymap',data = quakes)%>%
clearShapes() %>%
addCircleMarkers(data = quakes[quakes$mag>=5, ],
~long,
~lat,
radius=3,
group = 'quakes1',
stroke=FALSE,
fillOpacity=0.5,
color = ~color(colorData)
) %>%
addCircleMarkers(data = quakes[quakes$mag<5, ],
~long,
~lat,
radius=3,
group = 'quakes2',
stroke=FALSE,
fillOpacity=0.5,
color = ~color(colorData)
) %>%
addLegend("topright",
pal=color,
values=colorData,
title=colorBy,
layerId="colorLegend"
)
})
observeEvent(input$quakesToggle, {
if (is.null(input$circle_color)) {
return('stations')
}
if('Quakes1' %in% input$quakesToggle) {
leafletProxy('mymap', data = quakes) %>%
showGroup('quakes1')
} else {
leafletProxy('mymap', data = quakes) %>%
hideGroup('quakes1')
}
if('Quakes2' %in% input$quakesToggle) {
leafletProxy('mymap', data = quakes) %>%
showGroup('quakes2')
} else {
leafletProxy('mymap', data = quakes) %>%
hideGroup('quakes2')
}
}, ignoreNULL = FALSE)
}
shinyApp(ui, server)
A possible solution if you want to have multiple filters is to filter your data first, and reload your map every time.
library(shiny)
library(shinyjs)
library(leaflet)
library(dplyr)
mydata <- quakes %>%
mutate(binary_mag = if_else(mag>=5, 1, 2),
binary_depth = if_else(depth>=350, 1, 2),
colorgroup = paste(binary_mag, binary_depth))
mydata$colorgroup[mydata$colorgroup=="1 1"] <- "red"
mydata$colorgroup[mydata$colorgroup=="1 2"] <- "green"
mydata$colorgroup[mydata$colorgroup=="2 1"] <- "blue"
mydata$colorgroup[mydata$colorgroup=="2 2"] <- "yellow"
ui <- fluidPage(
uiOutput("quakesToggle"),
leafletOutput("mymap",height = '100vh')
)
server <- function(input, output) {
# Make filters input
output$quakesToggle <- renderUI({
tagList(
checkboxGroupInput('quakesToggleM', 'Select magnitude:',
choices = c('mag>=5','mag<5'),
selected = c('mag>=5','mag<5')
),
checkboxGroupInput('quakesToggleD', 'Select depth:',
choices = c('depth>=350','depth<350'),
selected = c('depth>=350','depth<350')
)
)
})
# Filter data depending on inputs, you may have to modify here depending on how you want
# mag and depth inputs to interact (is it mag AND depth ? is it mag OR depth ? etc)
filtred_data <- eventReactive({
input$quakesToggleM
input$quakesToggleD}, {
res <- mydata %>% {
if(!is.null(input$quakesToggleM)){
filter(., eval(parse(text = paste(input$quakesToggleM, collapse = "|"))))
} else {
filter(.,TRUE)
}
} %>%{
if(!is.null(input$quakesToggleD)){
filter(., eval(parse(text = paste(input$quakesToggleD, collapse = "|"))))
} else {
filter(.,TRUE)
}
}
if(is.null(input$quakesToggleM) & is.null(input$quakesToggleD)){
res <- mydata %>% filter(FALSE)
}
return(res)
}, ignoreNULL = FALSE)
# Make base map
output$mymap <- renderLeaflet({
leaflet(data = quakes) %>%
setView(lng = quakes[1, 'long'],
lat = quakes[1, 'lat'],
zoom=5) %>%
addTiles()
})
# Add filtred data markers on map
observeEvent(filtred_data(), {
leafletProxy("mymap") %>%
clearGroup("quakes") %>%
addCircleMarkers(data = filtred_data(),
~long,
~lat,
radius=3,
stroke=FALSE,
group = "quakes",
fillOpacity=0.5,
color = ~colorgroup)
# add a legend would be great
})
}
shinyApp(ui, server)
And to work with a color input just modify the observeEvent
# Add filtred data markers on map
observeEvent({
filtred_data()
input$circle_color
}, {
colorBy <- input$circle_color
colorData <- quakes[[input$circle_color]]
pal1 <- c('green', 'yellow1','orange','red1','purple')
color <- colorBin(pal1, colorData)
leafletProxy("mymap") %>%
clearGroup("quakes") %>%
removeControl("colorLegend") %>%
addCircleMarkers(data = filtred_data(),
~long,
~lat,
radius=3,
stroke=FALSE,
group = "quakes",
fillOpacity=0.5,
color = ~color(colorData)) %>%
addLegend("topright",
pal=color,
values=colorData,
title=colorBy,
layerId="colorLegend"
)
})

R Shiny Leaflet Toggle groups of points from UI checkboxGroupInput as opposed to addLayersControl

Given a Shiny app displaying multiple groups of map points via Leaflet, I want to create a toggle to show/hide specific groups.
I am able to achieve this via addLayersControl as per the following example:
library(shiny)
library(shinyjs)
library(leaflet)
library(dplyr)
ui <- fluidPage(
leafletOutput("mymap",height = '100vh')
)
server <- function(input, output) {
data(quakes)
output$mymap <- renderLeaflet({
leaflet(data = quakes) %>%
setView(lng = quakes[1, 'long'],
lat = quakes[1, 'lat'],
zoom=5) %>%
addTiles()
})
observe({
leafletProxy('mymap', data = quakes) %>%
clearShapes() %>%
addCircleMarkers(data = quakes[quakes$mag>=5, ],
~long,
~lat,
radius=3,
group = 'quakes1',
stroke=FALSE,
fillOpacity=0.5,
color = 'red') %>%
addCircleMarkers(data = quakes[quakes$mag<5, ],
~long,
~lat,
radius=3,
group = 'quakes2',
stroke=FALSE,
fillOpacity=0.5,
color = 'green') %>%
addLayersControl(overlayGroups = c('quakes1','quakes2'),
options = layersControlOptions(collapsed = FALSE))
})
}
shinyApp(ui, server)
However, I would prefer for the toggle to handled by checkboxGroupInput that is created server side. Something like:
# Changing `mag` to a binary classification for simplicity
quakes$mag<- ifelse(quakes$mag >=5, 1, 2)
output$quakesToggle <- renderUI({
checkboxGroupInput('quakesToggle', 'Select magnitude:',
choices = list('Quakes1'=1,'Quakes2'=2)
)
})
observe({
toggle(id='foo', condition = 'Quakes1' %in% input$quakesToggle)
toggle(id='bar', condition = 'Quakes2' %in% input$quakesToggle)
})
The problem is I don't know how or even if it's possible to associate the toggle id with the relevant groupid for the leaflet map points...
Your help is appreciated.
This is a possible solution :
library(shiny)
library(shinyjs)
library(leaflet)
library(dplyr)
ui <- fluidPage(
uiOutput("quakesToggle"),
leafletOutput("mymap",height = '100vh')
)
server <- function(input, output) {
data(quakes)
output$mymap <- renderLeaflet({
leaflet(data = quakes) %>%
setView(lng = quakes[1, 'long'],
lat = quakes[1, 'lat'],
zoom=5) %>%
addTiles() %>%
addCircleMarkers(data = quakes[quakes$mag>=5, ],
~long,
~lat,
radius=3,
group = 'quakes1',
stroke=FALSE,
fillOpacity=0.5,
color = 'red') %>%
addCircleMarkers(data = quakes[quakes$mag<5, ],
~long,
~lat,
radius=3,
group = 'quakes2',
stroke=FALSE,
fillOpacity=0.5,
color = 'green')
})
output$quakesToggle <- renderUI({
checkboxGroupInput('quakesToggle', 'Select magnitude:',
choices = c('Quakes1','Quakes2'),
selected = c('Quakes1','Quakes2')
)
})
observeEvent(input$quakesToggle, {
if('Quakes1' %in% input$quakesToggle) {
leafletProxy('mymap', data = quakes) %>%
showGroup('quakes1')
} else {
leafletProxy('mymap', data = quakes) %>%
hideGroup('quakes1')
}
if('Quakes2' %in% input$quakesToggle) {
leafletProxy('mymap', data = quakes) %>%
showGroup('quakes2')
} else {
leafletProxy('mymap', data = quakes) %>%
hideGroup('quakes2')
}
}, ignoreNULL = FALSE)
}
shinyApp(ui, server)

Split code of one leaflet map (so that input updates of one part does not affect other part of code)

Is it possible to split the code of a map so that a part of the map only updates if it's own input is changed?
In the reproducible example below, when selecting the "toner" tile and selecting a new station, the whole leaflet map is executed again because addLegend needs to be updated. Which makes the tile jump back to "OSM (default)" tile. I would like to stay at the tile I selected when I select other stations.
library(leaflet)
library(shiny)
library(dplyr)
pal <- colorFactor(
palette = "YlGnBu",
domain = quakes$stations
)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("stations",
"Choose a station",
choices=sort(unique(quakes$stations)),
selected = c(10, 11))
),
mainPanel(
leafletOutput("map")
)
)
)
server <- function(input, output) {
points <- reactive({
quakes %>%
filter(stations %in% input$stations)
})
output$map <- renderLeaflet({
leaflet(quakes) %>%
addTiles(group = "OSM (default)") %>%
addProviderTiles(providers$Stamen.Toner, group = "Toner") %>%
addLayersControl(
baseGroups = c("OSM (default)", "Toner"),
options = layersControlOptions(collapsed = FALSE)) %>%
addLegend("Legend", position = "topleft", pal = pal, values = input$stations)
})
observe({
if(nrow(points()) == 0) {
leafletProxy("map", data = points()) %>%
clearMarkers()
} else {
leafletProxy("map", data = points()) %>%
clearMarkers() %>%
addCircleMarkers(radius = 2)
}
})
}
shinyApp(ui, server)
I tried several things, including adding addLegend to the else statement, but that does not go well. I'm new to leaflet/shiny, moving addLegend seemed most logic to me. I really appreciate any suggestions!
As far as I get it you were on the right track by trying to move addLegend to the observer. Doing so worked fine for me.
Move addLegend to observe
Before adding the legend use clearControls to remove any existing legend (otherwise you get multiple legends)
I removed the duplicated code in the observe
As far as I get it the condition nrow(points()) > 0 is only needed to decide whether a legend should be drawn or not. For the markers it doesn't matter.
library(leaflet)
library(shiny)
library(dplyr)
pal <- colorFactor(
palette = "YlGnBu",
domain = quakes$stations
)
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
checkboxGroupInput("stations",
"Choose a station",
choices=sort(unique(quakes$stations)),
selected = c(10, 11))
),
mainPanel(
leafletOutput("map")
)
)
)
server <- function(input, output) {
points <- reactive({
quakes %>%
filter(stations %in% input$stations)
})
output$map <- renderLeaflet({
leaflet(quakes) %>%
addTiles(group = "OSM (default)") %>%
addProviderTiles(providers$Stamen.Toner, group = "Toner") %>%
addLayersControl(
baseGroups = c("OSM (default)", "Toner"),
options = layersControlOptions(collapsed = FALSE))
})
observe({
proxy <- leafletProxy("map", data = points()) %>%
clearMarkers() %>%
clearControls() %>%
addCircleMarkers(radius = 2)
if (nrow(points()) > 0)
proxy <- proxy %>% addLegend("Legend", position = "topleft", pal = pal, values = input$stations)
proxy
})
}
shinyApp(ui, server)

Leaflet in Shiny: Points are not plotted on initialization of app when using the search plugin in Firefox

I have a shiny app, where I want to plot CircleMarkers on a leaflet map. Additionally a marker should be plotted, controlled over an overlayGroup. When the zoom level is greater than 7 the marker should be plotted otherwise not. This is done by sending code to the server and getting the index of the marker. See also here: Show layer in leaflet map in Shiny only when zoom level > 8 with LayersControl?
It works fine, but when I add the addSearchOSM plugin from leaflet.extras the CircleMarkers will not be plotted anymore when the app starts. So the observe statement will not be rendered until I change an input.
This is the code:
library(leaflet)
library(leaflet.extras)
library(shiny)
data <- data.frame(longitude = c(11.43, 11.55), latitude = c(48, 48.5), label = c("a", "b"))
getInputwithJS <- '
Shiny.addCustomMessageHandler("findInput",
function(message) {
var inputs = document.getElementsByTagName("input");
console.log(inputs);
Shiny.onInputChange("marker1", inputs[1].checked);
}
);
'
ui <- fluidPage(
sidebarPanel(
selectInput("label", "label", selected = "a", choices = data$label)
),
mainPanel(
leafletOutput("map", width = "100%", height = "700"),
tags$head(tags$script(HTML(getInputwithJS)))
)
)
server <- function(input, output, session){
# subset data according to label input
data_subset <- reactive({
data[data$label %in% input$label, ]
})
output$map <- renderLeaflet({
leaflet() %>% addTiles() %>% setView(11, 48.5, 7) %>%
addLayersControl(overlayGroups = c("marker1"),
options = layersControlOptions(collapsed = FALSE)) %>%
addSearchOSM()
})
# does not show points when app starts
observe({
leafletProxy("map") %>% clearGroup("points") %>%
addCircleMarkers(data_subset()$longitude, data_subset()$latitude, group = "points")
})
global <- reactiveValues(DOMRdy = FALSE)
autoInvalidate <- reactiveTimer(1000)
observe({
autoInvalidate()
if(global$DOMRdy){
session$sendCustomMessage(type = "findInput", message = "")
}
})
session$onFlushed(function() {
global$DOMRdy <- TRUE
})
# add marker if marker is clicked in layerscontrol and zoom level of map > 7
observe({
if (!is.null(input$marker1)){
if (input$marker1 == TRUE){
if (input$map_zoom > 7) {
leafletProxy("map") %>% addMarkers(lng = 11.2, lat = 48, group = "marker1")
}else{
leafletProxy("map") %>% clearGroup(group = "marker1")
}
}
}
})
}
shinyApp(ui, server)

clearShapes() not working -- leaflet() for R

I cannot figure out why clearshapes() is not working in my leaflet shiny program. I am trying to remove the existing circles and replace with a category that is selected based on the input check box that I have. However, what happens is that new circles are overlayed on top of the existing ones.
Anyone encounter this before?
df = read.csv("mappingData.csv",header=T, sep =",")
ui = fluidPage(
checkboxGroupInput("set", label = "Pothole Reported by:",
choices = list("Citizens Connect App" = "Citizens Connect App",
"City Worker App" = "City Worker App",
"Constituent Call" = "Constituent Call",
"Self Service" = "Self Service",
"Employee Generated" = "Employee Generated",
"Not Available (Cambridge)" = "")),
verbatimTextOutput("value"),
leafletOutput("map")
)
server <- function(input, output) {
filteredDataCheck <- reactive({
# subset(df, Source == input$set)
print(input$set)
})
output$value <- renderPrint ({ filteredDataCheck() })
filteredData <- reactive({
df[as.character(df$Source) == input$set, ]
})
output$map <- renderLeaflet ({
leaflet(df) %>%
setView(-71.083, 42.353, 13) %>%
addProviderTiles("Stamen.TonerLite", options = providerTileOptions(noWrap=T))
})
observe({
leafletProxy("map", data = filteredData() ) %>% clearShapes() %>%
addCircles(radius = 1, color = "red", group = "circles") %>% clearShapes()
})
}
shinyApp(ui = ui, server = server)
Looks like there is an issue when filteredData() is empty. You can trying adding an if/else:
if(nrow(filteredData())==0) { leafletProxy("map") %>% clearShapes()}
else {
leafletProxy("map", data = filteredData() ) %>% clearShapes() %>%
addCircles(radius = 1, color = "red", group = "circles")
}
Also if you want all the data points that have a selected Source, you might want to use %in% instead of == in your filtering:
df[as.character(df$Source) %in% input$set, ]

Resources