Performance problems in R's Shiny on huge (?) dataset - r

I have a dataset of ~10.000 address pairs (origin, destination) which consists of two sources - a database and a CSV-file. I am visualizing those pairs of addresses by two different marker types and I visualize the connections between those pairs with a line. It's possible to toggle the visibility of origins, destinations, and connections. It's also possible to draw a polygon on the map to frame markers and then visualize the corresponding markers and connections (you can choose if the polygon should frame origins, destinations or both). And it's possible to toggle the datasource (CSV or database) and choose data by date.
All of this works quite well, I just wanted to make clear where and that I need to use reactive values. But the performance is way to slow. It takes a lot of time to load this application when running it with RStudio and it could not be loaded on Shiny Server because the connection breaks down. I'm don't use the Pro version of Shiny Server where the timeout is not settable out of the box.
I tried to speed up the application by using the leafletProxy as often as possible.
df.data.db <- getDataFromDb() #external function
df.data.csv <- getDataFromCsv() #external function
df.data.total <- rbind(df.data.db,df.data.csv)
ui <- bootstrapPage(
tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
tags$head(tags$style(HTML('.dest {color: rgba(11, 221, 25, 0.7);}'))),
tags$head(tags$style(HTML('.orig {color: rgba(255,100,20);}'))),
leafletOutput("map", height = "85%"),
fluidRow(
column(
3,
p(tags$b("Datasets")),
materialSwitch(inputId = "useDatabase", label = "database",value=TRUE),
materialSwitch(inputId = "useExcel", label = "excel",value=TRUE)),
column(
3,
p(),
dateRangeInput('dateRange',
label = 'Date range input: yyyy-mm-dd',
start = "2016-12-26",
end = Sys.Date(),
min = "2016-12-26",
max = Sys.Date()),
p(),
textOutput("number_of_data")
),
column(3,
p(),
actionButton("remove", "Remove shapes")),
column(3,
p(tags$b("Connections")),
textOutput("number_of_connections"))
)
)
server <- function(input, output, session) {
reactiveData <- reactiveValues(
markers = data.frame(lat = numeric(), lon = numeric()),
allPoly = data.frame(lat = numeric(), lon = numeric()),#should polygon frame all markers
origPoly = data.frame(lat = numeric(), lon = numeric()),#only origin markers
destPoly = data.frame(lat = numeric(), lon = numeric()),#only destination markers
shapeState = "poly_all",#what polygon type is drawn
connections=0
)
#used subset of data depending of the chosen date
mydata <- reactive({
base = base_data()
from <- input$dateRange[1]
to <- input$dateRange[2]
return(base[base$date>=from & base$date<=to,])
})
#choose data source (csv or db)
base_data <- reactive({
mydf = data.frame(orig_lat=numeric(),
orig_lon=numeric(),
dest_lat=numeric(),
dest_lon=numeric(),
date=as.Date(character()))
if(input$useExcel==TRUE && input$useDatabase==TRUE)
mydf = df.data.total
else if(input$useExcel==FALSE && input$useDatabase==TRUE)
mydf = df.data.db
else if(input$useExcel==TRUE && input$useDatabase==FALSE)
mydf = df.data.csv
reactiveData$connections <- nrow(mydf)
return(mydf)
})
#show / hide connections
observe({
leafletProxy("map",session = session) %>%
clearShapes() %>%
clearGroup("Connections")
conn.data <- mydata();
for(i in 1:nrow(conn.data)) {
row <- conn.data[i,]
leafletProxy("map",session = session) %>% addPolygons(lat=c(row$orig_lat,row$dest_lat),lng=c(row$orig_lon,row$dest_lon),group="Connections",weight=0.5)
}
})
#remove all customized stuff
observeEvent(input$remove,{
reactiveData$markers <- data.frame(lat = numeric(), lon = numeric())
reactiveData$allPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$origPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$destPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$shapeState <- "poly_all"
reactiveData$connections<-0
leafletProxy("map",session = session) %>%
clearShapes() %>%
clearGroup("polygon") %>%
clearGroup("polymarkers")%>%
clearGroup("polyconnections") %>%
showGroup("Origins") %>%
showGroup("Destinations") %>%
clearGroup("tempmarkers")
})
#my map
output$map <- renderLeaflet({
leaflet(data=mydata()) %>%
addTiles()%>%
setView("7.126501","48.609749", 10) %>%
addMarkers(
lng=~dest_lon,
lat=~dest_lat,
icon = uix.destMarker,
group = "Destinations",
layerId = "dest_layer",
clusterId = "dest_cluster",
clusterOptions = markerClusterOptions(
removeOutsideVisibleBounds = TRUE,
iconCreateFunction=js.destclusters
)) %>%
addMarkers(
lng=~orig_lon,
lat=~orig_lat,
icon = uix.origMarker,
group = "Origins",
layerId = "orig_layer",
clusterId = "orig_cluster",
clusterOptions = markerClusterOptions(
removeOutsideVisibleBounds = TRUE,
iconCreateFunction=js.origclusters
)) %>%
addLayersControl(overlayGroups = c("Origins","Destinations","Connections"))
})
#print markers for polygon on map
observeEvent(input$map_click,{
leafletProxy("map",session = session) %>%
hideGroup("Connections")
if(nrow(reactiveData$allPoly)>0){
reactiveData$markers <- data.frame(lat = numeric(), lon = numeric())
reactiveData$allPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$origPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$destPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$shapeState <- "poly_all"
reactiveData$connections<-0
leafletProxy("map",session = session) %>%
clearShapes() %>%
clearGroup("polygon") %>%
clearGroup("polymarkers")%>%
clearGroup("polyconnections") %>%
showGroup("Origins") %>%
showGroup("Destinations") %>%
clearGroup("tempmarkers")
}
if(nrow(reactiveData$origPoly)>0 && nrow(reactiveData$destPoly)>0){
showModal(modalDialog(
title = "Wrong workflow",
"Remove old shapes first!",
easyClose = TRUE
))
}
else{
click <- input$map_click
clat <- click$lat
clng <- click$lng
reactiveData$markers[nrow(reactiveData$markers) + 1, ] = c(clat, clng)
leafletProxy('map') %>%
addMarkers(lng = reactiveData$markers$lon,
lat = reactiveData$markers$lat,
group="polymarkers"
)
}
})
#change type of polygon by clicking on polygon. hiding connections by clicking on it
observeEvent(input$map_shape_click,{
click <- input$map_shape_click
if(click$group=="Connections"){
leafletProxy("map",session = session) %>%
hideGroup("Connections")
clat <- click$lat
clng <- click$lng
leafletProxy('map') %>%
addMarkers(lng = clng,
lat = clat)
reactiveData$markers[nrow(reactiveData$markers) + 1, ] = c(clat, clng)
}
else if(click$group =="polygon" && nrow(reactiveData$markers)==0){
tmp <- data.frame(lat = numeric(), lon = numeric())
if(reactiveData$shapeState=="poly_all") {
reactiveData$shapeState<-"poly_orig"
isolate(tmp<-reactiveData$allPoly)
reactiveData$origPoly <- rbind(reactiveData$origPoly,tmp)
reactiveData$allPoly<- data.frame(lat = numeric(), lon = numeric())
#reactiveData$destPoly <- rbind(reactiveData$destPoly,data.frame(lat = numeric(), lon = numeric()))
}
else if(reactiveData$shapeState=="poly_orig") {
reactiveData$shapeState<-"poly_dest"
isolate(tmp<-reactiveData$origPoly)
reactiveData$origPoly <- data.frame(lat = numeric(), lon = numeric())
#reactiveData$allPoly <- data.frame(lat = numeric(), lon = numeric())
reactiveData$destPoly <- rbind(reactiveData$destPoly,tmp)
}
else if(reactiveData$shapeState=="poly_dest") {
reactiveData$shapeState<-"poly_all"
isolate(tmp<-reactiveData$destPoly)
#reactiveData$origPoly <- rbind(reactiveData$origPoly,data.frame(lat = numeric(), lon = numeric()))
reactiveData$allPoly <- rbind(reactiveData$allPoly,tmp)
reactiveData$destPoly <- data.frame(lat = numeric(), lon = numeric())
}
createConnections()
leafletProxy('map') %>% # use the proxy to save computation
clearGroup("polygon") %>%
addPolygons(lat = tmp$lat, lng = tmp$lon, group="polygon",color = polyColor(),fillColor=polyColor())
}
else if(nrow(reactiveData$markers)>0){
showModal(modalDialog(
title = "Wrong workflow",
"It's too late to change the type of your selection. Please clear shapes and draw again!",
easyClose = TRUE
))
}
})
polyColor <- reactive({
if(reactiveData$shapeState=="poly_all") {
return("black")
}
else if(reactiveData$shapeState=="poly_orig") {
return("red")
}
else if(reactiveData$shapeState=="poly_dest") {
return("green")
}
})
createConnections <- reactive({
reactiveData$connections<-0
df.pois <- data.frame(lat=numeric(),lon=numeric())
data <- mydata()
allData <- data.frame(orig_lat=numeric(),
orig_lon=numeric(),
dest_lat=numeric(),
dest_lon=numeric(),
date=as.Date(character()))
if(nrow(reactiveData$allPoly)>0){
df.pois<-rbind(data.frame(lat=data$orig_lat, lon=data$orig_lon),
data.frame(lat=data$dest_lat, lon=data$dest_lon))
my_poly <- reactiveData$allPoly
pois <- SpatialPoints(df.pois)
poiPoly <- SpatialPolygons(list(Polygons(list(
Polygon(cbind(my_poly$lat, my_poly$lon))
), ID = "x11")))
coords<-as.data.frame(pois[poiPoly])
if(nrow(coords)>0){
allData1<-subset(data,((data$orig_lat %in% coords$lat)))
allData1<-subset(allData1,((allData1$orig_lon %in% coords$lon)))
allData2<-subset(data,((data$dest_lat %in% coords$lat)))
allData2<-subset(allData2,((allData2$dest_lon %in% coords$lon)))
allData<-rbind(allData1,allData2)
}
}else {
if(nrow(reactiveData$origPoly)>0){
df.pois<-data.frame(lat=data$orig_lat, lon=data$orig_lon)
my_poly <- reactiveData$origPoly
pois <- SpatialPoints(df.pois)
poiPoly <- SpatialPolygons(list(Polygons(list(
Polygon(cbind(my_poly$lat, my_poly$lon))
), ID = "x11")))
coords<-as.data.frame(pois[poiPoly])
allData1<-subset(data,((data$orig_lat %in% coords$lat)))
allData1<-subset(allData1,((allData1$orig_lon %in% coords$lon)))
allData<-allData1
data<-allData
}
if(nrow(reactiveData$destPoly)>0){
df.pois<-data.frame(lat=data$dest_lat, lon=data$dest_lon)
my_poly <- reactiveData$destPoly
pois <- SpatialPoints(df.pois)
poiPoly <- SpatialPolygons(list(Polygons(list(
Polygon(cbind(my_poly$lat, my_poly$lon))
), ID = "x11")))
coords<-as.data.frame(pois[poiPoly])
total <- mydata()
allData2<-subset(data,((data$dest_lat %in% coords$lat)))
allData2<-subset(allData2,((allData2$dest_lon %in% coords$lon)))
allData<-allData2
}
}
leafletProxy("map",session = session) %>%
clearGroup("polyconnections")
leafletProxy("map",session = session) %>%
hideGroup("Origins") %>%
hideGroup("Destinations") %>%
clearGroup("tempmarkers")
if(nrow(allData)>0){
reactiveData$connections<-nrow(allData)
leafletProxy("map",session = session,data=allData) %>%
addMarkers(
lng=~dest_lon,
lat=~dest_lat,
icon = uix.destMarker,
group = "tempmarkers"
) %>%
addMarkers(
lng=~orig_lon,
lat=~orig_lat,
icon = uix.origMarker,
group = "tempmarkers"
)
for(i in 1:nrow(allData)) {
row <- allData[i,]
leafletProxy("map",session = session) %>%
addPolygons(lat=c(row$orig_lat,row$dest_lat),lng=c(row$orig_lon,row$dest_lon),group="polyconnections",weight=1)
}
}
})
observeEvent(input$map_marker_click, {
my_poly <- data.frame(lat=numeric(),lon=numeric())
if (nrow(reactiveData$markers) >= 4) {
my_poly <- rbind(my_poly,reactiveData$markers)
if(reactiveData$shapeState=="poly_all") {
reactiveData$allPoly <- rbind(reactiveData$allPoly,my_poly)
}
else if(reactiveData$shapeState=="poly_orig") {
reactiveData$destPoly <- rbind(reactiveData$destPoly,my_poly)
reactiveData$shapeState = "poly_dest"
}
else if(reactiveData$shapeState=="poly_dest") {
reactiveData$origPoly <- rbind(reactiveData$origPoly,my_poly)
reactiveData$shapeState = "poly_orig"
}
leafletProxy('map') %>% # use the proxy to save computation
addPolygons(lat = my_poly$lat, lng = my_poly$lon, group="polygon",color = polyColor(),fillColor=polyColor())
createConnections()
reactiveData$markers <- data.frame(lat=numeric(),lon=numeric())
}
})
}
shinyApp(ui, server)
I don't think that a dataset of 10.000 pairs is "large" for statistics and I'm pretty sure R is designed well enough to handle this amount of data, so I guess it's leaflet itself or my faulty usage of leaflet or reactive data.
I'm also not very sure about the creation of the lines between origins and destinations which also takes a lot of time but I could not find an easier method to draw a simple line between two points on leaflet.
for(i in 1:nrow(conn.data)) {
row <- conn.data[i,]
leafletProxy("map",session = session) %>% addPolygons(lat=c(row$orig_lat,row$dest_lat),lng=c(row$orig_lon,row$dest_lon),group="Connections",weight=0.5)
}

Related

Unable to generate the candle charts on dialog box in R shiny

I have a R shiny dashboard for stocks analysis. This dashboard has the date at top as driving criteria. The symbols are selected based on that.
The detailed information of these stocks are shown further one by one.
For demo purpose the data is displayed in the attached code.
One button is also added to the dashboard for each of the symbol.
On clicking the button the candle stick graph of that symbol is expected.
In present code when the button is clicked the graph is shown in the Plots pane of the RStudio and not in the graph..but error message subscript out of bounds is shown.
This is shown in the image .
Kindly suggest the changes to display the graph in the popup window.
Image in Plot Pange
library(quantmod)
library(shiny)
getSymbols("AAPL", from = "2020/01/01",to = Sys.Date(), periodicity = "daily")
getSymbols("MSFT", from = "2020/01/01",to = Sys.Date(), periodicity = "daily")
getSymbols("META", from = "2020/01/01",to = Sys.Date(), periodicity = "daily")
getSymbols("ORCL", from = "2020/01/01",to = Sys.Date(), periodicity = "daily")
getSymbols("TSLA", from = "2020/01/01",to = Sys.Date(), periodicity = "daily")
getSymbols("GOOG", from = "2020/01/01",to = Sys.Date(), periodicity = "daily")
df_AAPL <- as.data.frame(AAPL)
df_AAPL$DATE <- index(AAPL)
rownames(df_AAPL) <- NULL
names(df_AAPL) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE')
df_AAPL$SYMBOL <- 'AAPL'
df_MSFT <- as.data.frame(MSFT)
df_MSFT$DATE <- index(MSFT)
rownames(df_MSFT) <- NULL
names(df_MSFT) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE')
df_MSFT$SYMBOL <- 'MSFT'
df_META <- as.data.frame(META)
df_META$DATE <- index(META)
rownames(df_META) <- NULL
names(df_META) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE')
df_META$SYMBOL <- 'META'
df_ORCL <- as.data.frame(ORCL)
df_ORCL$DATE <- index(ORCL)
rownames(df_ORCL) <- NULL
names(df_ORCL) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE')
df_ORCL$SYMBOL <- 'ORCL'
df_TSLA <- as.data.frame(TSLA )
df_TSLA$DATE <- index(TSLA)
rownames(df_TSLA) <- NULL
names(df_TSLA) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE')
df_TSLA$SYMBOL <- 'TSLA'
df_GOOG <- as.data.frame(GOOG)
df_GOOG$DATE <- index(GOOG)
rownames(df_GOOG) <- NULL
names(df_GOOG) <- c('OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED','DATE')
df_GOOG$SYMBOL <- 'GOOG'
df_all <- rbind(df_AAPL, df_MSFT,df_META,df_ORCL,df_TSLA,df_GOOG)
df_all[, c('SYMBOL','DATE','OPEN','HIGH','LOW','CLOSE','VOLUME','ADJUSTED')]
df_rep_date <- data.frame('RunDate'= character(),'ListStocks' = character(), stringsAsFactors=FALSE)
df_rep_date[1,] <- c("2020-01-06", 'AAPL,GOOG,TSLA')
df_rep_date[2,] <- c("2021-01-04", 'ORCL')
df_rep_date[3,] <- c("2022-01-04", 'META,MSFT')
#df_rep_date[4,] <- c("2022-01-07", 'MSFT')
df_rep_date$RunDate <- as.Date(df_rep_date$RunDate)
v_lst_sel_dates <-c(df_rep_date$RunDate)
func_common_crt_lst <- function(...){ x <- list(...); return(x)}
func_1symb_plot <- function(p_symb){
df_tmp_hist_dat = df_all[df_all$SYMBOL == p_symb,c("DATE" ,"OPEN","HIGH","LOW" ,"CLOSE","VOLUME" )]
v_df_dly_dat_6mnth_xts <- xts(df_tmp_hist_dat[, -1], order.by = df_tmp_hist_dat[, 1])
v_grph_op <- candleChart( v_df_dly_dat_6mnth_xts,name = p_symb, type = "auto", up.col = "green", dn.col = "red",
theme = "white",plot = TRUE,TA = "addVo();addSMA(n = 1, on = 1, overlay = TRUE, col ='black');
addSMA(n = 7, on = 1, overlay = TRUE, col ='gold'); addSMA(n = 14, on = 1, overlay = TRUE, col ='brown');addMACD(); addBBands();addRSI();addOBV();")
return(v_grph_op)}
func_1symb_tab <- function(p_symb){
df_tmp_hist_dat = df_all[df_all$SYMBOL == p_symb,c("DATE" ,"OPEN","HIGH","LOW" ,"CLOSE","VOLUME" )]
df_tmp_hist_dat <- df_tmp_hist_dat[1:5,]
df_tmp_hist_dat$DATE <- as.Date(df_tmp_hist_dat$DATE)
v_tab_op <- df_tmp_hist_dat
}
simpUI <- function(id) {
tagList(selectInput(NS(id, 'RunDate'), "Run Date", v_lst_sel_dates),
textOutput(NS(id,'date_output')),
textOutput(NS(id,'lst_symb_output')),
uiOutput(NS(id,"myplot"))
)
}
simpServer <- function(id) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
string <- reactive(input$RunDate)
output$date_output <- renderText(string())
v_lst_symbol <- reactive(df_rep_date[df_rep_date$RunDate == input$RunDate,]$ListStocks)
output$lst_symb_output <- renderText(v_lst_symbol())
observeEvent(input$RunDate, {
print(v_lst_symbol())
symbs <- read.table(text = v_lst_symbol(), sep = ",", colClasses = "character")
print(symbs)
lapply(symbs[1,], function(v_symb){
v_symb_name = paste0(v_symb, '_name')
output[[paste0(v_symb, '_name')]] = renderText(v_symb_name)
output[[paste0(v_symb, '_table')]] <- renderTable(func_1symb_tab(v_symb))
observeEvent({input[[paste0(v_symb, '_cndl_chart')]]},{
plt_cndl <- func_1symb_plot(v_symb)
print(' before showModal')
showModal(modalDialog(title = v_symb, size = "l",renderPlot(plt_cndl)))
print("after showmodel ")
})
})
})
output$myplot <- renderUI({
symbs <- read.table(text = v_lst_symbol(), sep = ",", colClasses = "character")
tagList(
lapply(symbs[1,], function(v_symb){
id1 <- paste0(v_symb, '_name')
id3 <- paste0(v_symb, '_table')
id4 <- paste0(v_symb, '_cndl_chart')
fluidRow(
func_common_crt_lst(htmlOutput(ns(id1)),
tableOutput(ns(id3)),
actionButton(ns(id4), ns(id4))))
})
)
})
})
}
ui <- fluidPage(fluidRow(simpUI("par1")))
server <- function(input, output, session) {
simpServer("par1")
}
shinyApp(ui = ui, server = server)
Your code is by far not minimal and it needed quite some amount of time to strip all the unnecessary parts away to find out what you are trying. For future reference you may get more help if you minimize your code.
Your code has several issues:
You cannot use renderPlot without a corresponding plotOutput. That is the main issue why your plot does not show up. Hence, you need to place a plotOutput in your modal and add an additional renderPlot to fill it.
Your module is somewhat useless and you shoudl rather create a module around the elements symbol name, table and modal action button.
Using some loops helps tremendously to avoid this massive code duplictation.
Having said that, here's a working example which refactored your code quite extensively and made use of the tidyverse to simplify some of the data operations you are doing.
library(quantmod)
library(shiny)
library(dplyr)
library(purrr)
library(stringr)
get_data <- function(symbols = c("AAPL", "MSFT", "META", "ORCL",
"TSLA", "GOOG")) {
syms <- getSymbols(symbols, from = "2020/01/01",
to = Sys.Date(), periodicity = "daily")
map_dfr(syms, function(sym) {
raw_data <- get(sym)
raw_data %>%
as_tibble() %>%
set_names(c("OPEN", "HIGH", "LOW", "CLOSE", "VOLUME", "ADJUSTED")) %>%
mutate(SYMBOL = sym,
DATE = index(raw_data)) %>%
select(SYMBOL, DATE, OPEN, HIGH, LOW, CLOSE, VOLUME, ADJUSTED)
})
}
if (!exists("df_all")) {
df_all <- get_data()
}
df_rep_data <- tribble(~ RunDate, ~ ListStocks,
"2020-01-06", "AAPL, GOOG, TSLA",
"2021-01-04", "ORCL",
"2022-01-04", "META, MSFT") %>%
mutate(RunDate = as.Date(RunDate))
make_candle_chart <- function(symbol, dat = df_all) {
vals <- dat %>%
filter(SYMBOL == symbol)
ts <- xts(vals %>%
select(OPEN, HIGH, LOW, CLOSE, VOLUME),
order.by = vals %>% pull(DATE))
candleChart(ts, name = symbol, type = "auto",
up.col = "green", dn.col = "red", theme = "white", plot = TRUE,
TA = c(addVo(),
addSMA(n = 1, on = 1, overlay = TRUE, col = "black"),
addSMA(n = 7, on = 1, overlay = TRUE, col = "gold"),
addSMA(n = 14, on = 1, overlay = TRUE, col = "brown"),
addMACD(),
addBBands(),
addRSI(),
addOBV()))
}
make_table <- function(symbol, dat = df_all) {
dat %>%
filter(SYMBOL == symbol) %>%
select(DATE, OPEN, HIGH, LOW, CLOSE, VOLUME) %>%
slice(1:5)
}
symb_ui <- function(id) {
ns <- NS(id)
tagList(
tags$h4(textOutput(ns("symbol"))),
tableOutput(ns("table")),
actionButton(ns("show_modal"), "Show Candle Chart")
)
}
symb_server <- function(id, get_symbol_name) {
moduleServer(id, function(input, output, session) {
ns <- session$ns
output$symbol <- renderText(get_symbol_name())
output$table <- renderTable(make_table(get_symbol_name()))
output$cndl_chart <- renderPlot(make_candle_chart(get_symbol_name()))
observeEvent(input$show_modal, {
mdl <- modalDialog(title = get_symbol_name(),
size = "l",
plotOutput(ns("cndl_chart")))
showModal(mdl)
})
})
}
ui <- fluidPage(
selectInput("run_date", "Run Date", df_rep_data %>% pull(RunDate)),
tags$h2(textOutput("date_output")),
tags$h3(textOutput("lst_symb_output")),
uiOutput("symbols_output")
)
server <- function(input, output, session) {
handler <- list()
get_syms <- list()
output$date_output <- renderText(req(input$run_date))
output$lst_symb_output <- renderText({
df_rep_data %>%
filter(RunDate == req(input$run_date)) %>%
pull(ListStocks)
})
output$symbols_output <- renderUI({
symbols <- df_rep_data %>%
filter(RunDate == req(input$run_date)) %>%
pull(ListStocks) %>%
str_split(fixed(", ")) %>%
unlist()
syms <- vector("list", length(symbols)) %>%
set_names(symbols)
for (sym in symbols) {
## this local construct is needed for scoping cf.
## https://gist.github.com/bborgesr/e1ce7305f914f9ca762c69509dda632e
local({
my_sym <- sym
syms[[my_sym]] <<- symb_ui(my_sym)
get_syms[[my_sym]] <<- reactive(my_sym)
handler[[my_sym]] <<- symb_server(my_sym, get_syms[[my_sym]])
})
}
tagList(syms)
})
}
shinyApp(ui = ui, server = server)

How do I loop an observeEvent in shiny? to change style in leaflet when polygons are clicked

I have a project that I am building a shiny for. I need to create n maps (up to 99) based on an input. The same polygons will be displayed on each map and when a user clicks on a polygon it changes the polygons colour.
So far I can create the number of maps based on an input value but I am struggling to work out how to put the observeEvent in a loop for each map.
The below example works, but I would have to write out the two observeEvents 99 times.
Please help!
library(leaflet)
## create two square polygons
Sr1 <- Polygon(cbind(c(1, 2, 2, 1, 1), c(1, 1, 2, 2, 1)))
Sr2 <- Polygon(cbind(c(2, 3, 3, 2, 2), c(1, 1, 2, 2, 1)))
Srs1 <- Polygons(list(Sr1), "s1")
Srs2 <- Polygons(list(Sr2), "s2")
SpP <- SpatialPolygons(list(Srs1, Srs2), 1:2)
ui <- fluidPage(
sliderInput("nomaps", "Number of maps:",
min = 1, max = 5, value = 1
),
uiOutput("plots")
)
change_color <- function(map, id_to_remove, data, colour, new_group){
leafletProxy(map) %>%
removeShape(id_to_remove) %>% # remove previous occurrence
addPolygons(
data = data,
label = data$display,
layerId = data$ID,
group = new_group, # change group
fillColor = colour)
}
server <- function(input,output,session){
## Polygon data
rv <- reactiveValues(
df = SpatialPolygonsDataFrame(SpP, data = data.frame(
ID = c("1", "2"),
display = c("1", "1")
), match.ID = FALSE)
)
# initialization
output$map <- renderLeaflet({
leaflet(options = leafletOptions( zoomControl = FALSE, minZoom = 6.2, maxZoom = 6.2, dragging = FALSE))
})
observe({
data <- rv$df
lapply(1:input$nomaps, function(i) {
output[[paste("plot", i, sep = "_")]] <- renderLeaflet({
leaflet(options = leafletOptions( zoomControl = FALSE, minZoom = 6.2, maxZoom = 6.2, dragging = FALSE))%>%
addPolygons(
data = data,
label = data$display,
layerId = data$ID,
group = "unclicked_poly")
})
})
})
# Create plot tag list
output$plots <- renderUI({
plot_output_list <- lapply(1:input$nomaps, function(i) {
plotname <- paste("plot", i, sep = "_")
leafletOutput(plotname)
})
do.call(tagList, plot_output_list)
})
#first click
observeEvent(input$plot_1_shape_click, {
# execute only if the polygon has never been clicked
req(input$plot_1_shape_click$group == "unclicked_poly")
# filter data
data <- rv$df[rv$df$ID==input$plot_1_shape_click$id,]
change_color(map = "plot_1",
id_to_remove = input$plot_1_shape_click$id,
data = data,
colour = "yellow",
new_group = "clicked1_poly")
})
#back to normal
observeEvent(input$plot_1_shape_click, {
req(input$plot_1_shape_click$group == "clicked1_poly")
data <- rv$df[rv$df$ID==input$plot_1_shape_click$id,]
# back to normal
leafletProxy("plot_1") %>%
removeShape(input$plot_1_shape_click$id) %>% # remove previous occurrence
addPolygons(
data = data,
label = as.character(data$display),
layerId = data$ID,
group = "unclicked_poly") # back to initialize group
})
}
shinyApp(ui, server)
Try this
observe({
lapply(1:input$nomaps, function(i) {
observeEvent(input[[paste0("plot_", i,"_shape_click")]], {
# execute only if the polygon has never been clicked
selected.id <- input[[paste0("plot_", i,"_shape_click")]]
data <- rv$df[rv$df$ID==selected.id$id,]
if (selected.id$group == "unclicked_poly") {
change_color(map = paste0("plot_", i),
id_to_remove = selected.id$id,
data = data,
colour = "yellow",
new_group = "clicked1_poly")
} else {
leafletProxy(paste0("plot_", i)) %>%
removeShape(selected.id$id) %>% # remove previous occurrence
addPolygons(
data = data,
label = as.character(data$display),
layerId = data$ID,
group = "unclicked_poly") # back to initialize group
}
})
})
})
observe ({
lapply(1:input$nomaps, function(i) {
observeEvent(input[[paste0("plot_", i,"_shape_click",sep="")]], {
# execute only if the polygon has never been clicked
if (input[[paste0("plot_", i,"_shape_click",sep="")]]$group == "unclicked_poly") {
selected.id <- input[[paste0("plot_", i,"_shape_click",sep="")]]
data <- rv$df[rv$df$ID==selected.id$id,]
change_color(map = paste0("plot_", i, sep=""),
id_to_remove = selected.id$id,
data = data,
colour = "yellow",
new_group = "clicked1_poly")
} else {
selected.id <- input[[paste0("plot_", i,"_shape_click",sep="")]]
data <- rv$df[rv$df$ID==selected.id$id,]
leafletProxy(paste0("plot_", i, sep="")) %>%
removeShape(selected.id$id) %>% # remove previous occurrence
addPolygons(
data = data,
label = as.character(data$display),
layerId = data$ID,
group = "unclicked_poly") # back to initialize group
}
})
})
})

How do I save addDrawToolbar shapes drawn in an R Leaflet Shiny map so I can re-import them?

I need to take the shapes drawn in an R Leaflet Shiny app using addDrawToolbar in leaflet.extras and save them to a file that can be re-imported by an R Leaflet Shiny app at a later time.
I am focusing on the leaflet.extras information in GitHub by Bhaskar Karambelkar where it lists the commands to pull out the data for the shapes drawn. How do I parse out this data in R?
The following code is what I can do so far: Draw shapes and print them out as a .csv or .txt file. I've included both examples. So in this code, you draw whatever shapes you want from the Draw Toolbar and then hit the Generate Shape List button.
It works for capturing all the shape coordinates, but in these formats the data is not as usable as I need them to be. Is there a way to parse this data so that it can be re-imported, displayed, and edited if need be? Any insights on this is really appreciated!
library(shiny)
library(leaflet)
library(leaflet.extras)
library(utils)
sh <- data.frame()
ui <- bootstrapPage(
tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
leafletOutput("mymap", width = "100%", height = "100%"),
absolutePanel(top = 10, right = 10, width = 300,
style = "padding: 8px",
actionButton("printShapes", h5(strong("Generate Shape List")))
)
)
server <- function(input, output, session) {
output$mymap <- renderLeaflet({
leaflet() %>%
addTiles(group = "Default", attribution = 'Map data © OpenStreetMap contributors') %>%
setView(lng = -98, lat = 38, zoom = 4) %>%
addDrawToolbar(targetGroup = "draw", position = "topleft", editOptions = editToolbarOptions(edit=TRUE))
})
# Generate Shape List Action Button
observeEvent(input$printShapes, {
shapedf <- data.frame()
reactive(shapedf)
shapedf <-input$mymap_draw_all_features
sh <<- as.data.frame(shapedf)
sh <- t(sh)
shpwrite <- write.csv(sh, paste0("OUTPUTdrawings",".csv"))
shpwrite1 <- dput(sh, file = "OUTPUTdrawings1.txt")
})
}
shinyApp(ui = ui, server = server)
After much cogitation, angst, trial and error, I finally figured out how to do this. Not sure if this is the best way to do this, but it works.
library(shiny)
library(leaflet)
library(leaflet.extras)
library(utils)
sh <- data.frame()
ui <- bootstrapPage(
tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
leafletOutput("mymap", width = "100%", height = "100%"),
absolutePanel(top = 10, right = 10, width = 300,
style = "padding: 8px",
fileInput("drawingFile",h4(strong("Input Drawing CSV")), accept = ".csv"),
actionButton("printShapes", h5(strong("Generate Drawing File")))
)
)
server <- function(input, output, session) {
output$mymap <- renderLeaflet({
leaflet() %>%
addTiles(group = "Default", attribution = 'Map data © OpenStreetMap contributors') %>%
setView(lng = -98, lat = 30, zoom = 4) %>%
addDrawToolbar(targetGroup = "draw", position = "topleft", editOptions = editToolbarOptions(edit=TRUE))
})
# Generate Shape List Action Button
observeEvent(input$printShapes, {
shapedf <- data.frame()
reactive(shapedf)
shapedf <-input$mymap_draw_all_features
sh <<- as.data.frame(shapedf)
# sh <- t(sh) # This is easier to read manually, but not for reading into R.
shpwrite <- write.csv(sh,paste0("Drawings", ".csv"))
})
# Intake Shape CSV
observeEvent(input$drawingFile, {
drawFile <- input$drawingFile
ext <- file_ext(drawFile$datapath)
req(drawFile)
validate(need(ext == "csv", "Please upload a csv file."))
ddf <- read.csv(drawFile$datapath, header = TRUE) # The drawing dataframe
ind <- which(ddf == "Feature") # Index for drawing df to break up the df to redraw the shapes.
ind <- as.array(ind)
for (i in 1:nrow(ind)) {
if(i != nrow(ind)) thisShape <- ddf[ind[i]:ind[i+1]]
else thisShape <- ddf[ind[i]:ncol(ddf)]
#####
if(thisShape[3] == "polyline") {
tf <- array(startsWith(names(thisShape),"features.geometry.coordinates"))
w <- 1
pnts <- array()
for (i in 1:nrow(tf)) {
if(tf[i] == TRUE) {
pnts[w] <- thisShape[i]
w <- w+1
}
}
n <- 1
m <- 1
plng <- array()
plat <- array()
pnts <- as.array(pnts)
for (j in 1:nrow(pnts)) {
if(j %% 2 == 1) {
plng[n] <- pnts[j]
n <- n+1
}
else if(j %% 2 == 0) {
plat[m] <- pnts[j]
m <- m+1
}
}
as.vector(plng, mode = "any")
as.vector(plat, mode = "any")
PL <- data.frame(matrix(unlist(plng)))
PLsub <- data.frame(matrix(unlist(plat)))
PL <- cbind(PL, PLsub)
colnames(PL) <- c("lng","lat")
PL1 <- reactiveVal(PL)
proxy <- leafletProxy("mymap", data = PL1())
proxy %>% addPolylines(lng = ~lng, lat = ~lat, group = "draw")
}
#####
else if(thisShape[3] == "polygon") {
tf <- array(startsWith(names(thisShape),"features.geometry.coordinates"))
w <- 1
pnts <- array()
for (i in 1:nrow(tf)) {
if(tf[i] == TRUE) {
pnts[w] <- thisShape[i]
w <- w+1
}
}
n <- 1
m <- 1
plng <- array()
plat <- array()
pnts <- as.array(pnts)
for (j in 1:nrow(pnts)) {
if(j %% 2 == 1) {
plng[n] <- pnts[j]
n <- n+1
}
else if(j %% 2 == 0) {
plat[m] <- pnts[j]
m <- m+1
}
}
as.vector(plng, mode = "any")
as.vector(plat, mode = "any")
PG <- data.frame(matrix(unlist(plng)))
PGsub <- data.frame(matrix(unlist(plat)))
PG <- cbind(PG, PGsub)
colnames(PG) <- c("lng","lat")
PG1 <- reactiveVal(PG)
proxy <- leafletProxy("mymap", data = PG1())
proxy %>% addPolygons(lng = ~lng, lat = ~lat, group = "draw")
}
#####
else if(thisShape[3] == "rectangle"){
rlng1 <- as.numeric(thisShape[5])
rlat1 <- as.numeric(thisShape[6])
rlng2 <- as.numeric(thisShape[9])
rlat2 <- as.numeric(thisShape[10])
proxy <- leafletProxy("mymap")
proxy %>% addRectangles(lng1 = rlng1, lat1 = rlat1, lng2 = rlng2, lat2 = rlat2,
group = "draw")
}
#####
else if(thisShape[3] == "circle"){
crad <- as.numeric(thisShape[4])
clng <- as.numeric(thisShape[6])
clat <- as.numeric(thisShape[7])
proxy <- leafletProxy("mymap")
proxy %>% addCircles(lng = clng, lat = clat, radius = crad, group = "draw")
}
#####
else if(thisShape[3] == "marker") {
mlng <- as.numeric(thisShape[5])
mlat <- as.numeric(thisShape[6])
proxy <- leafletProxy("mymap")
proxy %>% addMarkers(lng = mlng, lat = mlat, group = "draw")
}
#####
else if(thisShape[3] == "circlemarker") {
cmlng <- as.numeric(thisShape[6])
cmlat <- as.numeric(thisShape[7])
proxy <- leafletProxy("mymap")
proxy %>% addCircleMarkers(lng = cmlng, lat = cmlat, group = "draw")
}
}
})
}
shinyApp(ui = ui, server = server)

Shiny, Event reactive, running several functions

I am new in shiny, at the moment I am trying to set up a code where I can calculate activity clusters (through DBSCAN package) based on input variables: "eps" (minimum distances between points to be part of a cluster), "minpts" (minimum number of points to certain categories as Health), "maxpts" (minimum number of points for general categories as pubs, restaurants etc).
I did a test only through leaflet (without shiny) and the code runs smoothly, but once I bring-in shiny, I'm not able to make it work
the idea is that the user can modify these 3 variables on the side panel, and click an action button in order to trigger the calculation.
#----------LIBRARIES----------#
library(plyr)
library(geosphere)
library(dbscan)
library(osmdata)
library(sf)
library(tidyr)
library(sp)
library(rgdal)
library(leaflet)
library(shiny)
#-------LOAD FILES-------#
OSM_merged <- read.csv(file = "C:\\Users\\jsainz\\Documents\\R\\Shiny_test\\OSM_merged.csv")
OSM_points <- OSM_merged
OSM_points$color <- OSM_points$category
OSM_points$color <- str_replace_all(OSM_points$color, "Culture", "#3073A")
OSM_points$color <- str_replace_all(OSM_points$color, "Educational", "# 887CAF")
OSM_points$color <- str_replace_all(OSM_points$color,"Financial", "#540002")
OSM_points$color <- str_replace_all(OSM_points$color,"Health", "#D6E899")
OSM_points$color <- str_replace_all(OSM_points$color,"Leisure", "#D2D68D")
OSM_points$color <- str_replace_all(OSM_points$color,"Office", "#D3696C")
OSM_points$color <- str_replace_all(OSM_points$color,"Shop", "#AA9739")
OSM_points$color <- str_replace_all(OSM_points$color,"Sport", "#378B2E")
OSM_points$color <- str_replace_all(OSM_points$color,"Sustain", "#554600")
OSM_points$color <- str_replace_all(OSM_points$color,"Toursim", "#5FAE57")
xy <- OSM_points[,c(2,3)]
OSM_points <- SpatialPointsDataFrame(coords = xy, data = OSM_points,proj4string = CRS("+proj=longlat +datum=WGS84 +ellps=WGS84 +towgs84=0,0,0"))
#-------FUNCTIONS-------#
assign_clusters <- function(poi_df, minPts = NA) {
if(is.na(minPts)) {
if(poi_df[1, "category"] %in% c("Culture", "Leisure", "Education", "Health", "Financial")) {
minPts <- "minpts"
} else minPts <- "maxpts"
}
eps <- "epsilon"
poi_df[c("lng", "lat")] %>%
distm(fun = distHaversine) %>%
as.dist() %>%
dbscan(eps = eps, minPts = minPts) %>%
.[["cluster"]] %>%
cbind(poi_df, cluster = .)
}
get_hull<- function(df) {
cbind(df$lng, df$lat) %>%
as.matrix() %>%
st_multipoint() %>%
st_convex_hull() %>%
st_sfc(crs = 4326) %>%
{st_sf(category = df$category[1], cluster = df$cluster[1], geom = .)}
}
hulls <- function(df) {
df %>%
split(.$cluster) %>%
map(get_hull)
}
#----------SHINY CODE----------#
ui <- fluidPage(
titlePanel("Jorge_Test"),
sidebarPanel(
numericInput(inputId = "epsilon", label = "distance in meters to calculate activity clusters", 200),
numericInput(inputId = "minpts", label = "minimum points to calculate clusters", 5),
numericInput(inputId = "maxpts", label = "maximum points to calculate clusters", 10),
actionButton("run", "Run Calculation"),
actionButton("view", "generate plan"),
width = 2),
mainPanel(
leafletOutput("mymap", width = 1550, height = 850)
)
)
server <- function(input, output, session) {
output$mymap <- renderLeaflet({
leaflet("mymap")%>%
setView(lng = 0.0982, lat = 51.7674, zoom = 15)%>%
addProviderTiles(providers$CartoDB.Positron, options = providerTileOptions(noWrap = TRUE))%>%
addCircleMarkers(data = OSM_points,
radius = .7,
popup = ~category,
color = ~color)})
oberveEvent(input$run, {
updateNumericInput(session, "epsilon")
updateNumericInput(session, "minpts")
updateNumericInput(session, "maxpts")
})
Clean_data <- OSM_merged %>%
split(OSM_merged$category) %>%
map_df(assign_clusters)
hulls_cat <- Clean_data %>%
group_by(category) %>%
summarise()
map_cluster_hulls <- Clean_data %>%
filter(cluster != 0) %>%
select(lng, lat, category, cluster) %>%
split(.$category) %>%
map(hulls)
mdata <- melt(map_cluster_hulls, id = c("category", "cluster", "geom"))
mch <- data.frame(mdata$category, mdata$cluster, mdata$geom)
observeEvent(input$view, {
leafletProxy("mymap", session) %>%
addPolygons(data = mch$geom,
fill = NA,
fillOpacity = .01,
weight = 2,
color = "red",
opacity = .8)
}
)
}
shinyApp(ui, server)
any idea of how to solve it?
here is a link to the OSM_merged.csv file:
https://www.dropbox.com/s/5ok9frcvx8oj16y/OSM_merged.csv?dl=0

R Shiny, how to use highcharts drilldown in shinyapp depending on selectinput widget result?

I am trying to create a drill down chart using highcharts package, the chart must be dependent on the selectinput results.
The current error is
Error in .getReactiveEnvironment()$currentContext() :
Operation not allowed without an active reactive context. (You tried to do something that can only be done from inside a reactive expression or observer.)
the expected or desired output is to get dynamic plot depending on the selected value.
library (shinyjs)
library (tidyr)
library (data.table)
library (highcharter)
library (dplyr)
library (shinydashboard)
library (shiny)
x <- c("Farm","Farm","Farm","City","City","City","Ocean","Ocean")
y <- c("Sheep","Sheep","Cow","Car","Bus","Bus","Boat","Boat")
z <- c("Bill","Tracy","Sandy","Bob","Carl","Newt","Fig","Tony")
a <- c(1,1,1,1,1,1,1,1)
b <- c(3,2,5,1,3,5,1,5)
c <- c(4,6,7,7,4,2,1,6)
xxxx <- data.frame(x, y, z, a, b, c, stringsAsFactors = FALSE)
header <- dashboardHeader()
body <- dashboardBody(
selectInput("selectid","Select a Measurement",choices=c("a","b","c"),selected = "a"),
highchartOutput("Working"))
sidebar <- dashboardSidebar()
ui <- dashboardPage(header, sidebar, body)
server <- function(input, output, session) {
zzz<-reactive({
#browser
select(xxxx,one_of(c("x", "y", "z", input$selectid)))})
output$Working <- renderHighchart({
summarized <- zzz() %>%
group_by(x) %>%
summarize(Quantity = sum(!!sym(input$selectid)))
summarized <- arrange(summarized, desc(Quantity))
tibbled <- tibble(name = summarized$x, y = summarized$Quantity)
# This time, click handler is needed.
drilldownHandler <-
JS(
"function(event) {Shiny.onInputChange('ClickedInput', event.point.drilldown);}")
installDrilldownReceiver <- JS("function() {
var chart = this;
Shiny.addCustomMessageHandler('drilldown', function(message) {
var point = chart.get(message.point)
chart.addSeriesAsDrilldown(point, message.series);
});
}")
highchart() %>%
hc_chart(events = list(load = installDrilldownReceiver, drilldown = drilldownHandler)) %>%
hc_xAxis(type = "category") %>%
hc_add_series(tibbled, "column", hcaes(x = name, y = y, drilldown = name, id = name), color = "#E4551F") %>%
hc_plotOptions(column = list(stacking = "normal")) %>%
hc_drilldown(allowPointDrilldown = TRUE)
})
observeEvent(
#browser
input$ClickedInput, {
levels <- strsplit(input$ClickedInput, "_", fixed = TRUE)[[1]]
resemblences <- c("x", "y", "z")
dataSubSet <- reactive({
#browser()
zzz()
})
for (i in 1:length(levels)) {
dataSubSet() <- zzz()[zzz()[[resemblences[i]]] == levels[i],]
}
normalized <- data.frame(category = dataSubSet()[[resemblences[length(levels) + 1]]], amount = input$selectid)
summarized <- normalized %>%
group_by(category) %>%
summarize(Quantity = sum(amount))
summarized <- arrange(summarized, desc(Quantity))
tibbled <- tibble(name = summarized$category, y = summarized$Quantity)
nextLevelCodes = lapply(tibbled$name, function(fac) {
paste(c(levels, as.character(fac)), collapse = "_")
}) %>% unlist
tibbled$id = nextLevelCodes
if (length(levels) < length(resemblences) - 1) {
tibbled$drilldown = nextLevelCodes
}
session$sendCustomMessage("drilldown", list(
series = list(
type = "column",
name = paste(levels, sep = "_"),
data = list_parse(tibbled)
),
point = input$ClickedInput
))
})
output$trial <- renderText({input$ClickedInput})
}
shinyApp(ui, server)

Resources