Keep a running value in Shiny - shiny
I am an environmental scientist trying to create a harvest simulation for oysters. I want the simulation to display two maps, one for showing the current oyster populations, and one for showing no-take zones (sanctuaries). Clicking on a plot on the map (each one has an invisible marker) should do something different depending on which map is showing. Clicking with the oyster population map should cause the population to update with oyster harvest occurring in the clicked plot. Clicking on the sanctuary map should cause the clicked plot to change is designation of open or closed.
The issue, as far as I can tell, is that all of these values reset every time an input is clicked. For example, it does not matter which map was showing previously, the "showSanctuary" variable, which is supposed to display F is the population map is up and T if the sanctuary map is up, is always set to False, its starting value, whenever a new input is clicked. The oyster population vector and sanctuary vector appear to do the same thing. How can I prevent these variables from resetting back to their starting values?
Also, I am very new to this forum, and I am not sure of the etiquette. I am going to post all of my code below absent the two scripts where I store my functions (I am certain they are not the issue), but it is a fairly lengthy program. This question relates to code between lines 70 and 152. I will post only those lines first, with the full script below it. Sorry again if that is not typical etiquette.
Lines where problem is occurring (this is inside the Server function):
#Make Reactive Values
offLim <- reactiveValues()
offLim = 0
showSanctuary <- reactiveValues()
showSanctuary = F
myOutputs <- reactiveValues()
myOutputs$outHarvTime = 0
myOutputs$outSacksTaken = 0
myOutputs$outAvgSize = 0
#React to click event
observeEvent(input$map_marker_click, {
click<-input$map_marker_click
if(is.null(click))
return()
xClk=trunc(click$lng*1000)/1000
yClk=trunc(click$lat*1000)/1000
xCor = which(coord$x == xClk)
yCor = which(coord$y == yClk)
myPlot = intersect(xCor, yCor)
if(showSanctuary == T){
if(offLim[myPlot]==1){
offLim[myPlot]=0
}else if(offLim[myPlot]==0){
offLim[myPlot]=1
}
myMap <- makeSanctuaryMap(offLim)
myOutputs$finalMap = myMap
}
else{
myHarvLim = input$amount
myMaxTime = input$effort
myMinSize = input$size
returnShells = input$shell
param = c(myHarvLim, myMaxTime, myMinSize, myPlot, returnShells)
newOysters = oysters
newDens = myDens
newShell = myShell
outVar<-localUpdate(param, newOysters, myMaxDens, newDens, newShell, nplts)
oysters = outVar$oysters
myDens = outVar$myDens
myShell = outVar$myShell
myOutputs$outAvgSize = outVar$avgSize
myOutputs$outHarvTime = outVar$harvTime
myOutputs$outSacksTaken = outVar$sacksTaken
myOutputs$finalMap = outVar$myMap
}
})
observeEvent(input$update,{
#Make sanctuary variables. Needs to be reactive to be global
showSanctuary = F #Whether the Map Currently Displays Sanctuary Areas
offLim = vector(length=nplts) #1 if plot is a Sanctuary or 0 if not
offLim[] = 0
offLim[!cond]<-NA
myOutputs$outAvgSize = NA
myOutputs$outHarvTime = NA
myOutputs$outSacksTaken = NA
myOutputs$finalMap = myMap
})
observeEvent(input$sanctuaryMap,{
print(showSanctuary)
showSanctuary = T
myMap <- makeSanctuaryMap(offLim)
myOutputs$finalMap = myMap
})
observeEvent(input$harvestMap,{
print(showSanctuary)
showSanctuary = F
myMap <- updateMap(heatVec)
myOutputs$finalMap = myMap
})
Full Script:
library(shiny)
library('leaflet')
library(raster)
library('sf')
library(rgdal)
source('updateFunctions.R')
effortLbl = "What is the maximum number of hours that you are willing to spend harvesting each day?"
amountLbl = "What is the maximum number of sacks of oysters you would harvest in one day?"
sizeLbl = "Select a minimum size for legal harvest (inches)"
shellLbl="Check to require culling on site"
ui<-fluidPage(
numericInput(inputId="effort", label=effortLbl, value=8, min=1, max=16, step=1),
numericInput(inputId="amount", label=amountLbl, value=4, min=1, max=40, step=1),
numericInput(inputId="size", label=sizeLbl, value=3, min=1, max=6, step=1),
checkboxInput(inputId="shell", label=shellLbl, value = FALSE),
actionButton(inputId="update", label="Begin"),
actionButton(inputId="sanctuaryMap", label="Set Sanctuaries"),
actionButton(inputId="harvestMap", label="Choose Harvest Area"),
leafletOutput("map"),
textOutput("time"),
textOutput("sacks"),
textOutput("size")
)
server<-function(input, output, session){
#Generate list of clickable coordinates
cedKey <- readOGR(dsn=path.expand("shapefile"), layer="LC_10_Area") #Imports Cedar Key shape file
ckCrd <- spTransform(cedKey, CRSobj = CRS("+init=epsg:4326")) #Converts shape file coordinate to longitude/latitude
matCrd=expand.grid(x=seq(from=-83.1164,to=-83.06251,length.out=moveRow), #Generates a series of coordinates within range
y=seq(from=29.2169,to=29.26528,length.out=moveRow))
df = data.frame(x = matCrd$x, y = matCrd$y)
s = SpatialPixelsDataFrame(df[,c('x', 'y')], data = df, proj4string = crs(ckCrd))
clp <- over(s[,c("x", "y")], ckCrd)
cond <- !is.na(clp$Id)
spNew<-s[cond,]
spDf = as.data.frame(spNew)
coord = data.frame(x=(trunc(df$x*1000)/1000), y=(trunc(df$y*1000)/1000))
#Initialize oyster population variables
nplts = 1600 #Total number of plots
nsize = 7 #Number of size classes (including larva)
oysters = matrix(0, nplts,nsize)
myDens = vector(length=nplts) #Total number of oysters weighted by size
myShell = vector(length=nplts) #The amount of dead shell (or other non-living hard substrate)
myMaxDens = 1000 #The maximum capacity of every plot
moveRow = sqrt(nplts) #The number of plots in a row
#Initialize oyster population with randomization
for(i in 1:nplts){
initMin = c(20,20,5,5,0,0) #Minimum number of oysters of each size at game start
initMax = c(60,40,30,20,10,5) #Maximum number of oysters of each size at game start
oysters[i,1:6]=runif(6, initMin, initMax)
oysters[i,7]=sum(oysters[i,1:6]*4)
}
oysters[!cond,]<-NA
for(i in 1:nplts){
myDens[i]=sum(oysters[i,1:6]*c(1:6))
myShell[i]=0.2*myDens[i]
}
#Set values of outputs before initial update
heatVec = vector(length=nplts)
for(i in 1:nplts){
heatVec[i] = (100*myDens[i])/myMaxDens
}
myMap = updateMap(heatVec)
#Make Reactive Values
offLim <- reactiveValues()
offLim = 0
showSanctuary <- reactiveValues()
showSanctuary = F
myOutputs <- reactiveValues()
myOutputs$outHarvTime = 0
myOutputs$outSacksTaken = 0
myOutputs$outAvgSize = 0
#React to click event
observeEvent(input$map_marker_click, {
click<-input$map_marker_click
if(is.null(click))
return()
xClk=trunc(click$lng*1000)/1000
yClk=trunc(click$lat*1000)/1000
xCor = which(coord$x == xClk)
yCor = which(coord$y == yClk)
myPlot = intersect(xCor, yCor)
if(showSanctuary == T){
if(offLim[myPlot]==1){
offLim[myPlot]=0
}else if(offLim[myPlot]==0){
offLim[myPlot]=1
}
myMap <- makeSanctuaryMap(offLim)
myOutputs$finalMap = myMap
}
else{
myHarvLim = input$amount
myMaxTime = input$effort
myMinSize = input$size
returnShells = input$shell
param = c(myHarvLim, myMaxTime, myMinSize, myPlot, returnShells)
newOysters = oysters
newDens = myDens
newShell = myShell
outVar<-localUpdate(param, newOysters, myMaxDens, newDens, newShell, nplts)
oysters = outVar$oysters
myDens = outVar$myDens
myShell = outVar$myShell
myOutputs$outAvgSize = outVar$avgSize
myOutputs$outHarvTime = outVar$harvTime
myOutputs$outSacksTaken = outVar$sacksTaken
myOutputs$finalMap = outVar$myMap
}
})
observeEvent(input$update,{
#Make sanctuary variables. Needs to be reactive to be global
showSanctuary = F #Whether the Map Currently Displays Sanctuary Areas
offLim = vector(length=nplts) #1 if plot is a Sanctuary or 0 if not
offLim[] = 0
offLim[!cond]<-NA
myOutputs$outAvgSize = NA
myOutputs$outHarvTime = NA
myOutputs$outSacksTaken = NA
myOutputs$finalMap = myMap
})
observeEvent(input$sanctuaryMap,{
print(showSanctuary)
showSanctuary = T
myMap <- makeSanctuaryMap(offLim)
myOutputs$finalMap = myMap
})
observeEvent(input$harvestMap,{
print(showSanctuary)
showSanctuary = F
myMap <- updateMap(heatVec)
myOutputs$finalMap = myMap
})
localUpdate <- function(param, locOyster, myMaxDens, locDens, locShell, nplts){
myUpdate<-updateFunction(locOyster, myMaxDens, locDens, locShell, param, nplts) #All updates done in separate script
#Set oyster pop, dens, and dead shell according to updates
oysters = myUpdate$oysters
for(i in 1:nplts){
myDens[i] = sum(oysters[i,1:6]*c(1:6))
}
myShell = myUpdate$shell
#Calculate heatmap values based on density (biomass)
for(i in 1:nplts){
heatVec[i] = (100*myDens[i])/myMaxDens
}
myMap <- updateMap(heatVec) #Create Map
return(list(avgSize = myUpdate$avgSize, harvTime = myUpdate$harvTime, sacksTaken = myUpdate$sacksTaken,
myMap = myMap, myShell = myShell, myDens = myDens, oysters = oysters))
}
#Assemble and Display Outputs
output$map <- renderLeaflet({
input$map_marker_click #Makes output dependent on map or button click (via isolate)
input$sanctuaryMap
input$harvestMap
input$update
isolate(myOutputs$finalMap)})
output$time <- renderText({
input$map_marker_click
input$update
timeString <- isolate(c("Time Spent Harvesting Each Day: ",
toString(trunc(myOutputs$outHarvTime*100)/100), " hours"))
timeString})
output$sacks <- renderText({
input$map_marker_click
input$update
sacksString<-isolate(c("Average Number of Sacks Harvested per Day: ",
toString(trunc(myOutputs$outSacksTaken*100)/100), " sacks"))
sacksString})
output$size <- renderText({
input$map_marker_click
input$update
sizeString<-isolate(c("Average Size of Harvested Oysters this Year: ",
toString(trunc(myOutputs$outAvgSize*100)/100), " inches"))
sizeString})
}
shinyApp(ui = ui, server = server)
For starters, you aren't using reactiveValues correctly. It would be something like this:
my_reactives <- reactiveValues()
my_reactives$offLim <- 0
my_reactives$showSanctuary <- F
Related
ShinyApp - reactive inferno
I have two input variables, and changing one will cause the change of the other one. Further to that, if the value of input is outside limits it should default to min (if below) or max (if above) value. All works fine as long as up and down arrows are being used. The moment I am typing value 1 in Input1 it goes crazy. Same if I am deleting Input2, even before I am typing anything... I am aware that it must have something to do with reactive values, but can not fix it... Any suggestion will be very much appreciated! library(shiny) ui <- fluidPage( fluidRow( uiOutput("Input1"), numericInput("Input2", "Input 2", min = 50, max = 150, value = 100, step = 1)), tableOutput("result") ) ######################################################### server <- function(input, output, session) { global <- reactiveValues(numVal = 10, numMin = 5, numMax = 15) numVal <- reactive({ if(!is.null(input$Input1)){ if(input$Input1 < global$numMin) return(global$numMin) if(input$Input1 > global$numMax) return(global$numMax) return(input$Input1) }else{ return(global$numVal) } }) output$Input1 <- renderUI(numericInput("Input1", "Input 1", min = global$numMin, max = global$numMax, value = numVal(), step = 0.1)) # when Input1 change, update Input2 observeEvent(input$Input1, { updateNumericInput(session = session, "Input2", value = format(round(input$Input1*10, 0), nsmall = 0)) }) # when Input2 change, update Input1 observeEvent(input$Input2, { updateNumericInput(session = session, "Input1", value = format(round(input$Input2*0.1, 1), nsmall = 1)) }) inputdata <- reactive({ data <- data.frame(Coef = as.numeric(input$Input1)) data }) output$result <- renderTable({ data = inputdata() resultTable = as.character(round((data$Coef + 10)*100, digits=2)) resultTable }) } ######################################################### shinyApp(ui, server)
You are on the brink of getting into a race condition: Input 1 changes Input 2 changes Input 1 changes Input 2... So foremost you should reconsider your design. You can use debounce / throttle to avoid some of the race consition by telling Shiny not too fire too quickly and as the updates are bijective you may achieve what you want, but I would really think about your design b/c these circle dependencies are almost never a good idea. Having said that here is a solution which behaves better (N.B. I removed the dynamic rendering of the second input element as it has nothing to do wiht the problem at hand). It is not perfect, b/c you will eventually end up in a racing condition, but you can soften this situation by playing w/ the debouncing factors. library(shiny) ui <- fluidPage( fluidRow( numericInput("Input1", "Input 1", min = 5, max = 15, value = 10, step = .1), numericInput("Input2", "Input 2", min = 50, max = 150, value = 100, step = 1)), tableOutput("result") ) server <- function(input, output, session) { ## debounce both input, i.e. they are firing onyl if no change within 1sec happens ## c.f. ?debounce getI1 <- reactive(input$Input1) %>% debounce(1000) getI2 <- reactive(input$Input2) %>% debounce(1000) observeEvent(input$Input1, { updateNumericInput(session = session, "Input2", value = format(round(getI1() * 10, 0), nsmall = 0)) }) observeEvent(input$Input2, { updateNumericInput(session = session, "Input1", value = format(round(getI2() * 0.1, 1), nsmall = 1)) }) inputdata <- reactive({ data <- data.frame(Coef = as.numeric(input$Input1)) data }) output$result <- renderTable({ data = inputdata() resultTable = as.character(round((data$Coef + 10)*100, digits=2)) resultTable }) } shinyApp(ui, server)
How can I get a table to print under a picture using a loop in a .rmd with word_document?
I am trying to create a .rmd file that takes all of the pictures for a field day and the notes that was taken and create a report. I am able to get the pictures to plot but the no matter what I try the table with the notes does not want to print. Below is the loop I am utilizing: for(i in 1:nrow(subset_Inventory_data)) { singlept <- subset_Inventory_data[i,] picture <- pictureLookup[singlept$GlobalID == pictureLookup$REL_GLOBAL,] #PRINT PICTURE plot(image_read(paste(baseURL,picture$UID,sep = "")) %>% # image_resize("400x400") %>% image_rotate(degrees = 90) ) #creating table underneath picture Categories <- c("Latitude", "Longitude", "Road Width", "Conditon", "Lock Present","Additional Notes") sum_table <- data.frame(Category = character(), Information = character(), stringsAsFactors = FALSE) sum_table <- rbind(sum_table,Categories, stringsAsFactors = FALSE) colnames(sum_table) <- Categories sum_table$Latitude <- sprintf("%f",singlept$LAT) sum_table$Longitude <-sprintf("%f",singlept$LONG) sum_table$`Road Width` <- paste(singlept$Gate_Width,"feet") sum_table$Conditon <- singlept$Condition sum_table$`Lock Present` <- singlept$GlobalID sum_table$`Additional Notes` <- singlept$General_Notes #TRIED FLEXTABLE ft <- flextable(sum_table) ft <- fontsize(ft, size = 12) ft <- autofit(ft) print(ft) #TRIED KABLE print(kable(sum_table,"latex")) }
Simple Shiny selectInput not working with Intersect
Is there any reason this wouldn't work? I simply want to see which terms are found in the two selected columns. I figured intersect would do the job, but I'm not seeing results. If this looks alright, perhaps I have some other syntax error along the way? Do the inputs need to be in different sidebar panels? selectInput("data1", "Choose you Input:", choices = colnames(data), selected = "PD.Risk.Factor"), selectInput("data2", "Choose you Input:", choices = colnames(data), selected = "AD.Risk.Factor")), Output: p2 = intersect(x = input$data1, y = input$data2) print(p2)
Welcome to SO! Please provide a reprex the next time - this will help to get help. For our problem. What your snippet does is to compare not the columns of your data frame but the the strings as returned by selectInput. What you want to do is to use these strings to retrieve the corresponding columns in the data. library(shiny) sample_dat <- data.frame(x = 1:10, y = 5:14, z = 9:18) ui <- fluidPage(selectInput("col1", "Column 1:", names(sample_dat), "x"), selectInput("col2", "Column 1:", names(sample_dat), "y"), verbatimTextOutput("result")) server <- function(input, output, session) { output$result <- renderPrint({ list(on_strings = list(col1 = input$col1, col2 = input$col2, intersect = intersect(input$col1, input$col2)), on_cols = list(col1 = input$col1, col2 = input$col2, intersect = intersect(sample_dat[[input$col1]], sample_dat[[input$col2]]))) }) } shinyApp(ui, server)
Changing pallete values based on input (Shiny & leaflet)
I'm using leaflet and shiny. I would like to color my markers based on a column that can be changed via input. It's almost the same as Modifying Existing Maps with leafletProxy. In this example, the user can change the color palette. In my example, I would like to change the column that the palette is applied on. I'm trying to use something like: fillColor = ~pal(!!sym(input$col_to_apply)) # input$col_to_apply is a column name (string) I want to use However, this doesn't work. I'm also not sure if I have to use reactive() in this case.
Sure. My suggestion would be to do it before the palette is created. Palettes are tricky enough as it is. See the below minimal example: library(leaflet) library(maps) library(shiny) ui <- fluidPage( leafletOutput("map_1"), selectInput(inputId = "input_species", label = "Species Selection", choices = c("Species_1", "Species_2", "Species_3")) ) server <- function(input, output, session) { #Load a map of the US from the 'map' package (runs once when apps starts) shp_map = map("state", fill = TRUE, plot = FALSE) #Make up a dataframe with some data for three species for each state (runs once when apps starts) df_data <- data.frame(state = unique(shp_map$names), Species_1 = sample(100:199, 63), Species_2 = sample(200:299, 63), Species_3 = sample(300:399, 63)) #Create map output$map_1 <- renderLeaflet({ df_map <- df_data #Create a column called species selected based on which is selected in the dropdown box df_map$Species_Selected <- df_map[, paste(input$input_species)] #Create a palette function palette <- colorNumeric(palette = "Blues", domain = df_map$Species_Selected) #Use the palette function created above to add the appropriate RGB value to our dataframe df_map$color <- palette(df_map$Species_Selected) #Create map map_1 <- leaflet(data = shp_map) %>% addPolygons(fillColor = df_map$color, fillOpacity = 1, weight = 1, color = "#000000", popup = paste(sep = "", "<b>", paste(shp_map$names), " ", "</b><br>", df_map$Species_Selected)) map_1 }) } shinyApp(ui, server)
I want a line graph with mean and SD like
I want graph as like as the picture but don't understand how to do this. Actually a have different data for same date how do i made its a line. library(tidyverse) library(data.table) fish <- read_csv(file = "FishF.csv", col_types = cols(DD= col_factor())) fish1<-fish[is.na(fish)] <- 0 fish View(fish) fd<-fish[,c(1,2,3)] 1 newfish <- melt(setDT(fish), id.vars = "DD", measure.vars = patterns("avg","SE"), value.name = c("avg","SE"))[ , variable := lvls_revalue(variable, c("C3", "IgM", "IgT", "KHV", "Lyso"))][] n<- melt(setDT(fd), id.vars = "DD", measure.vars = patterns("avg","SE"), value.name = c("avg","SE"))[ , variable := lvls_revalue(variable, c("C3"))][]