Map generated with ggplot2 not showing properly in shiny app - r

I developed app to support my hydrological modelling teaching. In it, students can select a gauging station to work with. In order to enhance the amount of information the students get from the app, I decided to update it including a map that shows the station and its associated catchment on top of a national map, together with a more detailed map like this:
Desired output
This is the code I am using (simplified to represent just my problem):
station_data <- read.csv("station_timeseries.csv",na.strings = "NaN",fileEncoding="UTF-8-BOM")
station_data$date <- as.Date(station_data$date,"%d/%m/%Y")
station_lat <- read.csv("station_coord.csv",fileEncoding="UTF-8-BOM")
GB_boundary <- st_read("./maps/GB_boundary.shp")
catchment_boundary <- st_read("./maps/Catchments.shp")
ui <- shinyUI(fluidPage(
sidebarPanel(
# Select station
selectInput(inputId = "station", label = strong("Select station"),
choices = unique(station_data$gauge_name))
),
tabPanel("Catchment characteristics",plotOutput(outputId = "Map"))
)
)
server <- shinyServer(function(input, output) {
## Catchment and station map ----
output$Map <- renderPlot({
station <- station_lat[station_lat$gauge_name==input$Station,]$gauge_id
bbox <- st_bbox(catchment_boundary[catchment_boundary$ID==station,])
large_map <- ggplot()+
geom_sf(data=GB_boundary,size=1,color="black",fill="cyan1")+
geom_sf(data=catchment_boundary[catchment_boundary$ID==station,],size=1,color="black",fill="red")+
theme_bw()
detail_map <- ggplot()+
geom_sf(data=GB_boundary,size=1,color="black",fill="cyan1")+
geom_sf(data=catchment_boundary[catchment_boundary$ID==station,],size=1,color="black",fill="red")+
coord_sf(xlim=c(bbox[1]-20000,bbox[3]+20000),ylim = c(bbox[2]-20000,bbox[4]+20000),expand = FALSE)+
ggtitle("Catchment and station location")+
theme_bw()
grid.arrange(large_map,
detail_map,
ncol = 2,
widths = c(2,5))
})
})
shinyApp(ui = ui, server = server)
I used the ggplot2 code you can see to generate the above image. However, when running the shiny app I get this:
Actual output
It seems to me like shiny only reads the first geom_sf command and disregarding the next two plots. Does anyone have experienced something similar? Any suggestions to solve this?
Edit: I added links to the data used in this example, stations and both maps.
Cheers!
David

Related

R Shiny: "Reactive" data analysis based on user input, followed by ggplot figure output

I have a basic R code where, within the code, a user can enter a country name "Argentina". Using that value/name, the code will run an analysis for the "Argentina" subset of the pre-loaded data. Finally, the code will produce a simple ggplot showing results.
I have tried to make this code into a Shiny App, however I cannot get it to work properly. My main issue is that I cannot seem to get the data analysis in the Server section to work, which should subsequently feed into the plotting code. More importantly, I cannot seem to get the user inputted country name to feed into my data analysis.
Without going into the detail of the code, could someone kindly point me in the right direction of how one would do this in Shiny? e.g.
Field for user input;
Use that user input as an object used in the code;
subsequently run the analysis (whatever it might be); and
use the final analysis data frame in ggplot for a figure output to be displayed in the shiny app.
Please see my shiny code currently used, with reproducible data using MTcars
library(shiny)
# Some Sample data to run app using mtcars
mtcars$Primary<- rownames(mtcars)
mtcars$Area <- "Argentina"
mtcars$Y2016<- mtcars$mpg
mtcars$Element <- "Gross Production Value (constant 2004-2006 million US$)"
# Defining UI ----
ui <- pageWithSidebar(
# App title ----
headerPanel("Subsector Selection Tool"),
# Sidebar panel for inputs ----
sidebarPanel(
# Input: Country name
textInput("country", "Please enter country name", "")#,
),
# Main panel for displaying outputs ----
mainPanel("")
)
# Define server logic to plot various variables against mpg ----
server <- function(input, output) {
#Trying to make user inputed country name into an object to be used in
"reactive" code below, which in turn is be used to make dataset for graphing
country_interest <- reactive({
paste(input$country)
})
#Here I am trying to make the data analysis code run and create desired
dataset for graphing, and subsetting for country selected by user
Value_c_e_PRIM_x <- reactive({
Value_c <- Value[which(Value$Area==country_interest),]
Value_c_e <- Value_c[which(Value_c$Element=="Gross Production Value (constant 2004-2006 million US$)"),]
Value_c_e_PRIM$Primary <- Value_c_e_PRIM[,120]
Value_c_e_PRIM[,120] <- NULL
Value_c_e_PRIM <- Value_c_e_PRIM %>% group_by(Primary,Element) %>% summarise_at(vars(Y2016), sum)
Value_c_e_PRIM$Category <- "Value of Production"
Value_c_e_PRIM$Value <- Value_c_e_PRIM$Y2016
Value_c_e_PRIM <- Value_c_e_PRIM %>% group_by(Category,Primary) %>% summarise_at(vars(Value), mean)
})
#Graphing section, if Ihave the dataset "Value_c_e_PRIM_x" pre-loaded (e.g. not derived in code above), the figure is successfully shown in the output.
output$plot <- renderPlot({
Graph_data <- Value_c_e_PRIM_x
Graph_data$Score_type <- "Competitiveness Score"
Graph_data$`Competitiveness Score` <- round(Graph_data$Value, 2)
title1 <-paste("Competitiveness\nby",paste0(country_interest),"Subsector")
mycol <-c("red", "yellow", "#006600")
ggplot(data = Graph_data, aes(x = Score_type, y = reorder(Primary,Value), fill=Value)) +
geom_tile(aes(fill = Value), colour= "white")+
geom_text(data=Graph_data,aes(y=Primary, x= Score_type, label=Value))+
labs(title =(paste0(title1)),y = "", x = "")+
scale_fill_gradientn(colours = mycol)+
theme(legend.title=element_blank())+
theme(legend.position="bottom")
})
}
shinyApp(ui, server)

How to avoid widening of plot illustration when using plotly instead of ggplot

I'm plotting a bubble map in R Shiny which displays the sales volume of different city locations (called 'Stadt' in the data) in Germany. Therefore, I basically use a German default map (.rds) and complement it with plot layers of the bubbles.
Instead of ggplot I would like to use plotly, since it has more default features which are very useful for arranging the displayed information on the map.
However, when I apply the plotly function, the plot shows a very widely deformed country shape. The bubbles and the map legend displayed on the side work fine.
Here is the ggplot with the correct shape: https://ibb.co/dL4HTRG
Here is the deformed output of plotly: https://ibb.co/bWR2XP2
I'm aware that it is possible to fix height and size by the corresponding commands inside the ggplot brackets, but this does not lead to the required result in this case.
The map should adapt to the window size with maintaining its correct width-to-height ratio.
Is there another possibility to solve this except of fixing manually size and width by trial and error?
The basic code looks like the following:
library(shiny)
library(tidyverse)
library(ggplot2)
library(plotly)
##pool package necessary for dbpool function in order to access database via function in 'database_init.R'
library(pool)
### load and preprocess the German geo map polygons. Download here http://biogeo.ucdavis.edu/data/gadm2.8/rds/DEU_adm1.rds
map_data_sp2 <- readRDS("gadm36_DEU_2_sp.rds")
map_data_1 <- fortify(map_data_sp2, region = "NAME_1")
# link to database and declaration as 'pool'
pool <- dbPool(
drv = RMySQL::MySQL(),
dbname = "rlyshiny_",
host = "85.214.74.4",
username = "xxxxxxxx",
password = "xxxxxxxxxxxxxxxx")
ui <- dashboardPage(
dashboardHeader(),
dashboardSidebar(
sidebarPanel()
),
dashboardBody(
plotlyOutput("heatmap")
)
)
server <- function(input, output, session) {
output$heatmap <- renderPlotly({
locations <- dbReadTable(pool,"Locations")
##tbl extrahiert salesdata aus pool
sales_data <- pool%>%
tbl("Salesdata")%>%
as.data.frame()%>%
merge(locations, by="Stadt")%>%
select(Material,Nettogewicht,Stadt,Longitude,Latitude)%>%
group_by(Material,Stadt,Longitude,Latitude)%>%
summarise(Nettogewicht=sum(Nettogewicht))
ggplot() + geom_polygon(data=map_data_1,aes(x=long,y=lat,group=group),fill="grey",color="white")+
geom_point(data=sales_data,aes(x=Longitude,y=Latitude, size=Nettogewicht,colour=Stadt),alpha=0.7)+
scale_size(range = c(2,20))+
theme_void() +
coord_map()
}
)
}
shinyApp(ui, server)

Structure Server Function in a Shiny App

I am struggling with my first shiny app and having problems to make it work due to, I think, I am missing out something in the server function.
I have a R script that has two variables (the two reactive values in shiny) that creates a plot (a histogram) and a csv file with two columns (time and mm).
library(ggplot2)
**Pdmm** <- 125 # numeric input in shiny
**IndiceTorrencial** <- 10 # slider between 8 and 12 in shiny
DuracionAgua <- 24
IntervaloMin <- 60
IntervaloTiempo <- IntervaloMin/60
Intervalos <- DuracionAgua/IntervaloTiempo
t <- seq(1,Intervalos,IntervaloTiempo)
DF <- data.frame(t)
DF$I <- (**Pdmm**/24)*(**IndiceTorrencial**)^(((28^0.1)-(DF$t^0.1))/((28^0.1)-1)) # equation where the reactive values are running
DF$Pacu <- DF$t*DF$I
DF$Pmm <- c(DF$Pacu[1], diff(DF$Pacu, lag = 1))
DF$mm <- c(DF$Pmm[23],DF$Pmm[21],DF$Pmm[19],DF$Pmm[17], DF$Pmm[15],DF$Pmm[13],DF$Pmm[11],DF$Pmm[9],DF$Pmm[7],DF$Pmm[5],DF$Pmm[3],DF$Pmm[1],DF$Pmm[2],DF$Pmm[4],DF$Pmm[6],DF$Pmm[8],DF$Pmm[10],DF$Pmm[12],DF$Pmm[14],DF$Pmm[16],DF$Pmm[18],DF$Pmm[20],DF$Pmm[22],DF$Pmm[24])
ggplot(DF,aes(x=t,y=mm)) + geom_bar(stat = "identity",fill = "dodgerblue",color = "black") + scale_x_continuous(name = "t(h)", breaks = seq(1,24,1)) + scale_y_continuous(name = "mm")
My shiny app has, in its UI, a slider, a numeric input and a plot. It works, I will have time to make something better.
ui <- basicPage(
sliderInput(inputId = "coefTo",
label = h3("Torrentiality Coefficient"),
value = 10, min = 8, max = 12),
numericInput(inputId = "PmmS",
label = h3("Areal Precipitation"),
value = 100),
imageOutput("plot")
)
The problem is in the server function. I think I am sure that I have to use a reactive function in order to execute the code and to yield the results (histogram and csv). Also, to plot the histogram, I have a renderPlot.
server <- function(input, output, session){
data <- reactive({
DuracionAgua <- 24
IntervaloMin <- 60
IntervaloTiempo <- IntervaloMin/60
Intervalos <- DuracionAgua/IntervaloTiempo
t <- seq(1,Intervalos,IntervaloTiempo)
DF <- data.frame(t)
DF$I <- (input$PmmS/24)*(input$coefTo)^(((28^0.1)-(DF$t^0.1))/((28^0.1)-1))
DF$Pacu <- DF$t*DF$I
DF$Pmm <- c(DF$Pacu[1], diff(DF$Pacu, lag = 1))
DF$mm <- c(DF$Pmm[23],DF$Pmm[21],DF$Pmm[19],DF$Pmm[17], DF$Pmm[15],DF$Pmm[13],DF$Pmm[11],DF$Pmm[9],DF$Pmm[7],DF$Pmm[5],DF$Pmm[3],DF$Pmm[1],DF$Pmm[2],DF$Pmm[4],DF$Pmm[6],DF$Pmm[8],DF$Pmm[10],DF$Pmm[12],DF$Pmm[14],DF$Pmm[16],DF$Pmm[18],DF$Pmm[20],DF$Pmm[22],DF$Pmm[24])
DFm <- DF$mm
return(DFm)
})
output$plot <- renderPlot({
ggplot(data(DFm),aes(x=t,y=mm)) + geom_bar(stat = "identity",fill = "dodgerblue",color = "black") + scale_x_continuous(name = "t(h)", breaks = seq(1,24,1)) + scale_y_continuous(name = "mm")
})
}
When I run this script, I get the shiny app I have the slider and the numeric input, but not the plot getting an error message saying Error: unused argument (DFm). DFm is not created, so I assume that I am placing bad the code from my original script inside the shiny app, but I cannot figure out how to do it.
I have tried several things to make the server works, but I think that the script never runs inside the shiny app. I tried to create two reactive functions, one per each reactive value. I tried to put all the code from my original script out of the shiny leaving just the reactive values inside the shiny code... I tried with observe function as well.
Another problem I have is that I am not sure about the renderPlot. I am aware that I have to call the reactive function, data, but as I am not sure if it is well made.
I think that my server function is a total disaster. I have looked for examples in the shiny gallery and in Google, but I do not see something similar to help me.
Could anyone give a tip in order to fix my server function?
Many thanks in advance.
Following the advice of our colleague, I could solve my problem by setting DF, the first data frame I created, in return() inside the reactive function. Another confusion was how to set the object from the reactive function inside the renderplot.

shinyapps.io does not draw plots

I built a simple app using FactorMineR package to do MCA analysis and clustering depending on selected variables.
The app works fine on my local device, however it does not show any plots (either base plots and ggplots) on shinyapps.io server. I checked the packages and locally and remotley they are the same. I also checked if the MCA() function from FactoMineR pcg even works by extracking some results and rendering them as a table what gave positive results. So there is only the problem with plots drawing. I have been trying to solve it for two days but nothing helps so I am asking you for any advice.
Here is how it looks locally:
Here is the link to the app: https://mikolajm.shinyapps.io/MCA_test/
And a reproducible example
library(shiny)
library(FactoMineR)
library(cluster)
library(ggplot2)
data(tea)
ui <- fluidPage(
# Application title
titlePanel("MCA"),
textOutput("packages"),br(),
tableOutput("table"),br(),
fluidRow(
column(4, checkboxGroupInput("Variables", "Select variables:",
names(tea), selected=c("breakfast", "tea.time"))),
column(4, plotOutput("plot")), column(4, plotOutput("plot1"))),
fluidRow(column(12, plotOutput("dendro", height = "700px", width="1200px"))
)
)
server <- function(input, output) {
## packages checking
output$packages <- renderText({.packages()})
tea_selected <- reactive({
tea[, input$Variables]
})
## table with some results from MCA() fun
output$table <- renderTable({
tea.mca <- MCA(tea_selected(), ncp=9)
tea.mca$eig[1:5,]
})
## mca1
output$plot <- renderPlot({
library(FactoMineR)
par(mfrow=c(2,2))
tea.mca <- MCA(tea_selected(), ncp=9)
})
## mca with ggplot
output$plot1 <- renderPlot({
tea.mca <- MCA(tea_selected(), ncp=9)
tea_vars_df <- data.frame(tea.mca$var$eta2, Variable =names(tea_selected()))
library(ggplot2)
pp <- ggplot(data=tea_vars_df, aes(x=Dim.1, y=Dim.2, label=Variable))+
geom_hline(yintercept = 0, colour = "gray70") +
geom_vline(xintercept = 0, colour = "gray70") +
geom_point()+
geom_text() +
ggtitle("MCA plot of variables ")+
theme_bw()
pp
})
### dendro
output$dendro <- renderPlot({
library(FactoMineR)
library(cluster)
tea.mca <- MCA(tea_selected(), ncp=9)
classif <- agnes(tea.mca$ind$coord,method="ward")
plot(classif,main="Dendrogram",ask=F,which.plots=2)
})
}
# Run the application
shinyApp(ui = ui, server = server)
EDIT: You can see plots obviously, but
ORIGINAL
I could not see plots in your shiny app when I ran your code.
After some digging, my guess is only that:
You use a lot of functions that come with the FactoMineR package. For instance, you use the function MCA in output$plot1 code block. Type MCA in your R command line, and it should print the function. You can see MCA does a lot of stuff and eventually calls plot.MCA. Now type plot.MCA in your R command line. You can see that plot.MCA has a lot of plot commands, and I'm pretty sure this executes all the plotting when you call MCA. I think your problem is that plot in the function plot.MCA is sent to the graphic device, and these plots are not saved, ie they are not return() to the parent environment. This is only speculation.

R Shiny application: Modifying plot without re-rendering it

I've been looking into ways to update a plot within an R Shiny application without having to re-render the whole plot. I'm working with temporal data which is animated via a Slider Input (animationOptions(playButton = TRUE)). The idea is to somehow highlight the part of the plot which is selected via the Slider Input. Re-rendering the whole plot at every animation step would make the whole application uselessly slow.
The most elegant solution with ggplot2 would have been, if shiny offered a way to add layers to the ggplot (e.g. + geom line()) and integrated this layer seamlessly into the plot without re-rendering it. Sadly, this does not seem to work. A bit of a hack could include creating a second ggplot-instance with exactly the same x/y-dimensions and overlapping the two plots.
EDIT:
I've just learnt that there are more javascript oriented plotting methods than ggplot2. For example, using dygraphs and adding a layer of dyShading, the selected area gets highlighted nicely. The basic question remains the same though, since changing the start- and end values of dyShading() seems to require re-rendering the whole plot.
library(shiny)
library(dygraphs)
library(xts)
data <- data.frame(
datetime = as.POSIXct("2016-06-20 17:00:00", tz = "UTC") + 1:100*60,
y = rnorm(100)
)
data_xts <- as.xts(data[,-1], data[,1])
minDatetime <- min(data$datetime)
maxDatetime <- max(data$datetime)
minY = min(data$y)
maxY = max(data$y)
plotlimits <- lims(x = c(minDatetime, maxDatetime), y = c(minY, maxY))
ui <- fluidPage(
sliderInput("timeslider", "Time Slider",
min = minDatetime,
max = maxDatetime,
value = c(minDatetime, minDatetime+10*60),
animate = animationOptions(interval=200)
),
dygraphOutput("dyplot")
)
server <- function(input, output) {
data_fil <- reactive({
data[data$datetime <= input$timeslider[2] & data$datetime >= input$timeslider[1],]
})
output$dyplot <- renderDygraph({
dygraph(data_xts) %>%
dyShading(
from = as.character(input$timeslider[1]),
to = as.character(input$timeslider[2]),
color = "tomato")
})
}
shinyApp(ui = ui, server = server)

Resources