Let’s start off by creating a new Shiny application. We could do this by using the “Shiny Web App…” option under the new files menu and choosing “Single File.” You can do that as an example if you like, but we’ll eventually be creating one from scratch. For reference, this is what the default Shiny app looks like:
library(shiny)
# Define UI for application that draws a histogram
ui <- fluidPage(
# Application title
titlePanel("Old Faithful Geyser Data"),
# Sidebar with a slider input for number of bins
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
# Show a plot of the generated distribution
mainPanel(
plotOutput("distPlot")
)
)
)
# Define server logic required to draw a histogram
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}
# Run the application
shinyApp(ui = ui, server = server)This is divided into two principal components:
The user interface is what it sounds like - the user-facing side of your application. It’s all contained in a fluidPage wrapper, and is made up of various display elements. These are mainly the titlePanel, sidebarPanel (with a sliderInput), and the mainPanel (which contains a plotOutput). Here it is on its own:
ui <- fluidPage(
titlePanel("Old Faithful Geyser Data"),
sidebarLayout(
sidebarPanel(
sliderInput("bins",
"Number of bins:",
min = 1,
max = 50,
value = 30)
),
mainPanel(
plotOutput("distPlot")
)
)
)These are just a few of the UI elements available to you. You can see more about layouts and UI elements here.
The server is where the processing and updating of data, plots, etc. happens. Most of the R code that you’ve used already will happen here. This is what it looks like in the default application:
server <- function(input, output) {
output$distPlot <- renderPlot({
# generate bins based on input$bins from ui.R
x <- faithful[, 2]
bins <- seq(min(x), max(x), length.out = input$bins + 1)
# draw the histogram with the specified number of bins
hist(x, breaks = bins, col = 'darkgray', border = 'white')
})
}This code is where the data is filtered and plot is created.
You can see the result of the renderPlot function is saved into an output called distPlot. This is mirrored in the output("distPlot") in the UI side, where the plot is displayed. You can also see that the server contains input$bins, which is a reference to sliderInput("bins", ...) in the UI. Inputs and outputs are special objects in Shiny that allow the UI and server to communicate with one another and create interactivity. The input on the UI side accepts information which is processed in the server. The server creates changes to the plot in response to that information, and the plot is then displayed again on the UI side.
The general flow looks like this:
inputinput and saves results to outputoutputThe final line of the default app is the call to run the application. It tells R to link and start the UI and server, and looks like this:
shinyApp(ui = ui, server = server)We’re going to start our application with the same overall structure, but let’s not include any of the other elements we don’t need from the default app. We’ll need a basic structure, along with a couple of preliminary elements (like calls to packages we’ll want).
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
ui <- fluidPage()
server <- function(input, output) {}
shinyApp(ui = ui, server = server)There are a few other things that won’t change within our application. The first is our original dataset (which will contain all of our data, unfiltered), and the second is the color palette we’ll be creating. Let’s add those in.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage()
# Server --------------------
server <- function(input, output) {}
shinyApp(ui = ui, server = server)Now that we’ve got a basic (but still empty) application, let’s add some UI elements to provide us with something to look at. We can start with the basic structure by adding things like the title and some rows and columns to organize our UI
Our page is going to be made up of three fluidRow() elements, which organize the page horizontally. For aesthetic’s sake, we’re going to separate those rows with horizontal rules hr().
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(),
hr(),
fluidRow(),
hr(),
fluidRow()
)
# Server --------------------
server <- function(input, output) {}
shinyApp(ui = ui, server = server)fluidRow() and column()A fluidRow() is divided into column() elements, and the width of those columns should add up to 12. Our main panel and attribution can both be 12, but we’re going to want three inputs, so we can make the width of those as 4 each (4 + 4 + 4 = 12).
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12)
),
hr(),
fluidRow(
column(6),
column(6)
),
hr(),
fluidRow(
column(12)
)
)
# Server --------------------
server <- function(input, output) {}
shinyApp(ui = ui, server = server)If you run this application now, it won’t look much different, but we’ve just completed the underlying structure for our UI! Now we can add some content. Let’s start off with our data attribution - citing our sources is important!
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12)
),
hr(),
fluidRow(
column(6),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {}
shinyApp(ui = ui, server = server)This takes us up to the content that we have in starting-app.R, which was available to download from our Canvas site. This is where you can start adding code in yourself.
Now let’s add in our main panel with our map.
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
)Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {}
shinyApp(ui = ui, server = server)Hmm…we’ve added a place for the map on our UI, but nothing’s happening yet. That’s because we need to put some code on our server that will create the map. This comes in two steps. The first is to filter our dataset to a specific day, and the next is to create a map with it. We’ll filter our data and save it as a reactive(), which is a type of object that Shiny knows will change whenever new data is sent from the inputs.
Right now we’re going to stick with the latest date, but we’ll change it later on so that we can use an input to select the date.
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == max(case_month))
return(tmp)
})Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == max(case_month))
return(tmp)
})
}
shinyApp(ui = ui, server = server)outputNow that we’ve added our data as a reactive(), let’s go ahead and use it to make our map. This will happen in two parts. The first is going to be setting up our empty map and legend using our bounds, the second is going to be adding our markers. The first part will be wrapped in a renderLeaflet() function, which is one of the ways we tell Shiny that something will react to inputs. There are other renderX functions, e.g. renderPlot(), renderText(), renderTable(). Here’s how we add the empty map:
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == max(case_month))
return(tmp)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})
}
shinyApp(ui = ui, server = server)leafletProxy() for markersBecause there are a lot of markers and we plan to update them frequently, we’ll be using leafletProxy() to only change our markers without having to reload the entire map every time something changes. You can read more about leafletProxy here. We’re going to put it inside of an observe(), which will look for changes from our inputs and re-run the code inside any time something changes.
observe({
leafletProxy("map", data = df()) %>%
clearMarkers() %>%
addCircleMarkers(radius = ~log(cases),
stroke = FALSE,
fillOpacity = 0.5,
color = ~pal(largest_age_group),
popup = ~paste0(
region, "</br>",
"Number of Cases: ", cases, "</br>",
"Lab-confirmed Cases: ", lab_n, "</br>",
"Number of Deaths: ", death_n, "</br>",
"Largest Age Group: ", largest_age_group
))
})Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == max(case_month))
return(tmp)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})
observe({
leafletProxy("map", data = df()) %>%
clearMarkers() %>%
addCircleMarkers(radius = ~log(cases),
stroke = FALSE,
fillOpacity = 0.5,
color = ~pal(largest_age_group),
popup = ~paste0(
region, "</br>",
"Number of Cases: ", cases, "</br>",
"Lab-confirmed Cases: ", lab_n, "</br>",
"Number of Deaths: ", death_n, "</br>",
"Largest Age Group: ", largest_age_group
))
})
}
shinyApp(ui = ui, server = server)Excellent! Now we’ve got our map from last time! Time to make it even better!
Let’s add some inputs to our UI and see how they connect to our server.
In the first column() of our second fluidRow(), we can add in a date filter. We’ll do that by adding a sliderInput() to the first column() of our second fluidRow(). We have to define things like the range and beginning value of our slider. We’re also going to set animate = TRUE to add a play/pause button.
column(6,
sliderTextInput(
"date_select",
"Select Mapping Date",
choices = unique(df_original$case_month),
selected = min(df_original$case_month),
animate = TRUE
)
)Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6,
sliderTextInput(
"date_select",
"Select Mapping Date",
choices = unique(df_original$case_month),
selected = min(df_original$case_month),
animate = TRUE
)
),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == max(case_month))
return(tmp)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})
observe({
leafletProxy("map", data = df()) %>%
clearMarkers() %>%
addCircleMarkers(radius = ~log(cases),
stroke = FALSE,
fillOpacity = 0.5,
color = ~pal(largest_age_group),
popup = ~paste0(
region, "</br>",
"Number of Cases: ", cases, "</br>",
"Lab-confirmed Cases: ", lab_n, "</br>",
"Number of Deaths: ", death_n, "</br>",
"Largest Age Group: ", largest_age_group
))
})
}
shinyApp(ui = ui, server = server)This is great, but it isn’t hooked up to anything yet. This input allows us to select a date within the range that’s in our dataset, but we need to filter the data to the correct date before we can display it. Let’s add the code to handle that to our server side in our reactive(). Rather than filtering for the last date using filter(case_month == max(case_month)), we’re going to reference our input using filter(case_month == input$date_select).
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == input$date_select)
return(tmp)
})Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6,
sliderTextInput(
"date_select",
"Select Mapping Date",
choices = unique(df_original$case_month),
selected = min(df_original$case_month),
animate = TRUE
)
),
column(6)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == input$date_select)
return(tmp)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})
observe({
leafletProxy("map", data = df()) %>%
clearMarkers() %>%
addCircleMarkers(radius = ~log(cases),
stroke = FALSE,
fillOpacity = 0.5,
color = ~pal(largest_age_group),
popup = ~paste0(
region, "</br>",
"Number of Cases: ", cases, "</br>",
"Lab-confirmed Cases: ", lab_n, "</br>",
"Number of Deaths: ", death_n, "</br>",
"Largest Age Group: ", largest_age_group
))
})
}
shinyApp(ui = ui, server = server)Now try running that code - you should be able to animate the map using the date slider we’ve created.
Let’s try using another type of widget to control our map. By default, we’re sizing our bubbles using the “cases” variable. We might also be interested seeing other variables, though, so let’s give ourselves the option to explore that. First we need to add elements to our UI - this time we’ll use a radioButtons() input, which will go in the second column() of our second fluidRow(). For the radioButtons(), we will provide a list of choices. The text displayed in the UI will appear on the left, and the variable name we’re referencing will appear on the right, e.g. "Number of Lab-Confirmed Cases" = "lab_n".
column(6,
radioButtons("size_by",
"Size Markers By Value",
choices = list("Total Cases" = "cases",
"Number of deaths" = "death_n",
"% of cases resulting in death" = "death_pct",
"Number admitted to ICU" = "icu_n",
"% of cases admitted to ICU" = "icu_pct",
"Number of Lab-Confirmed Cases" = "lab_n",
"% of lab-confirmed cases" = "lab_pct"),
selected = "cases")
)Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6,
sliderTextInput(
"date_select",
"Select Mapping Date",
choices = unique(df_original$case_month),
selected = min(df_original$case_month),
animate = TRUE
)
),
column(6,
radioButtons("size_by",
"Size Markers By Value",
choices = list("Total Cases" = "cases",
"Number of deaths" = "death_n",
"% of cases resulting in death" = "death_pct",
"Number admitted to ICU" = "icu_n",
"% of cases admitted to ICU" = "icu_pct",
"Number of Lab-Confirmed Cases" = "lab_n",
"% of lab-confirmed cases" = "lab_pct"),
selected = "cases")
)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == input$date_select)
return(tmp)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})
observe({
leafletProxy("map", data = df()) %>%
clearMarkers() %>%
addCircleMarkers(radius = ~log(cases),
stroke = FALSE,
fillOpacity = 0.5,
color = ~pal(largest_age_group),
popup = ~paste0(
region, "</br>",
"Number of Cases: ", cases, "</br>",
"Lab-confirmed Cases: ", lab_n, "</br>",
"Number of Deaths: ", death_n, "</br>",
"Largest Age Group: ", largest_age_group
))
})
}
shinyApp(ui = ui, server = server)If we run the app now, we’ll have the widget on our UI. It’s not hooked up to anything in our server yet, though, so that’s the next step. Because we’re referencing a variable name in the code to size the markers, we need to use the get() function around input$size_by; this tells Shiny to read the input as a variable lab_n rather than as just a character string "lab_n".
radius = ~log(get(input$size_by))Click on the Code button to see how that looks in the context of our application.
library(tidyverse)
library(shiny)
library(shinyWidgets)
library(leaflet)
df_original <- read_csv("./data/processed/all_cases.csv")
pal <- colorFactor("Set1", df_original$largest_age_group)
lng1 <- -125
lat1 <- 25
lng2 <- -68
lat2 <- 49
# UI --------------------
ui <- fluidPage(
title = "Tracking the Spread of COVID-19",
titlePanel("Tracking the Spread of COVID-19 by County"),
fluidRow(
column(12,
mainPanel(leafletOutput("map"))
)
),
hr(),
fluidRow(
column(6,
sliderTextInput(
"date_select",
"Select Mapping Date",
choices = unique(df_original$case_month),
selected = min(df_original$case_month),
animate = TRUE
)
),
column(6,
radioButtons("size_by",
"Size Markers By Value",
choices = list("Total Cases" = "cases",
"Number of deaths" = "death_n",
"% of cases resulting in death" = "death_pct",
"Number admitted to ICU" = "icu_n",
"% of cases admitted to ICU" = "icu_pct",
"Number of Lab-Confirmed Cases" = "lab_n",
"% of lab-confirmed cases" = "lab_pct"),
selected = "cases")
)
),
hr(),
fluidRow(
column(12,
p("Data from ",
a("CDC",
href = "https://data.cdc.gov/Case-Surveillance/COVID-19-Case-Surveillance-Public-Use-Data-with-Ge/n8mc-b4w4",
target = "_blank"))
)
)
)
# Server --------------------
server <- function(input, output) {
df <- reactive({
tmp <- df_original %>%
filter(cases > 0) %>%
filter(case_month == input$date_select)
return(tmp)
})
output$map <- renderLeaflet({
leaflet() %>%
addTiles() %>%
fitBounds(lng1, lat1, lng2, lat2) %>%
addLegend("bottomright",
pal = pal,
values = c(FALSE, TRUE),
title = "Stay at Home Order",
opacity = 1)
})
observe({
leafletProxy("map", data = df()) %>%
clearMarkers() %>%
addCircleMarkers(radius = ~log(get(input$size_by)),
stroke = FALSE,
fillOpacity = 0.5,
color = ~pal(largest_age_group),
popup = ~paste0(
region, "</br>",
"Number of Cases: ", cases, "</br>",
"Lab-confirmed Cases: ", lab_n, "</br>",
"Number of Deaths: ", death_n, "</br>",
"Largest Age Group: ", largest_age_group
))
})
}
shinyApp(ui = ui, server = server)