I have a leaflet map which renders different polygons based off of a picker input. For some reason, leafletProxy will only render if the picker is changed, even though the observe({}) event it's wrapped inside will trigger when the app loads. Is there some way I can fix this?
The map is also slow to update when the picker is changed - is there a way for me to speed this up, just as an aside?
I've included a very simplified version of the code I'm using here. Unfortunately, the data is sensitive so I cannot share it.
library(shiny)
library(shinythemes)
library(shinydashboard)
library(shinydashboardPlus)
library(shinyjs)
library(shinyWidgets)
library(leaflet)
library(tidyverse)
library(feather)
library(sf)
library(viridis)
ui <- fluidPage(
useShinyjs(),
theme = shinytheme("cerulean"),
useShinydashboard(),
tabsetPanel(
tabPanel(
"Tab 1",
pickerInput(
width = "100%",
inputId = "indicator_input",
label = "Select an indicator on the map:",
choices = list(
"Choice 1", "Choice 2", "Choice 3"
),
options = list(`style` = "btn-primary")
),
leafletOutput("example1")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output, session) {
#Label object
example_labels <- reactive({
labels <- paste0(
"<strong>", "Polygon name: ", "</strong>", shapedata$polygon_name,"<br>",
"<strong>", "Indicator 1: ", "</strong>",
shapedata$indicator_1, "%", "<br>",
"<strong>", "Indicator 2: ", "</strong>",
shapedata$indicator_2, "%", "<br>"
) %>% lapply(htmltools::HTML)
return(cald_labels)
})
# Start initial leaflet render
output$example1 <- renderLeaflet({
glimpse("Loaded")
leaflet(shapedata) %>%
addProviderTiles(leaflet::providers$Esri.WorldImagery) %>%
setView(lng = 133.7751, lat = -25.2744, zoom = 4)
}) # end leaflet output
observe({
glimpse("Observe triggered")
map_pal <- colorBin(palette = mako(10),
domain = shapedata[[input$indicator_input]],
bins=10,
na.color = "grey",
reverse = TRUE)
leafletProxy(
"example1",
data = shapedata
) %>%
addPolygons(
layerId = shapedata$polygon_name,
fillColor = ~map_pal(shapedata[[input$indicator_input]]),
weight = 2,
color = "white",
fillOpacity = 0.9,
label = example_labels(),
labelOptions = labelOptions(#labels
style = list("font-weight" = "normal",
padding = "3px 3px"),
textsize = "10px",
direction = "auto")
) %>%
clearControls() %>%
addLegend(pal = map_pal,
title = "Decile",
opacity = 0.9,
values = ~shapedata[[input$indicator_input]],
labels = c(min(input$indicator_input), max(input$indicator_input)),
position = "topright")
})
}
# Run the application
shinyApp(ui = ui, server = server)
The issue was caused by the same problem, and solved by the same solution, as here.
Related
I am trying to make a shiny app where I can select a location on the map and display a gauge chart for each corresponding location.
I have been able to make the app reactive but the googlevis gauge display appears on the browser instead of in the app. In the app I get the error $ operator is invalid for atomic vectors. I tried converting the data into a dataframe but I am still getting this error.
the code is as follows
library(shiny)
library(leaflet)
library(shinydashboard)
library(dplyr)
library(googleVis)
#Making the Dataframe
locations<-c("A","B","C")
x<-c(36.05617,36.05626,36.05634)
y<-c(-2.1007,-2.05553,-2.01035)
yield<-c(5.86,3.06,1.07)
df<-data.frame(locations,x,y,yield)
################## Defining UI for application ############################
ui <- shinyUI(dashboardPage(title = "Yield Lookup",
dashboardHeader(title = "Crop Yield (Tonnes per Hectare)",titleWidth = 350),
dashboardSidebar(
sidebarMenu(
menuItem("Map Dashboard", tabName = "datavis", icon = icon("map", verify_fa = FALSE)),
menuItem("Select by Location Name", icon = icon("leaf"),
selectizeInput("locations", "Click on Location", choices = levels(factor(df$locations)))
)
)
),
dashboardBody(
tabItems(
tabItem(tabName = "datavis",
h4("Map and Plot"),
fluidRow(box(width= 8, leafletOutput("map", height = 800)),
box("Gauge for crop yield by area",width = 4, htmlOutput("myplot")))
)
)
)
)
)
################## Defining Server for application ############################
server<- shinyServer(function(input,output, session){
## Sub data
lo<-reactive({
})
locat_data <- reactive({
df[df$locations %in% input$locations,]
})
output[["myplot"]] <- renderGvis({
newdf<-locat_data()%>%select(locations, yield)
newdf<-as.data.frame(newdf)
Gauge <- gvisGauge(as.data.frame(newdf),
options=list(min=0, max=6, greenFrom=4,
greenTo=6, yellowFrom=2, yellowTo=4,
redFrom=0, redTo=2, width=400, height=300))
plot(Gauge)
})
output$map <- renderLeaflet({
leaflet(df) %>%
addTiles() %>%
addCircleMarkers(lng = ~x, lat = ~y, layerId = ~locations, color = "blue", radius = 3) %>%
addCircles(lng = ~x, lat = ~y, weight = 1,
radius = 1, label = ~locations
)
})
observeEvent(input$locations,{
updateSelectInput(session, "locations", "Click on Locations",
choices = levels(factor(df$locations)),
selected = c(input$locations))
})
observeEvent(input$map_marker_click, {
click <- input$map_marker_click
location <- df[which(df$y == click$lat & df$x == click$lng), ]$locations
updateSelectInput(session, "locations", "Click on Location",
choices = levels(factor(df$locations)),
selected = c(input$locations, location))
})
})
shinyApp(ui=ui, server = server)
I am not sure where I am going wrong. Please help.
You were very close.
Just drop the plot() and leave Gauge in the server section. (Alternatively drop the Gauge <- and Gauge on the next line and just leave gvisGauge())
library(shiny)
library(leaflet)
library(shinydashboard)
library(dplyr)
library(googleVis)
#Making the Dataframe
locations<-c("A","B","C")
x<-c(36.05617,36.05626,36.05634)
y<-c(-2.1007,-2.05553,-2.01035)
yield<-c(5.86,3.06,1.07)
df<-data.frame(locations,x,y,yield)
################## Defining UI for application ############################
ui <- shinyUI(dashboardPage(title = "Yield Lookup",
dashboardHeader(title = "Crop Yield (Tonnes per Hectare)",titleWidth = 350),
dashboardSidebar(
sidebarMenu(
menuItem("Map Dashboard", tabName = "datavis", icon = icon("map", verify_fa = FALSE)),
menuItem("Select by Location Name", icon = icon("leaf"),
selectizeInput("locations", "Click on Location", choices = levels(factor(df$locations)))
)
)
),
dashboardBody(
tabItems(
tabItem(tabName = "datavis",
h4("Map and Plot"),
fluidRow(box(width= 8, leafletOutput("map", height = 800)),
box("Gauge for crop yield by area",width = 4, htmlOutput("myplot")))
)
)
)
)
)
################## Defining Server for application ############################
server<- shinyServer(function(input,output, session){
## Sub data
lo<-reactive({
})
locat_data <- reactive({
df[df$locations %in% input$locations,]
})
output[["myplot"]] <- renderGvis({
newdf<-locat_data()%>%select(locations, yield)
newdf<-as.data.frame(newdf)
Gauge <- gvisGauge(as.data.frame(newdf),
options=list(min=0, max=6, greenFrom=4,
greenTo=6, yellowFrom=2, yellowTo=4,
redFrom=0, redTo=2, width=400, height=300))
Gauge
})
output$map <- renderLeaflet({
leaflet(df) %>%
addTiles() %>%
addCircleMarkers(lng = ~x, lat = ~y, layerId = ~locations, color = "blue", radius = 3) %>%
addCircles(lng = ~x, lat = ~y, weight = 1,
radius = 1, label = ~locations
)
})
observeEvent(input$locations,{
updateSelectInput(session, "locations", "Click on Locations",
choices = levels(factor(df$locations)),
selected = c(input$locations))
})
observeEvent(input$map_marker_click, {
click <- input$map_marker_click
location <- df[which(df$y == click$lat & df$x == click$lng), ]$locations
updateSelectInput(session, "locations", "Click on Location",
choices = levels(factor(df$locations)),
selected = c(input$locations, location))
})
})
shinyApp(ui=ui, server = server)
In continuation to my previous post where this was applied on map, I am trying to filter a table in R Shiny using Dropdown input: How to build dynamic Leaflet Map in RShiny?
Code:
library(shiny)
library(shinydashboard)
library(tidyverse)
library(leaflet)
library(readxl)
library(RCurl)
library(DT)
URL <- "https://www.mohfw.gov.in/pdf/PMJAYPRIVATEHOSPITALSCONSOLIDATED.xlsx"
download.file(URL, destfile = "../../timesnow_PMJAYPRIVATEHOSPITALSCONSOLIDATED.xlsx",method = "curl")
# Data
ind_vaccination_center <- readxl::read_xlsx(path = "../../timesnow_PMJAYPRIVATEHOSPITALSCONSOLIDATED.xlsx",
sheet = 1)
ind_vaccination_leaflet <- ind_vaccination_center %>%
mutate(label_display = paste(
"<h2>", ind_vaccination_center$`Name of the Vaccination Site*`, "</h2>",
"<h4>",ind_vaccination_center$`District*`,",", ind_vaccination_center$`State*`, "</h4>",
"<p>", "Address: ", ind_vaccination_center$Address,",", ind_vaccination_center$`PinCode*`, "</p>",
"<p>", "Mobile: ", ind_vaccination_center$`Mobile Number`, "</p>",
"<p>", "Contact Person: ", ind_vaccination_center$`Contact Person`, "</p>"
)
)
# Define UI for application
ui <- fluidPage(
# Application title
titlePanel("Covid19 Vaccination Centers in India"),
# Sidebar with a Dropdown
sidebarLayout(
sidebarPanel(
selectInput(inputId = "state_selection",
label = "Select State",
choices = ind_vaccination_center$`State*`),
h3("List of Vaccination Centers is plotted on Map & also listed in searchable table."),
"source of list:",
a("https://www.timesnownews.com/india/article/covid-19-vaccination-in-uttar-pradesh-check-complete-list-of-govt-and-private-hospitals-for-jab/726412"),
br(),
br(),
a("https://www.oneindia.com/india/full-list-of-private-hospitals-where-the-covid-19-vaccine-will-be-administered-3223706.html"),
br(),
br(),
"P.S - There might be more center's added to this list, kindly recheck from other sources as well like:",
br(),
a("https://www.cowin.gov.in/home")
),
# Show Map & table
mainPanel(
tabsetPanel(type = "tabs",
tabPanel("Map", leafletOutput("map",height = 800, width = "100%")),
tabPanel("Data Table", tableOutput("mytable"))
)
)
)
)
# Define server logic
server <- function(input, output) {
# solution from: https://stackoverflow.com/questions/66732758/how-to-build-dynamic-leaflet-map-in-rshiny/66733086#66733086
output$map <- renderLeaflet({
req(input$state_selection)
data <- ind_vaccination_leaflet %>%
filter(`State*` == input$state_selection)
# Creating map object & adding layers
leaflet(data) %>%
setView(lat = 26.64510, lng = 80.17012, zoom = 4) %>%
addTiles(group = "OSM") %>%
addProviderTiles(providers$CartoDB.DarkMatter, group = "Dark") %>%
addProviderTiles(providers$CartoDB.Positron, group = "Light") %>%
addProviderTiles("Stamen.Terrain", group = "Terrain") %>%
addProviderTiles("Esri.WorldImagery", group = "WorldImagery") %>%
addLayersControl(baseGroups = c("OSM","WorldImagery","Dark","Light","Terrain")) %>%
addCircleMarkers(
lng = ~`Longitude*`,
lat = ~`Latitude*`,
label = lapply(data$label_display, htmltools::HTML),
color = "midnightblue",
weight = 1,
radius = 8
)%>%
addMiniMap(tiles = providers$OpenStreetMap, width = 120, height=80)
})
output$mytable = DT::renderDataTable({
req(input$state_selection)
data <- ind_vaccination_leaflet %>%
filter(`State*` == input$state_selection)
data
})
}
# Run the application
shinyApp(ui = ui, server = server)
You need to do two changes.
tabPanel("Data Table", dataTableOutput("mytable"))
and
output$mytable = DT::renderDataTable({
req(input$state_selection)
data <- ind_vaccination_leaflet %>%
filter(`State*` == input$state_selection)
datatable(data)
})
I would like to render a map based on selectInput(). I have two selectInput()s: first one product_type and second one product_name. In the second one selectInput() the drop down options should be display only relevant to first selectInput(). Based on these drill down inputs map should change dynamically.
Here is the code:
ui <- shinyUI(dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
fluidPage(
box("",
leafletOutput("abc", width = '100%', height = 300),
height = 350, width = 12),
box("",
selectInput('vtype', label = 'Prod Type',choices = brand$prod_type),
selectInput('vname', label = 'Prod Name',choices = brand$prod_name),
width = 4),
valueBoxOutput("gr", width = 8)
)
)
))
server <- shinyServer(function(input,output,session){
a <- ship %>% select(prod_name,prod_type,LON,LAT) %>% filter(prod_type == input$vtype)
output$gr <- renderValueBox({
box(table(a))
})
output$abc <- renderLeaflet({
leaflet() %>% addProviderTiles(providers$OpenTopoMap )
%>% setView(lat = a$LAT ,lng = A$LON, zoom = 4)
})
})
shinyApp(ui,server)
The dynamically changed points in the map should be marked up. I tried but could not able to build the code.
Any help on this code would be graceful for me.
I hope my example helps. I invented a data.frame 'ship' and made everything dependent on it. That means it is used for your variable 'brand' as well as 'ship'.
I'm not sure how you envisioned the value box, so I put category and products in it.
library(shiny)
library(shinydashboard)
library(dplyr)
library(leaflet)
ship <- data.frame(
product_type = c("food","food","tool","tool","tool","accessories","accessories","lighting","lighting","lighting"),
product_name=c("eggs", "bread","clamp","hammer","screw driver", "watch" ,"sun glases","LED","bulb","briquette"),
LON=c(-61.783,2.632,47.395,20.068,44.563,17.544,-170.730,-65.167,136.189,50.562),
LAT=c(17.078 ,28.163 ,40.430 ,41.143 ,40.534 ,-12.296 ,-14.318 ,-35.377 ,-24.973 ,26.019),
stringsAsFactors = F)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(collapsed = TRUE, disable = FALSE),
dashboardBody(
# fluidPage(
box(
leafletOutput("abc", width = '100%', height = 300),
height = 350,
width = 12),
box(
selectInput('vtype', label = 'Prod Type', choices = c("< select product type>"="", ship$product_type)),
selectInput('vname', label = 'Prod Name', choices = c("< select item>"="", ship$product_name)),
width = 4),
valueBoxOutput("gr", width = 8)
#)
)
)
server <- shinyServer(function(input,output,session){
a <- reactive({
ship %>%
select(product_name, product_type, LON, LAT) %>%
filter(product_type %in% input$vtype)
})
output$gr <- renderValueBox({
valueBox( input$vtype, paste(a()$product_name, collapse=' - ') )
})
observe({
updateSelectInput(session,
inputId='vname',
choices = c("< select item>"="", a()$product_name ))
})
output$abc <- renderLeaflet({
leaflet() %>%
addProviderTiles(providers$OpenTopoMap ) %>%
setView(lng=0, lat=0, zoom = 1)
})
observe({
selection <- a() %>% filter(product_name %in% input$vname)
leafletProxy("abc") %>%
flyTo(lat = selection$LAT,
lng = selection$LON,
zoom = 4)
})
})
shinyApp(ui,server)
Please provide example data next time.
I have created this shiny app:
https://garrettrsmith.shinyapps.io/RIB_shinyapp/
When I run it locally I can switch between the years in the SelectInput drop-down menu no problem. When I deploy the app onto my shinyapps.io account and switch between years in the SelectInput drop-down menu the map goes gray and I get the "Disconnected from the server. Reload" icon. I also cannot seem to get the popups to work.
Here is my code:
site <- c("Browns Canyon", "Hancock", "Monarch Crest")
lat <- c("38.76210", "38.70581", "38.49185")
long <- c("-105.9776", "-106.3405", "-106.3171")
agency <- c("BLM", "USFS", "BLM")
Total2016 <- ("353", "1112", "9875")
Total2017 <- c("0", "138", "7435")
Total2018 <- c("201", "145", "16448")
Total2019 <- c("153", "0", "9655")
alluse <- data.frame(site, lat, long, Total2016, Total2017,
Total2018, Total2019)
ui <- navbarPage(
"Chaffee County Trail Counts", id = "nav",
tabPanel("Trail Count Map By Year", div(class = "outer",
tags$head(
includeCSS("www/style.css"),
includeScript("www/gomap.js")),
leafletOutput("UsageMap", width = "100%", height = "100%"),
absolutePanel(id = "controls", class = "panel panel-default", fixed = TRUE,
draggable = TRUE, top = 70, left = "auto", right = 20, bottom = "auto",
width = 330, height = "auto",
h2("Trailhead Explorer"),
selectInput("year", label = h4("Year:"),
choices = c("2016" = "Total2016",
"2017" = "Total2017",
"2018" = "Total2018",
"2019" = "Total2019"),
selected = "", width = "90%", multiple = FALSE),
tags$div(id="cite",
'Data provided by USFS and BLM and compiled for
Chaffee County Recreation in Balance')
))),
tabPanel("Trail Count Database",
DT::dataTableOutput("trailheadtable")
))
server <- function(input, output, session){
output$UsageMap <- renderLeaflet({
leaflet() %>%
addProviderTiles("Esri.WorldTopoMap") %>%
setView(lng = -106.183908, lat = 38.766663, zoom = 9)
})
observe({
yearcolorBy <- input$year
colorData <- alluse[[yearcolorBy]]
radius <- sqrt(alluse[[yearcolorBy]]) * 30
pal <- colorBin("viridis", colorData, 10, pretty = FALSE)
leafletProxy("UsageMap", data = alluse) %>%
clearShapes() %>%
addCircles(~long, ~lat, radius = radius, layerId =~ site,
stroke = FALSE, fillOpacity = 0.4, fillColor = pal(colorData)) %>%
addLegend("bottomleft", pal = pal, values = colorData, title = yearcolorBy, layerId = "colorLegend")
})
showTrailheadPopup <- function(site, lat, long) {
selectedSite <- alluse[alluse$site == site,]
content <- as.character(tagList(
tags$h4("Trailhead:", as.character(selectedSite$site)),
tags$h3("Agency:", as.character(selectedSite$agency)),
tags$br(),
sprintf("Total 2016: %s", as.numeric(selectedSite$Total2016)), tags$br(),
sprintf("Total 2017: %s", as.numeric(selectedSite$Total2017)), tags$br(),
sprintf("Total 2018: %s", as.numeric(selectedSite$Total2018)), tags$br(),
sprintf("Total 2019: %s", as.numeric(selectedSite$Total2019))
))
leafletProxy("UsageMap") %>% addPopups(lat, long, content, layerId = site)
}
observe({
leafletProxy("UsageMap") %>% clearPopups()
event <- input$map_shape_click
if (is.null(event))
return()
isolate({
showTrailheadPopup(event$id, event$lat, event$long)
})
})
output$trailheadtable <- DT::renderDataTable({
alluse %>%
filter(is.null(input$site)) %>%
mutate(Action = paste('<a class="go-map" href="" data-lat="', lat,
'" data-long="', long, '" data-trailhead="', site, '"><i class="fa fa-crosshairs"></i></a>',
sep=""))
action <- DT::dataTableAjax(session, alluse, outputId = "trailheadtable")
DT::datatable(alluse, options = list(ajax = list(url = action)), escape = FALSE)
})
}
I am also having an issue with the popups not working.
I am including my warnings from the shiny log below:
2019-12-14T12:57:55.646156+00:00 shinyapps[1598221]: Warning: Error in : 'is_weakref' is not an exported object from 'namespace:rlang'
2019-12-14T12:57:55.651640+00:00 shinyapps[1598221]: [No stack trace available]
2019-12-14T12:57:55.651907+00:00 shinyapps[1598221]: Error : 'is_weakref' is not an exported object from 'namespace:rlang'
Thank you for any help you can provide.
Using the example below, I am trying to figure out a way to add functionality to my shiny app such that the following works:
Click on a point on the map
This changes the plot according to station AND
Inputs the corresponding station into the "Click on Station" sidebar
Basically I'd like to be able either click on the map for a station OR input the station manually with a keyboard.
Is this possible with leaflet? I've seen references to using plotly which may be ultimate solution but I'd love to leaflet if possible in no small part because I have already done a lot of work with leaflet. This is similar to thisquestion though there is working example here:
library(shiny)
library(leaflet)
library(shinydashboard)
library(ggplot2)
library(dplyr)
data("quakes")
shinyApp(
ui = dashboardPage(title = "Station Lookup",
dashboardHeader(title = "Test"),
dashboardSidebar(
sidebarMenu(
menuItem("Data Dashboard", tabName = "datavis", icon = icon("dashboard")),
menuItem("Select by station number", icon = icon("bar-chart-o"),
selectizeInput("stations", "Click on Station", choices = levels(factor(quakes$stations)), selected = 10, multiple = TRUE)
)
)
),
dashboardBody(
tabItems(
tabItem(tabName = "datavis",
h4("Map and Plot"),
fluidRow(box(width= 4, leafletOutput("map")),
box(width = 8, plotOutput("plot")))
)
)
)
),
server = function(input, output) {
## Sub data
quakes_sub <- reactive({
quakes[quakes$stations %in% input$stations,]
})
output$plot <- renderPlot({
ggplot(quakes_sub(), aes(x = depth, y = mag))+
geom_point()
})
output$map <- renderLeaflet({
leaflet(quakes) %>%
addTiles() %>%
addCircleMarkers(lng = ~long, lat = ~lat, layerId = ~stations, color = "blue", radius = 3) %>%
addCircles(lng = ~long, lat = ~lat, weight = 1,
radius = 1, label = ~stations,
popup = ~paste(stations, "<br>",
depth, "<br>",
mag)
)
})
}
)
You can use input$map_marker_click and updateSelectInput():
Edit: Added functionality that stations can be deleted from selectInput() as suggested by OP in the comments.
(Dont forget to add session to your sever function).
observeEvent(input$stations,{
updateSelectInput(session, "stations", "Click on Station",
choices = levels(factor(quakes$stations)),
selected = c(input$stations))
})
observeEvent(input$map_marker_click, {
click <- input$map_marker_click
station <- quakes[which(quakes$lat == click$lat & quakes$long == click$lng), ]$stations
updateSelectInput(session, "stations", "Click on Station",
choices = levels(factor(quakes$stations)),
selected = c(input$stations, station))
})
However, this functionality is partly overwritten by the popup event(?). As i see it there is an inner blue circle (darker blue) that if clicked produces the popup. However, the input$map_marker_click only works if you click the outer (light blue) circle. I would report it as a bug,...