I'm currently developing an R Shiny application where I'm mapping services providers on a map and when I click on a specific marker I have a popup with additional information and I would like to include a downloadButton in that popup. Unfortunately when I'm calling the downloadHandler it doesn't work and I'm downloading a html file called qwe_download.html. But if I put the downloadButton outside the popup (i.e. in the ui) then it works. Is it possible to use a downloadButton inside a leaflet popup?
I can't share the original code as it is quite sensitive but you can find below what I'm trying to achieve.
library('leaflet')
library('shinydashboard')
id <- c(1, 2, 3)
lat <- c(10.01, 10.6, 10.3)
long <- c(0.2, 0.3, 0.4)
name <- c('test1', ' test2', 'test3')
test <- data_frame(id, lat, long, name)
#User interface
header <- dashboardHeader(title = 'Title', titleWidth = 900)
sidebar <- dashboardSidebar(
width = 300)
body <- dashboardBody(
tags$style(type = "text/css", "#map {height: calc(100vh - 80px) !important;}"),
leafletOutput("map")
)
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output, session) {
data <- reactiveValues(clickedMarker=NULL)
output$map <- renderLeaflet({
mymap <- leaflet() %>%
addTiles() %>%
addMarkers(data = test, lng = long, lat = lat, layerId = id,
popup = paste0(
"<div>",
"<h3>",
"Name: ",
test$name,
downloadButton(outputId = "dlData",label = "Download Details"),
"</div>"))
observeEvent(input$map_marker_click,{
print("observed map_marker_click")
data$clickedMarker <- input$map_marker_click
print(data$clickedMarker)
x <- filter(test, id == data$clickedMarker$id)
view(x)})
data_react <- reactive({
data_table <- filter(test, test$id == data$clickedMarker$id)
})
output$dlData <- downloadHandler(
filename = "dataset.csv",
content = function(file) {
write.csv(data_react(), file)
}
)
mymap
})
}
# Run app ----
shinyApp(ui, server)
Note that the observeEvent block was just there for me to check if my code was filtering the right selection.
Hope this makes sense.
Thanks!
You need to bind the downloadButtons yourself after placing them in the popup.
Please see this related answer from Joe Cheng.
Here you can find some great answers on how to bindAll custom inputs in a leaflet popup.
And this is how to apply those answers regarding your particular requirements:
library('leaflet')
library('shinydashboard')
id <- c(1, 2, 3)
lat <- c(10.01, 10.6, 10.3)
long <- c(0.2, 0.3, 0.4)
name <- c('test1', ' test2', 'test3')
test <- data.frame(id, lat, long, name)
header <- dashboardHeader(title = 'Title', titleWidth = 900)
sidebar <- dashboardSidebar(width = 300)
body <- dashboardBody(
tags$div(id = "garbage"),
tags$style(type = "text/css", "#map {height: calc(100vh - 80px) !important;}"),
leafletOutput("map")
)
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output, session) {
data <- reactiveValues(clickedMarker = NULL)
output$map <- renderLeaflet({
mymap <- leaflet() %>%
addTiles() %>%
addMarkers(
data = test,
lng = long,
lat = lat,
layerId = id,
popup = sprintf(
paste0(
"<div>",
"<h3>",
"Name: ",
test$name,
br(),
downloadButton(outputId = "dlData%s", label = "Download Details"),
"</div>"
),
id
)
) %>% htmlwidgets::onRender(
'function(el, x) {
var target = document.querySelector(".leaflet-popup-pane");
var observer = new MutationObserver(function(mutations) {
mutations.forEach(function(mutation) {
if(mutation.addedNodes.length > 0){
Shiny.bindAll(".leaflet-popup-content");
}
if(mutation.removedNodes.length > 0){
var popupNode = mutation.removedNodes[0];
var garbageCan = document.getElementById("garbage");
garbageCan.appendChild(popupNode);
Shiny.unbindAll("#garbage");
garbageCan.innerHTML = "";
}
});
});
var config = {childList: true};
observer.observe(target, config);
}'
)
})
observeEvent(input$map_marker_click,{
print("observed map_marker_click")
data$clickedMarker <- input$map_marker_click
print(data$clickedMarker)
x <- filter(test, id == data$clickedMarker$id)
})
data_react <- reactive({
data_table <- filter(test, test$id == data$clickedMarker$id)
})
lapply(id, function(i) {
output[[paste0("dlData", i)]] <- downloadHandler(
filename = "dataset.csv",
content = function(file) {
write.csv(data_react(), file)
}
)
})
}
shinyApp(ui, server)
The download button is not binded to Shiny. You can use the pointerenter event to run Shiny.bindAll() and the pointerleave event to run Shiny.unbindAll():
library('leaflet')
library('shinydashboard')
library(shiny)
library(dplyr)
id <- c(1, 2, 3)
lat <- c(10.01, 10.6, 10.3)
long <- c(0.2, 0.3, 0.4)
name <- c('test1', ' test2', 'test3')
test <- tibble(id, lat, long, name)
js <- "$('body').on('pointerenter', '#dlData', function(){Shiny.bindAll('#dwnld');}).on('pointerleave', '#dlData', function(){Shiny.unbindAll('#dwnld');})"
header <- dashboardHeader(title = 'Title', titleWidth = 900)
sidebar <- dashboardSidebar(
width = 300)
body <- dashboardBody(
useShinyjs(),
tags$script(HTML(js)),
tags$style(type = "text/css", "#map {height: calc(100vh - 80px) !important;}"),
leafletOutput("map")
)
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output, session) {
data <- reactiveValues(clickedMarker=NULL)
output$map <- renderLeaflet({
mymap <- leaflet() %>%
addTiles() %>%
addMarkers(
data = test, lng = long, lat = lat, layerId = id,
popup = paste0(
"<div id='dwnld'>",
"<h3>",
"Name: ",
test$name,
"</h3>",
downloadButton(
outputId = "dlData", label = "Download Details"
),
"</div>"))
mymap
})
observeEvent(input$map_marker_click,{
data$clickedMarker <- input$map_marker_click
})
data_react <- reactive({
filter(test, id == data$clickedMarker$id)
})
output$dlData <- downloadHandler(
"dataset.csv",
content = function(file) {
write.csv(data_react(), file)
})
}
# Run app ----
shinyApp(ui, server)
To add a summarizing answer, what we need to do:
Call Shiny.[un]bindAll in the "right" moment.
The "right" moment is apparently once the popup is added / removed from the DOM.
Non working downloads can happen as a result of re-using the same id (unfortunately I could not identify a pattern and I thought that unbinding helps, but it does not). Thus, to play it safe creating unique download handlers should avoid this behaviour.
Having said that, the IMHO cleanest option to call Shiny.bindAll() is in response to the popupopen event:
output$map <- renderLeaflet({
mymap <- leaflet() %>%
addTiles() %>%
addMarkers(
data = test, lng = long, lat = lat, layerId = id,
popup = paste0(
"<div id='dwnld'>",
"<h3>",
"Name: ",
test$name,
downloadButton(outputId = "dlData",label = "Download Details"),
"</div>"))
mymap %>% htmlwidgets::onRender(HTML("
function(el, x) {
this.on('popupopen', function() {
Shiny.bindAll('#dwnld');
});
this.on('popupclose', function() {
Shiny.unbindAll('#dwnld');
});
}"))
})
Related
What I would like to do is that if a user clicks on a line, it displays the line name in the box to the right of the map, and if a user clicks somewhere else on the map, it 'deselects' that line:
The problem is that when a user clicks the polyline, leaflet fires both a map_shape_click (the polyline) and map_click (the map) event. Even more annoyingly, it fires the map_shape_click event before the map_click event.
How can I distinguish whether the user has clicked a line, or just the base map, so that my select/deselect works? Reproducible example:
library(shiny)
library(tidyverse)
library(leaflet)
ui <- fluidPage(
fluidRow(
column(
width = 8,
leafletOutput("map")
),
column(
width = 4,
uiOutput("info")
)
)
)
server <- function(input, output) {
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(lng = -71.03165, lat = 42.37595, zoom = 13) %>%
addPolylines(lng = c(-71.05884, -71.02), lat = c(42.360081, 42.359),
layerId = "line1") %>%
addPolylines(lng = c(-71.05884, -71.05), lat = c(42.360081, 42.4),
layerId = "line2")
})
observeEvent(input$map_shape_click, {
x <- input$map_shape_click
output$info <- renderUI({
div(
"Line: ", x$id
)
})
})
observeEvent(input$map_click, {
output$info <- renderUI({
div(
"Nothing selected"
)
})
})
}
shinyApp(ui = ui, server = server)
library(shiny)
library(tidyverse)
library(leaflet)
ui <- fluidPage(
fluidRow(
column(
width = 8,
leafletOutput("map")
),
column(
width = 4,
uiOutput("info")
)
)
)
server <- function(input, output) {
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
setView(lng = -71.03165, lat = 42.37595, zoom = 13) %>%
addPolylines(lng = c(-71.05884, -71.02), lat = c(42.360081, 42.359),
layerId = "line1") %>%
addPolylines(lng = c(-71.05884, -71.05), lat = c(42.360081, 42.4),
layerId = "line2")
})
clicked <- reactiveVal()
observeEvent(input$map_shape_click, {
freezeReactiveValue(input, 'map_click')
clicked(input$map_shape_click)
})
observeEvent(input$map_click, {
clicked(input$map_click)
})
output$info <- renderUI({
req(clicked())
if(is.null(clicked()[['id']])) return(div("Nothing selected"))
div("Line: ", clicked()$id)
})
}
shinyApp(ui = ui, server = server)
Things are a little tricky here. we use freezeReactiveValue to freeze the map click, meaning if there is any shape click event, we do not update the value of map_click. This is a little advanced shiny. I recommend you read the help file and read this chapter: https://mastering-shiny.org/action-dynamic.html#freezing-reactive-inputs
I am trying to add an easyButton with a flyTo function within a shiny app in R.
When the user presses the button, it will fly to the current location (lat/long). I am using a reactivePoll to poll a boat instrument simulator every 5 seconds (NMEA simulator), which is where the lat/long come from. A path is also drawn by using addCircleMarkers. I want to keep this path drawn, and the flyTo button to pan and zoom to the current location without refreshing the map, i.e. removing the path that was drawn.
In my current code with the flyTo button, with every poll the map refreshes. If I remove this code, the map does not refresh, so I think how I'm using the reactive within this button is the issue, but I'm not sure why. It may be because I have a reactive inside a reactive (All_NMEA() inside of renderleaflet()). The code of interest in the reprex is:
addEasyButton(easyButton(
icon = "fa-crosshairs", title = "Locate Vessel",
onClick = JS("
function(btn, map) {
map.flyTo([", paste(as.numeric(All_NMEA()["lat"]) / 100), ",", paste(as.numeric(All_NMEA()["long"]) / -100), "], zoom = 10);
}
")
))
The NMEA simulator is required to produce data that is polled, linked above.
Reproducible example:
# https://chrome.google.com/webstore/detail/nmea-simulator/dfhcgoinjchfcfnnkecjpjcnknlipcll?hl=en
# needs an NMEA simulator to generate the poll data
#
library(shiny)
library(leaflet)
connect <- function() {
s_con <<- socketConnection("127.0.0.1", port = 55555, open = "a+")
Sys.sleep(1)
NMEA_poll <<- readLines(s_con, n = 18)
close(s_con)
return(NMEA_poll)
}
pollGPRMC <- function(data) {
gps_ans <- list(rmc = NULL, rest = data)
rxp <-
"\\$GPRMC(,[^,]*){12}\\*[0-9,A-F]{2}"
beg <- regexpr(rxp, data)
if (beg == -1)
return(gps_ans)
end <-
beg + attr(beg, "match.length")
sub <-
substr(data, beg, end - 6)
gps_ans$rmc <-
strsplit(sub, ",")[[1]]
names(gps_ans$rmc) <- c(
"id_rmc",
"UTC",
"status",
"lat",
"N/S",
"long",
"E/W",
"boat speed (knots)",
"cog (deg)",
"date (ddmmyy)" # ddmmyy
)
gps_ans$rest <- substr(data, end, nchar(data))
return(gps_ans)
}
map_data <- data.frame(lat = c(36.05, 36.25), lon = c(-132.13, -132.33))
ui <- fluidPage(
# Application title
titlePanel("Map"),
mainPanel(tags$style(type = "text/css", "#map {height: calc(100vh - 80px) !important;}"),
leafletOutput("map"))
)
server <- function(input, output, session) {
All_NMEA <- shiny::reactivePoll(
5000,
session,
checkFunc = Sys.time,
valueFunc = function() {
connect()
NMEA_data <- toString(NMEA_poll)
GPS_dat <- pollGPRMC(NMEA_data)
lat_deg <- substr(GPS_dat$rmc["lat"], 1, 2)
lat_mins <- substr(GPS_dat$rmc["lat"], 3, 9)
lat_for_dist <- as.numeric(lat_deg) + (as.numeric(lat_mins) / 60)
print(lat_for_dist)
lon_deg <- substr(GPS_dat$rmc["long"], 1, 3)
lon_mins <- substr(GPS_dat$rmc["long"], 4, 9)
lon_for_dist <- (as.numeric(lon_deg) + (as.numeric(lon_mins) / 60))*-1
print(lon_for_dist)
leafletProxy("map", session = session) %>%
addCircleMarkers(
lng = lon_for_dist,
lat = lat_for_dist,
radius = 1,
fillOpacity = 1, color = "red"
)
NMEA_out <- c(GPS_dat$rmc)
return(NMEA_out)
}
)
ord <- function(data) {
print(data)
}
observe(ord(All_NMEA()))
output$map <- renderLeaflet({
map <- leaflet(map_data) %>%
addProviderTiles(providers$Esri.OceanBasemap, group = "ocean basemap (default)") %>%
addTiles(group = "Basic") %>%
fitBounds( ~ min(lon), ~ min(lat), ~ max(lon), ~ max(lat)) %>%
addLayersControl(
baseGroups = c("ocean basemap (default)", "Basic"),
options = layersControlOptions(collapsed = FALSE)) %>%
fitBounds( ~ min(lon), ~ min(lat), ~ max(lon), ~ max(lat)) %>%
addEasyButton(easyButton(
icon = "fa-crosshairs", title = "Locate Vessel",
onClick = JS("
function(btn, map) {
map.flyTo([", paste(as.numeric(All_NMEA()["lat"]) / 100), ",", paste(as.numeric(All_NMEA()["long"]) / -100), "], zoom = 10);
}
")
))
})
}
shinyApp(ui = ui, server = server)
You answered the question yourself in your last sentence. The map will always be redrawn whenever the reactive All_NMEA changes. To prevent that, you would normally use leafletProxy but apparently you cannot add an easyButton like that, so I offer you another solution.
A click on the easyButton will trigger another shiny input that is called my_easy_button. In an observeEvent you listen to this event and do the flyTo there within a leafletProxy.
library(shiny)
library(leaflet)
map_data <- data.frame(lat = c(36.05, 36.25), lon = c(-132.13, -132.33))
ui <- fluidPage(
titlePanel("Map"),
mainPanel(tags$style(type = "text/css", "#map {height: calc(100vh - 80px) !important;}"),
leafletOutput("map"))
)
server <- function(input, output, session) {
All_NMEA <- shiny::reactivePoll(
intervalMillis = 5000,
session = session,
checkFunc = Sys.time,
valueFunc = function() {
NMEA_out <- data.frame(lat = runif(1, 0, 20),
long = runif(1, 0, 20))
leafletProxy("map", session = session) %>%
addCircleMarkers(
lng = NMEA_out$long,
lat = NMEA_out$lat,
radius = 1,
fillOpacity = 1, color = "red"
)
return(NMEA_out)
}
)
observe({All_NMEA()})
output$map <- renderLeaflet({
map <- leaflet(map_data) %>%
addProviderTiles(providers$Esri.OceanBasemap, group = "ocean basemap (default)") %>%
addTiles(group = "Basic") %>%
addLayersControl(
baseGroups = c("ocean basemap (default)", "Basic"),
options = layersControlOptions(collapsed = FALSE)) %>%
addEasyButton(
easyButton(id = "buttonid",
icon = "fa-crosshairs", title = "Locate Vessel",
onClick = JS("function(btn, map) {
Shiny.onInputChange('my_easy_button', 'clicked', {priority: 'event'});
}")
))
})
observeEvent(input$my_easy_button, {
print("easyButton is clicked")
allnmea <- req(All_NMEA())
leafletProxy("map", session = session) %>%
flyTo(lng = allnmea$long, lat = allnmea$lat, zoom = 5)
})
}
shinyApp(ui = ui, server = server)
The code below is meant to reproduce that which is found in this example with the exception of adding an additional parameter for "speed". However, my map-datatable link has broken - Can anyone help me spot the bug? The original code updates the table based on the bounds of the map, while in my code changing the map zoom has no effect on my table. I'm also not able to get the "speed" filter to work on the table and map, which is a functionality I am looking for. Any pointers would be appreciated.
library(shiny)
library(magrittr)
library(leaflet)
library(DT)
ships <-
read.csv(
"https://raw.githubusercontent.com/Appsilon/crossfilter-demo/master/app/ships.csv"
)
ui <- shinyUI(fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(width = 3,
numericInput(
"speed", label = h5("Ship's Speed"), value = 100
)),
mainPanel(tabsetPanel(
type = "tabs",
tabPanel(
"Leaflet",
leafletOutput("leafletmap", width = "350px"),
dataTableOutput("tbl")
)
))
)
))
server <- shinyServer(function(input, output) {
in_bounding_box <- function(data, lat, long, bounds, speed) {
data %>%
dplyr::filter(
lat > bounds$south &
lat < bounds$north &
long < bounds$east & long > bounds$west & ship_speed < input$speed
)
}
output$leafletmap <- renderLeaflet({
leaflet() %>%
addProviderTiles("Esri.WorldImagery", group = "ESRI World Imagery") %>%
addCircleMarkers(
data = ships,
~ long ,
~ lat,
popup = ~ speed,
radius = 5 ,
stroke = FALSE,
fillOpacity = 0.8,
popupOptions = popupOptions(closeButton = FALSE)
)
})
data_map <- reactive({
if (is.null(input$map_bounds)) {
ships
} else {
bounds <- input$map_bounds
in_bounding_box(ships, lat, long, bounds, speed)
}
})
output$tbl <- DT::renderDataTable({
DT::datatable(
data_map(),
extensions = "Scroller",
style = "bootstrap",
class = "compact",
width = "100%",
options = list(
deferRender = TRUE,
scrollY = 300,
scroller = TRUE,
dom = 'tp'
)
)
})
})
shinyApp(ui = ui, server = server)
Two small changes:
In the example you linked, input$map_bounds works, because the leaflet output object is called map. However, you renamed it to leafletmap, so we should refer to input$leafletmap_bounds.
in the dplyr statement, we should refer to speed, not ship_speed.
Working code is given below, hope this helps!
library(shiny)
library(magrittr)
library(leaflet)
library(DT)
ships <-
read.csv(
"https://raw.githubusercontent.com/Appsilon/crossfilter-demo/master/app/ships.csv"
)
ui <- shinyUI(fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(width = 3,
numericInput(
"speed", label = h5("Ship's Speed"), value = 100
)),
mainPanel(tabsetPanel(
type = "tabs",
tabPanel(
"Leaflet",
leafletOutput("leafletmap", width = "350px"),
dataTableOutput("tbl")
)
))
)
))
server <- shinyServer(function(input, output) {
in_bounding_box <- function(data, lat, long, bounds, speed) {
data %>%
dplyr::filter(
lat > bounds$south &
lat < bounds$north &
long < bounds$east & long > bounds$west & speed < input$speed
)
}
output$leafletmap <- renderLeaflet({
leaflet() %>%
addProviderTiles("Esri.WorldImagery", group = "ESRI World Imagery") %>%
addCircleMarkers(
data = ships,
~ long ,
~ lat,
popup = ~ speed,
radius = 5 ,
stroke = FALSE,
fillOpacity = 0.8,
popupOptions = popupOptions(closeButton = FALSE)
)
})
data_map <- reactive({
if (is.null(input$leafletmap_bounds)) {
ships
} else {
bounds <- input$leafletmap_bounds
in_bounding_box(ships, lat, long, bounds, speed)
}
})
output$tbl <- DT::renderDataTable({
DT::datatable(
data_map(),
extensions = "Scroller",
style = "bootstrap",
class = "compact",
width = "100%",
options = list(
deferRender = TRUE,
scrollY = 300,
scroller = TRUE,
dom = 'tp'
)
)
})
})
shinyApp(ui = ui, server = server)
The leaflet map you are rendering is called leafletmap. So rather than referring to map_bounds try changing it to leafletmap_bounds:
data_map <- reactive({
if (is.null(input$leafletmap_bounds)) {
ships
} else {
bounds <- input$leafletmap_bounds
in_bounding_box(ships, lat, long, bounds, speed)
}
})
Also in the filter, change ship_speed to speed. Should hopefully work.
I want to create a modal window when I click an icon in a leaflet map in shiny. Is this doable? I tried the code below, but the bsModal is not doing anything.
library(shiny)
library(leaflet)
library(shinyBS)
points <- data.frame(cbind(latitude = rnorm(40) * 2 + 13, longitude =
rnorm(40) + 48))
ui <- fluidPage(
leafletOutput("mymap"),
bsModal("modalExample", "This will open a modal", "assign_task", size =
"large",
HTML(""))
)
server <- function(input, output, session) {
output$mymap <- renderLeaflet({
leaflet(options = leafletOptions(maxZoom = 18)) %>% addTiles() %>%
addMarkers(lat = ~ latitude, lng = ~ longitude,
data = points,
popup=~ sprintf(
'<button type="button" id="assign_task">Open Modal </button>'
))
})
}
shinyApp(ui, server)
I'll post two possible solutions. The first one is the solution that I think would suit your needs best, the second one more cosely matches your current code. Hope this helps!
Solution 1:
library(shiny)
library(leaflet)
points <- data.frame(cbind(id=seq(1,40),latitude = rnorm(40) * 2 + 13, longitude =
rnorm(40) + 48))
ui <- fluidPage(
leafletOutput("mymap"),
actionButton("action1","Show modal")
)
server <- function(input, output, session) {
observeEvent(input$mymap_marker_click, {
id = input$mymap_marker_click$id
showModal(modalDialog(
title = "You selected a marker!",
paste0("ID: ", id, ", lat: ", round(points$latitude[id==id],2),", lon: ", round(points$longitude[id==id],2))
))
})
output$mymap <- renderLeaflet({
leaflet(options = leafletOptions(maxZoom = 18)) %>% addTiles() %>%
addMarkers(layerId = ~ id,lat = ~ latitude, lng = ~ longitude,
data = points
)
})
}
shinyApp(ui, server)
Solution 2:
library(shiny)
library(leaflet)
library(shinyBS)
points <- data.frame(cbind(latitude = rnorm(40) * 2 + 13, longitude =
rnorm(40) + 48))
ui <- fluidPage(
leafletOutput("mymap"),
actionButton("action1","Show modal")
)
server <- function(input, output, session) {
observeEvent(input$button_click, {
showModal(modalDialog(
title = "Important message",
"This is an important message!"
))
})
output$mymap <- renderLeaflet({
leaflet(options = leafletOptions(maxZoom = 18)) %>% addTiles() %>%
addMarkers(lat = ~ latitude, lng = ~ longitude,
data = points,
popup= ~paste("<b>", latitude, longitude, "</b></br>", actionButton("showmodal", "Show modal", onclick = 'Shiny.onInputChange(\"button_click\", Math.random())')))
})
}
shinyApp(ui, server)
I would like to display a conditional panel in my map when I click on a circle, and this conditional panel must disappear if I click outside a circle, but it does not appear and I don't know why.
I think it's about reactive values (one more time).
If any idea, please tell me.
Thank you very much, this is a reproducible example (thanks to SymbolixAU) :
ui :
library(shiny)
library(leaflet)
ui <- fluidPage(
leafletOutput("mymap",width="100%",height="750px"),
conditionalPanel(
condition = "output.COND == '2'",
fluidRow(
absolutePanel(id = "cond_panel",
class = "panel panel-default",
fixed = TRUE,
draggable = TRUE,
top = "auto",
left = 200,
right = "auto",
bottom = 0,
width = 400,
height = 400,
fluidRow(
) # e. of fluidRow(
) # # e. of absolutePanel
) # e. of fluidRow
) # e. of conditionalPanel
) # e. of fluidPage
and the server :
server <- function(input, output){
rv <- reactiveValues()
rv$myDf <- NULL
rv$cond <- NULL
cities <- read.csv(textConnection("
City,Lat,Long,Pop
Boston,42.3601,-71.0589,645966
Hartford,41.7627,-72.6743,125017
New York City,40.7127,-74.0059,8406000
Philadelphia,39.9500,-75.1667,1553000
Pittsburgh,40.4397,-79.9764,305841
Providence,41.8236,-71.4222,177994
"))
cities$id <- 1:nrow(cities)
output$mymap <- renderLeaflet({
leaflet(cities) %>% addTiles() %>%
addCircles(lng = ~Long, lat = ~Lat, weight = 1,
radius = ~sqrt(Pop) * 30, popup = ~City, layerId = ~id)
})
observeEvent(input$mymap_click, {
print("map clicked")
rv$cond <- "1"
print(paste("Value rv$cond = ", rv$cond))
output$COND <- reactive({rv$cond})
leafletProxy("mymap")
}) # e. of observeEvent
observeEvent(input$mymap_shape_click, {
print("shape clicked")
rv$cond <- "2"
print(paste("Value rv$cond = ", rv$cond))
output$COND <- reactive({rv$cond})
leafletProxy("mymap")
}) # e. of observeEvent
} # e. of server
I'm going to propose a slightly different approach that uses library(shinyjs) to use javascript to control whether the panel is hidden or not.
In this example I've created a hidden div element (i.e., the panel will start hidden when the app opens). Then the 'div' is shown when the circle is clicked, and hidden again when the map is clicked.
This answer is inspired by #Daattali's answer here (he's the author of library(shinyjs).
library(shiny)
library(leaflet)
library(shinyjs)
ui <- fluidPage(
useShinyjs(), ## Call to use shinyJS
leafletOutput("mymap",width="100%",height="750px"),
#conditionalPanel(
#condition = "output.COND === '2'",
hidden(
div(id = "conditionalPanel",
fluidRow(
absolutePanel(id = "cond_panel",
class = "panel panel-default",
fixed = TRUE,
draggable = TRUE,
top = "auto",
left = 200,
right = "auto",
bottom = 0,
width = 400,
height = 400,
fluidRow(
) # e. of fluidRow(
) # # e. of absolutePanel
) # e. of fluidRow
)
)
# ) # e. of conditionalPanel
) # e. of fluidPage
server <- function(input, output){
rv <- reactiveValues()
rv$myDf <- NULL
rv$cond <- NULL
cities <- read.csv(textConnection("
City,Lat,Long,Pop
Boston,42.3601,-71.0589,645966
Hartford,41.7627,-72.6743,125017
New York City,40.7127,-74.0059,8406000
Philadelphia,39.9500,-75.1667,1553000
Pittsburgh,40.4397,-79.9764,305841
Providence,41.8236,-71.4222,177994
"))
cities$id <- 1:nrow(cities)
output$mymap <- renderLeaflet({
leaflet(cities) %>% addTiles() %>%
addCircles(lng = ~Long, lat = ~Lat, weight = 1,
radius = ~sqrt(Pop) * 30, popup = ~City, layerId = ~id)
})
observeEvent(input$mymap_click, {
shinyjs::hide(id = "conditionalPanel")
print("map clicked")
rv$cond <- "1"
print(paste("Value rv$cond = ", rv$cond))
output$COND <- reactive({rv$cond})
leafletProxy("mymap")
}) # e. of observeEvent
observeEvent(input$mymap_shape_click, {
shinyjs::show(id = "conditionalPanel")
print("shape clicked")
rv$cond <- "2"
print(paste("Value rv$cond = ", rv$cond))
output$COND <- reactive({rv$cond})
leafletProxy("mymap")
}) # e. of observeEvent
} # e. of server
shinyApp(ui, server)
Just use the absolutePanel inside a conditionalPanel whose condition you reset based on user input.