diff --git a/materials/DESCRIPTION b/materials/DESCRIPTION index be7e832d..924da10f 100755 --- a/materials/DESCRIPTION +++ b/materials/DESCRIPTION @@ -19,6 +19,7 @@ Imports: forcats, foreach, ggplot2, + ggExtra, ggforce, ggmap, ggpmisc, diff --git a/materials/sections/visualization-shiny.qmd b/materials/sections/visualization-shiny.qmd index 756e1768..771f7cc2 100644 --- a/materials/sections/visualization-shiny.qmd +++ b/materials/sections/visualization-shiny.qmd @@ -1,4 +1,4 @@ -### Learning Objectives +## Learning Objectives In this lesson we will: - review the capabilities in Shiny applications @@ -6,7 +6,7 @@ In this lesson we will: - learn about the server component for Shiny applications - build a simple shiny application for interactive plotting -### Overview +## Overview [Shiny](http://shiny.rstudio.com/) is an R package for creating interactive data visualizations embedded in a web application that you and your colleagues can view with just a web browser. Shiny apps are relatively easy to construct, and provide interactive features for letting others share and explore data and analyses. @@ -26,7 +26,7 @@ While that is the best citation and archival location of the dataset, using Shin We're going to create a simple shiny app with two sliders so we can interactively control inputs to an R function. These sliders will allow us to interactively control a plot. -### Create a sample shiny application +## Create a sample shiny application - File > New > Shiny Web App... - Set some fields: @@ -45,7 +45,7 @@ the number of bins in the plot. Note that you can drag the slider to change the number of bins in the histogram. -### Shiny architecture +## Shiny architecture A Shiny application consists of two functions, the `ui` and the `server`. The `ui` function is responsible for drawing the web page, while the `server` is responsible @@ -62,11 +62,11 @@ else on the Internet (e.g., if the application is deployed to a web server). ![](images/shiny-architecture.png) -### Interactive scatterplots +## Interactive scatterplots Let's modify this application to plot Yolo bypass secchi disk data in a time-series, and allow aspects of the plot to be interactively changed. -#### Load data for the example +### Load data for the example Use this code to load the data at the top of your `app.R` script. Note we are using `contentId` again, and we have filtered for some species of interest. @@ -90,7 +90,7 @@ names(delta_data) ``` -#### Add a simple timeseries using ggplot +### Add a simple timeseries using ggplot We know there has been a lot of variation through time in the delta, so let's plot a time-series of Secchi depth. We do so by switching out the histogram code for a simple ggplot, like so: @@ -113,7 +113,7 @@ In a Shiny application, the `server` function provides the part of the applicati that creates our interactive components, and returns them to the user interface (`ui`) to be displayed on the page. -#### Add sliders to set the start and end date for the X axis +### Add sliders to set the start and end date for the X axis To make the plot interactive, first we need to modify our user interface to include widgits that we'll use to control the plot. Specifically, we will add a new slider @@ -139,7 +139,7 @@ sidebarPanel( If you reload the app, you'll see two new sliders, but if you change them, they don't make any changes to the plot. Let's fix that. -#### Connect the slider values to the plot +### Connect the slider values to the plot Finally, to make the plot interactive, we can use the `input` and `output` variables that are passed into the `server` function to access the current values of the sliders. @@ -160,7 +160,7 @@ min and max of the Depth axis. Looks so shiny! -#### Reversed Axes? +### Reversed Axes? What happens if a clever user sets the minimum for the X axis at a greater value than the maximum? You'll see that the direction of the X axis becomes reversed, and the plotted points display right @@ -193,7 +193,7 @@ second is the max value on the slider. ![](images/shiny-yolo-2.png) -### Extending the user interface with dynamic plots +## Extending the user interface with dynamic plots If you want to display more than one plot in your application, and provide a different set of controls for each plot, the current layout would be too simple. @@ -205,7 +205,7 @@ a `sidebarLayout`, which is divided horizontally into a `sidebarPanel` and a ![](images/shiny-layout-1.png) -#### Vertical layout +### Vertical layout To extend the layout, we will first nest the existing `sidebarLayout` in a new `verticalLayout`, which simply flows components down the page vertically. Then @@ -272,7 +272,7 @@ cols <- names(delta_data) ``` -#### Add the dynamic plot +### Add the dynamic plot Because we named the second plot `varPlot` in our UI section, we now need to modify the server to produce this plot. Its very similar to the first plot, but this time @@ -291,7 +291,7 @@ strings, and so would not be recognized as symbols in the `aes` mapping in ggplo ``` -### Finishing touches: data citation +## Finishing touches: data citation Citing the data that we used for this application is the right thing to do, and easy. You can add arbitrary HTML to the layout using utility functions in the `tags` list. @@ -315,7 +315,7 @@ configurable scatterplot in three distinct panels. ![](images/shiny-yolo-app.png) -### Publishing Shiny applications +## Publishing Shiny applications Once you've finished your app, you'll want to share it with others. To do so, you need to publish it to a server that is set up to [handle Shiny apps](https://shiny.rstudio.com/deploy/). @@ -335,7 +335,7 @@ Your main choices are: A comparison of [publishing features](https://rstudio.com/products/shiny/shiny-server/) is available from RStudio. -#### Publishing to shinyapps.io +### Publishing to shinyapps.io The easiest path is to create an account on shinyapps.io, and then configure RStudio to use that account for publishing. Instructions for enabling your local RStudio to publish @@ -348,7 +348,7 @@ application window in RStudio, and your app will be live before you know it! ![](images/shiny-publish.png) -### Summary +## Summary Shiny is a fantastic way to quickly and efficiently provide data exploration for your data and code. We highly recommend it for its interactivity, but an archival-quality @@ -358,7 +358,7 @@ in our Shiny app, which offers both the preservation guarantees of an archive, p the interactive data exploration from Shiny. You can utilize the full power of R and the tidyverse for writing your interactive applications. -### Full source code for the final application +## Full source code for the final application ```{r shinyapp_source, eval=FALSE} @@ -458,7 +458,7 @@ shinyApp(ui = ui, server = server) ``` -### A shinier app with tabs and a map! +## A shinier app with tabs and a map! ```{r, eval = F} library(shiny) @@ -601,7 +601,7 @@ shinyApp(ui = ui, server = server) ``` -### Resources +## Resources - [Main Shiny site](http://shiny.rstudio.com/) - [Official Shiny Tutorial](http://shiny.rstudio.com/tutorial/) diff --git a/materials/session_05.qmd b/materials/session_05.qmd index 72a66ab4..5a444acc 100644 --- a/materials/session_05.qmd +++ b/materials/session_05.qmd @@ -4,4 +4,3 @@ title-block-banner: true --- {{< include /sections/visualization-shiny.qmd >}} - diff --git a/materials/shiny-demo/delta-map-app.R b/materials/shiny-demo/delta-map-app.R new file mode 100644 index 00000000..89439f6a --- /dev/null +++ b/materials/shiny-demo/delta-map-app.R @@ -0,0 +1,136 @@ +library(shiny) +library(contentid) +library(dplyr) +library(tidyr) +library(ggplot2) +library(lubridate) +library(shinythemes) +library(sf) +library(leaflet) +library(snakecase) + +# read in the data from EDI +sha1 <- 'hash://sha1/317d7f840e598f5f3be732ab0e04f00a8051c6d0' +delta.file <- contentid::resolve(sha1, registries=c("dataone"), store = TRUE) + +# fix the sample date format, and filter for species of interest +delta_data <- read.csv(delta.file) %>% + mutate(SampleDate = mdy(SampleDate)) %>% + filter(grepl("Salmon|Striped Bass|Smelt|Sturgeon", CommonName)) %>% + rename(DissolvedOxygen = DO, + Ph = pH, + SpecificConductivity = SpCnd) + +cols <- names(delta_data) + +sites <- delta_data %>% + distinct(StationCode, Latitude, Longitude) %>% + drop_na() %>% + st_as_sf(coords = c('Longitude','Latitude'), crs = 4269, remove = FALSE) + + + +# Define UI for application +ui <- fluidPage( + navbarPage(theme = shinytheme("flatly"), collapsible = TRUE, + HTML('Sacramento River Floodplain Data'), id="nav", + windowTitle = "Sacramento River floodplain fish and water quality data", + + tabPanel("Data Sources", + verticalLayout( + # Application title and data source + titlePanel("Sacramento River floodplain fish and water quality data"), + p("Data for this application are from: "), + tags$ul( + tags$li("Interagency Ecological Program: Fish catch and water quality data from the Sacramento River floodplain and tidal slough, collected by the Yolo Bypass Fish Monitoring Program, 1998-2018.", + tags$a("doi:10.6073/pasta/b0b15aef7f3b52d2c5adc10004c05a6f", href="http://doi.org/10.6073/pasta/b0b15aef7f3b52d2c5adc10004c05a6f") + ) + ), + tags$br(), + tags$hr(), + p("Map of sampling locations"), + mainPanel(leafletOutput("map")) + ) + ), + + tabPanel( + "Explore", + verticalLayout( + mainPanel( + plotOutput("distPlot"), + width = 12, + absolutePanel(id = "controls", + class = "panel panel-default", + top = 175, left = 75, width = 300, fixed=TRUE, + draggable = TRUE, height = "auto", + sliderInput("date", + "Date:", + min = as.Date("1998-01-01"), + max = as.Date("2020-01-01"), + value = c(as.Date("1998-01-01"), as.Date("2020-01-01"))) + ) + ), + + tags$hr(), + + sidebarLayout( + sidebarPanel( + selectInput("x_variable", "X Variable", cols, selected = "SampleDate"), + selectInput("y_variable", "Y Variable", cols, selected = "Count"), + selectInput("color_variable", "Color", cols, selected = "CommonName") + ), + + # Show a plot with configurable axes + mainPanel( + plotOutput("varPlot") + ) + ), + tags$hr() + ) + ) + ) +) + +# Define server logic required to draw the two plots +server <- function(input, output) { + + + output$map <- renderLeaflet({leaflet(sites) %>% + addTiles() %>% + addCircleMarkers(data = sites, + lat = ~Latitude, + lng = ~Longitude, + radius = 10, # arbitrary scaling + fillColor = "gray", + fillOpacity = 1, + weight = 0.25, + color = "black", + label = ~StationCode) + }) + + # turbidity plot + output$distPlot <- renderPlot({ + + ggplot(delta_data, mapping = aes(SampleDate, Secchi)) + + geom_point(colour="red", size=4) + + xlim(c(input$date[1],input$date[2])) + + labs(x = "Sample Date", y = "Secchi Depth (m)") + + theme_light() + }) + + # mix and match plot + output$varPlot <- renderPlot({ + ggplot(delta_data, mapping = aes(x = .data[[input$x_variable]], + y = .data[[input$y_variable]], + color = .data[[input$color_variable]])) + + labs(x = to_any_case(input$x_variable, case = "title"), + y = to_any_case(input$y_variable, case = "title"), + color = to_any_case(input$color_variable, case = "title")) + + geom_point(size=4) + + theme_light() + }) +} + + +# Run the application +shinyApp(ui = ui, server = server)