Building a climate risk dashboard - server (output)
Communicating climate risk
FEMA has asked us to build an improved dashboard that visualizes the risk of climate change in the United States.
Based on your submitted designs and our skill level, we will work to implement this Shiny dashboard.
Construct the server (output)
National map
Fill in the TODO
s and correctly integrate into the server code chunk.
# visualize the risks nationally
ggplot(data = climate_sf) +
# layer for each county's risk
geom_sf(mapping = aes(fill = TODO)) +
# layer for state boundaries to better locate regions
geom_sf(data = state_sf, fill = NA, color = "white") +
# appropriate color palette
scale_fill_discrete_diverging(rev = TRUE) +
# don't label the legend
labs(
fill = NULL
+
) # map theme
theme_map(base_size = 18) +
# position the legend on the right
theme(legend.position = "right")
To access input values which are character strings and treat them as column names, use the bang-bang operator. For example, if input$var_name
contained the name of a column and you wanted to use it in a ggplot2 visualization, you could write your code like:
```{r}
#| context: server
output$myplot <- renderPlot({
ggplot(data = df, mapping = aes(x = !!input$var_name)) +
geom_histogram()
})
```
County map
Fill in the TODO
s and correctly integrate into the server code chunk.
# get selected county's state
<- climate_sf |>
county_state filter(county == TODO) |>
pull(STATE_NAME)
|>
climate_sf # filter for counties in specific state
filter(STATE_NAME == county_state) |>
# variable to highlight selected county
mutate(selected = county == TODO) |>
# draw the map
ggplot() +
geom_sf(mapping = aes(fill = selected, color = selected)) +
scale_fill_manual(values = c(NA, "orange")) +
scale_color_manual(values = c("white", "orange")) +
theme_map() +
theme(legend.position = "none")
Validating inputs
What happens if there is no county provided in the input? Cryptic red error messages in the UI can be disconcerting to users. Instead, we can use validate()
and need()
to shunt the error messages to the console and print a human-readable message to the user.
For example, say the output relies on input$num
which must not be empty. We can use
need(input$num, "Provide a number")
to check if input$num
is provided. If the condition fails (i.e. input$num
is blank or missing), then need()
returns the specified character string. We can incorporate these requirements into the output using validate()
.
$myplot <- renderPlot({
outputvalidate(
need(input$num, "Provide a number")
)
# remaining code...
})
validate()
takes any number of arguments depending on how many conditions must be checked. If any of them fail, the app triggers an error which is reported in the console and displays the need()
-provided message to the user in the UI.
County risk scores
selected_county
County stats for value boxes
Use the filtered data frame to generate the text strings for each of the value boxes.
# overall risk
$county_risk <- renderText({
output# get selected county's overall risk score
<- selected_county() |>
val pull(national_risk_index_score_composite)
# format using scales function
label_number(accuracy = 1)(val)
})
Individual hazard percentiles
Fill in the TODO
s and correctly integrate into the server code chunk.
# get selected county's hazard percentiles
<- climate_risk |>
selected_county_hazards filter(county == TODO)
# plot hazard percentiles
|>
selected_county_hazards # reshape to long format for visualizing
pivot_longer(
cols = contains("hazard"),
names_to = "hazard",
values_to = "percentile"
|>
) # only visualize selected hazard types
filter(hazard %in% TODO) |>
# order alphabetically
mutate(hazard = str_remove(hazard, "_hazard_type_risk_index_score") |>
make_clean_names(case = "title") |>
fct_rev()) |>
ggplot(mapping = aes(y = hazard)) +
# all hazards range between 0 and 100
geom_linerange(mapping = aes(xmin = 0, xmax = 100)) +
# draw specific county
geom_point(
mapping = aes(x = percentile, color = percentile),
size = 4
+
) # optimized color palette
scale_color_continuous_diverging(mid = 50, rev = TRUE, guide = "none") +
# appropriate labels
labs(
x = "Percentile",
y = NULL
+
) # clean up the theme
theme_minimal(base_size = 18) +
theme(
panel.grid.minor = element_blank(),
panel.grid.major.y = element_blank()
)