Change google maps heatmap options in shiny - r

I am using googleway library in Shiny R.
The heatmap displays correctly, but I cannot change the heatmap options. If I uncomment the block code where I try to change options, the app crashes.
Here is the part of the code that works, with the offending lines commented out.
library(googleway)
library(magrittr)
library(shiny)
library(shinydashboard)
# Define UI for app
header1 <- dashboardHeader(
title = "My Dashboard"
)
sidebar1 <- dashboardSidebar(
sidebarMenu(
fileInput("file0", "Choose CSV File",
multiple = TRUE,
accept = c("text/csv",
"text/comma-separated-values,text/plain",".csv")),
sliderInput("opacity", "Opacity:",
min = 0, max = 1,
value = 0.5, step = 0.05),
sliderInput("radius", "Radius:",
min = 0, max = 50,
value = 25),
sliderInput("blur", "Blur:",
min = 0, max = 1,
value = 0.75, step = 0.05),
sliderInput("maxvalue", "MaxValue:",
min = 0, max = 1,
value = 1, step = 0.05)
) #sidebarMenu
) #dashboardSidebar
body1 <- dashboardBody(
fluidRow(
tabBox(
title = "TabBox Title 1",
id = "tabset1", height = "400px", width = 11,
selected = "Tab1",
tabPanel("Tab1",
google_mapOutput("Map1")
),
tabPanel("Tab2", "Tab content 2")
) #box
) #fluidRow
) #dashboardBody
ui <- dashboardPage(header1, sidebar1, body1)
# Define data
df <- data.frame(lat = c(14.61),
lon = c(-90.54),
weight = c(100))
# Define SERVER logic
server <- function(input, output, session) {
map_key <- "my_key"
## https://developers.google.com/maps/documentation/javascript/get-api-key
## plot the map
output$Map1 <- renderGoogle_map({
google_map(key = map_key, data = df, zoom = 2, search_box = F) %>%
add_heatmap(weight = "weight") #%>%
#add_traffic()
}) #renderGoogle_map
observeEvent(input$opacity, {
# THIS PART IS COMMENTED OUT BECAUSE THE APP CRASHES
# google_map_update(map_id = "Map1") %>%
# update_heatmap(data = df, option_opacity = input$opacity)
}) #observeEvent
} #server
# Run app
shinyApp(ui, server)
Your help with this will be greatly appreciated! :)

You can use a reactive({}) to carry the input$opacity value and pass it directly to add_heatmap() to achieve the opacity responsiveness.
This can still be done inside the google_map_update(), but you'd have to clear the heatmap layer first, otherwise you'd just be adding layers on top of each other.
server <- function(input, output, session) {
map_key <- "your_key"
## https://developers.google.com/maps/documentation/javascript/get-api-key
opacity <- reactive({
return(input$opacity)
})
## plot the map
output$Map1 <- renderGoogle_map({
google_map(key = map_key, data = df, zoom = 2, search_box = F) %>%
add_heatmap(weight = "weight") #%>%
#add_traffic()
}) #renderGoogle_map
observeEvent(input$opacity, {
google_map_update(map_id = "Map1") %>%
clear_heatmap() %>%
add_heatmap(data = df, option_opacity = opacity())
})
}
} #server

Related

edit a reactive database

Trying to edit a reactive database so that updates to the database are reflected in the output.
Have tried numerous variants, but none are working, general idea is shown - where I would like to have the figure update with changes to the database.
library(tidyverse)
library(shiny)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("ages", "Max age:", 10, 100, 15),
sliderInput("nsamp",
"Sample size:",
min = 10,
max = 1000,
value = 100)),
mainPanel(dt_output('Sample sizes and weighting', 'x1'),
plotOutput("fig"))
)
)
server <- function(input, output) {
x = reactive({
df = data.frame(age = 1:input$ages,
samples = input$nsamp,
weighting = 1)
})
output$x1 = renderDT(x(),
selection = 'none',
editable = TRUE,
server = TRUE,
rownames = FALSE)
output$fig = renderPlot({
ggplot(x(), aes(age, samples)) +
geom_line() +
geom_point()
})
}
shinyApp(ui = ui, server = server)
We can use input$x1_cell_edit and reactiveValues to modify the data that is passed to the plot.
Note the use of isolate inside renderDT, that is to prevent the table from re-rendering when db$database is modified.
library(tidyverse)
library(shiny)
library(DT)
# Define UI for application that draws a histogram
ui <- fluidPage(
sidebarLayout(
sidebarPanel(
sliderInput("ages", "Max age:", 10, 100, 15),
sliderInput("nsamp",
"Sample size:",
min = 10,
max = 1000,
value = 100
)
),
mainPanel(
dataTableOutput("x1"),
plotOutput("fig")
)
)
)
server <- function(input, output) {
# all the data will be stored in this two objects
db <- reactiveValues(database = NULL)
# to store the modified values
edited_vals <- reactiveVal(tibble(row = numeric(), col = numeric(), value = numeric()))
# create a new table each time the sliders are changed
observeEvent(c(input$ages, input$nsamp), {
df <- data.frame(
age = 1:input$ages,
samples = input$nsamp,
weighting = 1
)
db$database <- df
})
observeEvent(input$x1_cell_edit, {
db$database[as.numeric(input$x1_cell_edit$row), as.numeric(input$x1_cell_edit$col + 1)] <- as.numeric(input$x1_cell_edit$value)
})
output$x1 <- renderDT(
{
input$ages
input$nsamp
datatable(
isolate(db$database),
selection = "none",
editable = TRUE,
rownames = FALSE,
options = list(stateSave = TRUE)
)
},
server = TRUE
)
output$fig <- renderPlot({
ggplot(db$database, aes(as.numeric(age), as.numeric(samples))) +
geom_point() +
geom_line()
})
}
shinyApp(ui = ui, server = server)

how to avoid flickering while refreshing valueboxes in Shiny dashboard

I use shiny modules to update a large number of value boxes.
The annoying part is the value boxes donot seem to scale above 10 or 20 as their updating is causing annoying flickers.
Even those boxes whose values are not changing on the next invalidation, flicker. Ideally if the value is not changing the box should not refresh.
A representative shiny app using shiny modules is presented to replicate the problem.
When the value of N is 4 or 5 the number of boxes are small and the updates happen instantaneously. As you increase N to 10 it gets noticeable and at N = 20 the flicker is unbearable.
### ui.R
## reprex ui.r
suppressPackageStartupMessages(library(shiny))
suppressPackageStartupMessages(library(magrittr))
suppressPackageStartupMessages(library(shinydashboard))
suppressPackageStartupMessages(library(shinydashboardPlus))
suppressPackageStartupMessages(library(lubridate))
suppressPackageStartupMessages(library(shinyjs))
ui <- dashboardPage(
header = dashboardHeader(title = "Reprex"),
sidebar = dashboardSidebar(
sidebarMenu(id = "sidebar",
menuItem(text = "Fuel prediction",tabName = "LIVE",icon = icon("tachometer-alt"))
)
), # end of sidebarMenu
body = dashboardBody(id="body",useShinyjs(),
tabItems(
tabItem(tabName = "LIVE",h1("FUEL DISPENSATION"),
fluidRow(id = "parameters",
column(width = 2,h3("STATION")),
column(width = 2,h4("TIME UPDT")),
column(width = 2,h4("TANK LEVEL")),
column(width = 2,h4("DISPENSED")),
column(width = 2,h4("REFUELLED"))
),
uiOutput("st1"),
uiOutput("st2"),
uiOutput("st3"),
uiOutput("st4"),
uiOutput("st5"),
uiOutput("st6"),
uiOutput("st7"),
uiOutput("st8"),
uiOutput("st9"),
uiOutput("st10"),
uiOutput("st11"),
uiOutput("st12"),
uiOutput("st13"),
uiOutput("st14"),
uiOutput("st15"),
uiOutput("st16"),
uiOutput("st17"),
uiOutput("st18"),
uiOutput("st19"),
uiOutput("st20")
)
)
) # End of body
) # end of dashboard page
And this is the server.R:
## reprex server.R
suppressPackageStartupMessages(library(shiny))
suppressPackageStartupMessages(library(shinydashboard))
suppressPackageStartupMessages(library(data.table))
source("modules.R")
shinyServer(function(input, output,session) {
seqno <- reactiveVal(5)
timer <- reactiveTimer(3000)
observeEvent(timer(),{
seqno((seqno() + 1))
for(i in seq_len(N)){ ## the for loop generates all the output assignment statements using shiny module.
genrVB(i = i,output = output,s = seqno())
}
})
# This is just to stop the app when session ends. Ignore for the purposes of this reprex.
session$onSessionEnded(function() {
print("Session ended")
stopApp()
})
})
And this is the modules.R
### Shiny module reprex
library(shiny)
library(purrr)
library(maps)
# take N cities and N data.tables randomly generated to serve our input data for the shiny app
N <- 4
cities <- world.cities %>% as.data.table() %>% .$name %>% sample(N)
### Generate N simulated data.tables for the N cities.
### Notice the values of the column 2,3,4 donot change every minute.
simdata <- purrr::map(seq_len(N),
~data.table(ts = seq.POSIXt(Sys.time(),by = 60,length.out = 100),
fuel = rep(c(5000:5004),each = 2),
out = rep(c(100,110),each = 25),
fill = rep(c(100,200),each = 10)
))
fuelrowUI <- function(id,label = "Site X",n = 1){
ns <- NS(id)
fluidRow(id = ns("siteid"),
column(2,h3(cities[n])),
valueBoxOutput(ns("upd"),width = 2),
valueBoxOutput(ns("tank"),width = 2),
valueBoxOutput(ns("out"),width = 2),
valueBoxOutput(ns("fill"),width = 2)
)
}
fuelrowServer <- function(id,datarow=1,n = 1){
moduleServer(id,
function(input,output,session){
output$upd <- renderValueBox(vbtime(n,k = datarow))
output$tank <- renderValueBox(vblevel(n,k = datarow))
output$out <- renderValueBox(vbout(n,k = datarow))
output$fill <- renderValueBox(vbin(n,k = datarow))
})
}
# Function to loop through the output$.. in server.R using the two shiny modules
genrVB <- function(i,s,output = output){
stn <- paste0("st",i)
output[[stn]] <- renderUI(fuelrowUI(stn,label = "DUMMY",n = i))
fuelrowServer(stn,datarow = s,n = i)
}
##### Value box helper functions ##########
vblevel <- function(n = 1,k=1){
val <- simdata[[n]][k,round(fuel,0)]
valueBox(value = paste(val,"L"),
subtitle = tags$h4(cities[n]),
color = case_when(
val < 1000 ~ "red",
val >= 1000 ~ "green"
))
}
vbout <- function(n = 1,k=1){
val = simdata[[n]][k,out]
valueBox(value = paste(val,"L"),
subtitle = tags$h4(cities[n]),
color = case_when(
val < 100 ~ "aqua",
val >= 100 ~ "purple"
))
}
vbin <- function(n = 1,k=1){
val = simdata[[n]][k,fill]
valueBox(value = paste(val,"L"),
subtitle = tags$h4(cities[n]),
color = case_when(
val < 100 ~ "teal",
val >= 100 ~ "olive"
))
}
# Time Value box
vbtime <- function(n = 1,k = 1){
time <-simdata[[n]][k,ts]
timestr <- format(time,"%H:%M")
valueBox(value = timestr,
subtitle = "Last Updated",color = "aqua")
}
Please load the three code sections in three files: ui.R, server.R and modules.R.
Note: In the modules.R the first line has a line N <- 4. Please set it to 20 to see the annoying flicker.
If you only want to stop the flashing while recalculating all you'll have to do is adding
tags$style(".recalculating { opacity: inherit !important; }")
to your UI - taken from here.
Still I'd encourage you to simplify your app for better performance.
Here is an example for the approach I mentioned in the comments:
library(shiny)
library(shinydashboard)
library(data.table)
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(),
dashboardBody(
tags$style(".recalculating { opacity: inherit !important; }"),
fluidPage(
sliderInput(
inputId = "nBoxesRows",
label = "rows of Boxes",
min = 1L,
max = 100L,
value = 20L
),
uiOutput("myValueBoxes")
)
)
)
server <- function(input, output, session) {
DT <- reactive({
invalidateLater(1000)
data.table(replicate(4, round(runif(input$nBoxesRows), digits = 2)))
})
output$myValueBoxes <- renderUI({
longDT <- melt(DT(), measure.vars = names(DT()))
longDT[, subtitle := paste0(variable, "_", seq_len(.N)), by = variable]
tagList(mapply(valueBox, subtitle = longDT$subtitle, value = longDT$value, MoreArgs = list(width = 3), SIMPLIFY = FALSE))
})
}
shinyApp(ui, server)

How to prevent JS code to run multiple times in R Shiny?

I try to use a workaround for the highcharter package to update the chart and not to rerender it which looks much smoother. So far, my functions works fine as long as I run the code in a seperate JS file. But to make it more flexible I want to write with function with R. When I click the input$data button, the code seems to run as many times as the value input$data has got (see the print statement). Why is this happening and what can I do to prevent this issue?
library(highcharter)
library(shiny)
library(shinyjs)
df <- data.frame(
a = floor(runif(10, min = 1, max = 10)),
b = floor(runif(10, min = 1, max = 10))
)
updaterfunction <- function() {
jscode <-
'$("#data").on("click",function() {
console.log("code was run")
Shiny.addCustomMessageHandler("handler1", function(message1){
var chart1 = $("#plot").highcharts()
var newArray1 = new Array(message1.length)
var newArray2 = new Array(message1.length)
for(var i in message1) {
newArray1[i] = message1[i].a
newArray2[i] = message1[i].b
}
chart1.series[0].update({
// type: "line",
data: newArray1
}, false)
chart1.series[1].update({
// type: "line",
data: newArray2
}, false)
chart1.redraw();
})
});'
runjs(jscode)
}
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
#includeScript("www/script.js"),
useShinyjs(),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30),
actionButton("data2", "Generate Data"),
actionButton("data", "Generate Data")
),
# Show a plot of the generated distribution
mainPanel(
highchartOutput("plot"),
highchartOutput("plot2")
)
)
)
server <- function(input, output, session) {
observeEvent(input$data, {
print(input$data)
df <- data.frame(
a = floor(runif(10, min = 1, max = 10)),
b = floor(runif(10, min = 1, max = 10))
)
message1 = jsonlite::toJSON(df)
session$sendCustomMessage("handler1", message1)
updaterfunction()
})
reactivedata <- eventReactive(input$data2, {
df <- data.frame(
a = floor(runif(10, min = 1, max = 10)),
b = floor(runif(10, min = 1, max = 10))
)
})
output$plot <- renderHighchart({
highchart() %>%
hc_add_series(type = "bar", data = df$a) %>%
hc_add_series(type = "bar", data = df$b)
})
output$plot2 <- renderHighchart({
highchart() %>%
hc_add_series(type = "bar", data = reactivedata()$a) %>%
hc_add_series(type = "bar", data = reactivedata()$b)
})
}
# Run the application
shinyApp(ui = ui, server = server)
That's because each time you run the JS code, it attaches a new click event to the button. You can use off("click") to remove the previous event handler:
jscode <-
'$("#data").off("click").on("click",function() {
But I'm not sure this produces the expected behaviour. Is it ?

R Shiny animation scatterplot speed performance

I want to make an animation in R Shiny where my scatter plot is progressively updated at each iteration, here is my current plot
library(shiny)
library(plotly)
ui <- fluidPage(
titlePanel(""),
sidebarLayout(
sidebarPanel(
actionButton("launch", "Launch Simulation"),
radioButtons("display","Show every iteration", selected = 10,
choices = c(1,5,10,50),inline = FALSE),
numericInput("iter","Maximum number of iterations", value = 2000,
min = 500,max = 5000, step = 500)
),
mainPanel(
plotlyOutput('plot')
)
)
)
server <- function(input, output) {
rv <- reactiveValues(i = 0,
df = data.frame(x = -1,y = -1))
observeEvent(input$launch,{
rv$i = 0
rv$df = data.frame(x = runif(5000, min = -1,max = 1),
y = runif(5000, min = -1,max = 1))
})
observe({
isolate({
rv$i = rv$i + as.numeric(input$display)
})
if ((rv$i < input$iter)&input$launch){
invalidateLater(0)
}
})
output$plot <- renderPlotly({
df = data.frame(x = 0,y = -1)
df = rbind(df,rv$df)
plot_ly(df[1:(rv$i + 1),], x = ~x, y = ~y,
type = 'scatter', mode = 'markers',
marker = list(size = 4), hoverinfo="none") %>%
layout(showlegend = FALSE)
})
}
shinyApp(ui = ui, server = server)
The code is working fine at the beginning but after around 1000 iterations, the animation becomes very slow. I think the main problem is that because in my code, I have to re-make the plot all over again at each iteration, is there a smoother way to do what I want to do?
(Not necessarily with Plotly but it is important to me that I keep track of the number of the iterations outside of the plot (here rv$i))

Format table output in R shiny based on user inputs

I have a table being display in a shiny app. I want to format the tables based on the values and color it accordingly. I have seen the formattable area coloring where based on the range of the values it defines the breaks and then color gradients are generated which are applied to the table. What I want to do is allow the user to fill the min and max value and depending on it the values in the table will be colored. So if the values range from 1-20 and if the user inputs are 5 and 15 , values below 5 and above 15 shouldnt have any color gradients applied to them. Below is the code of how I am doing currently using formatable area formatting.
library(shiny)
library(shinyWidgets)
library(shinydashboard)
library(DT)
sidebar <- dashboardSidebar(
sidebarMenu(id = "tab",
menuItem("1", tabName = "1")
)
)
body <- ## Body content
dashboardBody(box(width = 12,fluidRow(
fluidRow( column(
width = 3, textInput("text1", label = h5("Min"), value = "Enter min")),
column(
width = 3, textInput("text2", label = h5("Max"), value = "Enter max"))),
DT::dataTableOutput("op")
)))
ui <- dashboardPage(dashboardHeader(title = "Scorecard"),
sidebar,
body)
# Define the server code
server <- function(input, output,session) {
df <- data.frame(month = c("mazda 3", "mazda cx5", "mazda 6","mazda miata","honda civic","honda accord"),
april = c(.1,.2,.3,.3,.4,.5),
may = c(.3,.4,.5,.2,.1,.5),
june = c(.2,.1,.5,.1,.2,.3))
brks <- reactive({ quantile(df$april, probs = seq(.05, .95, .05), na.rm = TRUE)})
clrs <- reactive({ round(seq(255, 175, length.out = length(brks()) + 1), 0) %>%
{paste0("rgb(",.,",", ., ",255 )")}})
df_format<- reactive ({datatable(df,options = list(searching = FALSE,pageLength = 15, lengthChange = FALSE))%>%
formatStyle(names(df),backgroundColor = styleInterval(brks(), clrs()))})
output$op <-renderDataTable({
df_format()
})
}
shinyApp(ui = ui, server = server)
Here is your working code.
You must use that input minimal and maximal value as limits for your sequence (I just change it to range - is easier for user to put a range like that)
Then you generate sequence - according your notation - brks() - in my case I use length.out of 10 but you can put as many breaks as you want or dynamically.
Then generate on
number of colors - 1
and in the end in styleInterval() for background add limits of white - or any other color you want.
library(shiny)
library(shinyWidgets)
library(shinydashboard)
library(DT)
sidebar <- dashboardSidebar(
sidebarMenu(id = "tab",
menuItem("1", tabName = "1")
)
)
body <- ## Body content
dashboardBody(box(width = 12,fluidRow(
fluidRow(column(
width = 3,
sliderInput("range_value",
label = h3("Put a range value"),
min = 0,
max = 100,
value = c(5, 15)
)
)
),
DT::dataTableOutput("op")
)))
ui <- dashboardPage(dashboardHeader(title = "Scorecard"),
sidebar,
body)
# Define the server code
server <- function(input, output,session) {
df <- data.frame(month = c("mazda 3", "mazda cx5", "mazda 6","mazda miata","honda
civic","honda accord"),
april = c(9, 8, 11,14,16,1),
may = c(3,4,15,12,11, 19),
june = c(2,11,9,7,14,1))
brks <- reactive({
seq(input$range_value[1], input$range_value[2], length.out = 10)
})
clrs <- reactive({ round(seq(255, 175, length.out = length(brks()) - 1), 0) %>%
{paste0("rgb(",.,",", ., ",255)")}})
df_format<- reactive ({datatable(df,options = list(searching = FALSE, pageLength = 15, lengthChange = FALSE)) %>%
formatStyle(names(df),
backgroundColor = styleInterval(c(brks()), c('white', clrs() ,'white'))
)
})
output$op <-renderDataTable({
df_format()
})
}
shinyApp(ui = ui, server = server)

Resources