---
title: "Connection to other Shiny elements"
output: rmarkdown::html_vignette
vignette: >
%\VignetteIndexEntry{Connection to other Shiny elements}
%\VignetteEngine{knitr::rmarkdown}
%\VignetteEncoding{UTF-8}
---
```{r, include = FALSE}
knitr::opts_chunk$set(
collapse = TRUE,
comment = "#>"
)
```
In this article, we demonstrate how to connect a `leafdown` map to other Shiny elements.
```{r, echo=FALSE, out.width="100%"}
knitr::include_graphics("../man/figures/app_election_map.png")
```
Click here for the [full demo app](https://pega.shinyapps.io/election16/)
## Data
As described in the [Introduction article.](https://hoga-it.github.io/leafdown/articles/Introduction.html), we need two
types of data:
- **SpatialPolygonsDataFrames**, the shapes of the US-States and Counties, taken from the `raster` package.
- **Election Results and Census Data**, the data we want to display on the map, taken from the example data sets that
come with the `leafdown` package. (The original data comes from [Deleetdk](https://github.com/Deleetdk/USA.county.data).
For more information about the data, please see `?us_election_states` or `?us_election_counties` respectively)
## The Map itself
The structure of the map is pretty similar to the map from the [Introduction article.](https://hoga-it.github.io/leafdown/articles/Introduction.html)
Here we show the results of the US Presidential Election from 2016.
## Connecting Graphs to our Map
In this section, we want to demonstrate how simple it is to connect graphs or similar UI-elements with the map.
We create two graphs that give more insight into the currently selected shapes:
- As the map only shows the winner, we create a bar chart to show the percentages from every party.
- Additionally, we add a graph showing the racial makeup.
The changes in the UI are straightforward:
```{r, eval = FALSE}
column(
width = 5,
# box for racial makeup graph
bs4Card(
width = 12,
closable = F,
collapsible = F,
title = "Racial makeup in percentages",
echarts4rOutput("socio")
),
# box for party percent graph
bs4Card(
width = 12,
closable = F,
collapsible = F,
title = "Votes in percent",
echarts4rOutput("party")
)
)
```
To connect the graphs with the map, we can use the `$curr_sel_data()` attribute.
This attribute is a `reactiveValue` which allows us to update the graphs whenever the user selects a shape on the map or drills a level up or down.
In the server, we obtain the data using `df <- my_leafdown$curr_sel_data()`.
Creating the rest of the graph is again straightforward.
```{r, eval = FALSE}
output$party <- renderEcharts4r({
# get the currently selected data from the map
df <- my_leafdown$curr_sel_data()
# check whether any shape is selected, show general election-result if nothing is selected
if (nrow(df) > 0) {
if (my_leafdown$curr_map_level == 1) {
df <- df[, c("state_abbr", "Democrats2016", "Republicans2016", "Libertarians2016", "Green2016")]
df <- df %>%
pivot_longer(2:5, "party") %>%
group_by(party)
} else {
df <- df[, c("County", "Democrats2016", "Republicans2016", "Libertarians2016", "Green2016")]
df <- df %>%
pivot_longer(2:5, "party") %>%
group_by(party)
df$value <- df$value
names(df)[1] <- "state_abbr"
}
} else {
# show general election-result as no state is selected
df <- data.frame(
party = c("Democrats2016", "Republicans2016", "Libertarians2016", "Green2016"),
state_abbr = "USA",
value = c(0.153, 0.634, 0.134, 0.059)
) %>%
group_by(party)
}
# create the graph
df %>%
e_charts(state_abbr, stack = "grp") %>%
e_bar(value) %>%
e_y_axis(formatter = e_axis_formatter("percent", digits = 2)) %>%
e_tooltip(trigger = "axis", axisPointer = list(type = "shadow")) %>%
e_legend(right = 10, top = 10) %>%
e_color(c("#232066", "#E91D0E", "#f3b300", "#006900")) %>%
e_tooltip(formatter = e_tooltip_item_formatter("percent", digits = 2))
})
```
# Final Demo App
The full code of the election map...
**Note:** The shapes have to be manually downloaded before the app can be used.
In the given election app the shapes have also been simplified to 0.5% of their original size.
```{r, eval=FALSE}
states <- raster::getData(country = "USA", level = 1)
counties <- raster::getData(country = "USA", level = 2)
# TODO replace the path to your downloaded shapes in the server code
```
```{r, eval=FALSE}
library(shiny)
library(bs4Dash)
library(shinyjs)
library(leaflet)
library(leafdown)
library(echarts4r)
library(dplyr)
library(tidyr)
library(RColorBrewer)
ui <- bs4DashPage(
title = "Leafdown Showcase - USA Election Data",
navbar = bs4DashNavbar(tags$h3("Leafdown Showcase - USA Election Data", style = "margin-bottom: .2rem;")),
bs4DashSidebar(disable = TRUE),
body = bs4DashBody(
# set the background of the map-container to be white
tags$head(
tags$style(HTML(".leaflet-container { background: #fff; height: 100%}")),
# workaround for the NA in leaflet legend see https://github.com/rstudio/leaflet/issues/615
tags$style(HTML(".leaflet-control div:last-child {clear: both;}")),
tags$style(HTML(".card {height: 100%;}")),
tags$style(HTML(".col-sm-12:last-child .card {margin-bottom: 0 !important;}")),
tags$style(HTML("#leafdown {height: 80% !important; margin-top: 10px; margin-bottom: 10px;}"))
),
# we need shinyjs for the leafdown map
useShinyjs(),
fluidRow(
# a card for the map
bs4Card(
title = "Map",
closable = FALSE,
collapsible = FALSE,
width = 6,
# a dropdown to select what KPI should be displayed on the map
selectInput(
"map_sel", "Select what KPI to display on the map:",
c("Votes" = "votes", "Unemployment" = "unemployment")
),
# the two buttons used for drilling
actionButton("drill_down", "Drill Down"),
actionButton("drill_up", "Drill Up"),
# the actual map element
leafletOutput("leafdown")
),
# a column with the two graphs
column(
width = 6,
# box for racial makeup graph
bs4Card(
width = 12,
closable = F,
collapsible = F,
title = "Racial makeup in percentages",
echarts4rOutput("socio")
),
# box for party percent graph
bs4Card(
width = 12,
closable = F,
collapsible = F,
title = "Votes in percent",
echarts4rOutput("party")
)
)
)
)
)
# Create user-defined function
percent <- function(x, digits = 2, format = "f", ...) {
paste0(formatC(x * 100, format = format, digits = digits, ...), "%")
}
create_labels <- function(data, map_level) {
labels <- sprintf(
"%s
Democrats: %s
Republicans: %s
Libertarians: %s
Green: %s
",
data[, paste0("NAME_", map_level)],
percent(data$Democrats2016),
percent(data$Republicans2016),
percent(data$Libertarians2016),
percent(data$Green2016)
)
labels %>% lapply(htmltools::HTML)
}
# Define server for leafdown app
server <- function(input, output) {
# load the shapes for the two levels
# TODO load the shapes you have downloaded via the raster package
states <- readRDS("../inst/app_election/us1.RDS")
counties <- readRDS("../inst/app_election/us2.RDS")
spdfs_list <- list(states, counties)
# create leafdown object
my_leafdown <- Leafdown$new(spdfs_list, "leafdown", input)
rv <- reactiveValues()
rv$update_leafdown <- 0
# observers for the drilling buttons
observeEvent(input$drill_down, {
my_leafdown$drill_down()
rv$update_leafdown <- rv$update_leafdown + 1
})
observeEvent(input$drill_up, {
my_leafdown$drill_up()
rv$update_leafdown <- rv$update_leafdown + 1
})
data <- reactive({
req(rv$update_leafdown)
# fetch the current metadata from the leafdown object
data <- my_leafdown$curr_data
# join the metadata with the election-data.
# depending on the map_level we have different election-data so the 'by' columns for the join are different
if (my_leafdown$curr_map_level == 2) {
data$ST <- substr(data$HASC_2, 4, 5)
# there are counties with the same name in different states so we have to join on both
data <- left_join(data, us_election_counties, by = c("NAME_2", "ST"))
} else {
data$ST <- substr(data$HASC_1, 4, 5)
data <- left_join(data, us_election_states, by = "ST")
}
# add the data back to the leafdown object
my_leafdown$add_data(data)
data
})
# this is where the leafdown magic happens
output$leafdown <- renderLeaflet({
req(spdfs_list)
req(data)
data <- data()
# depending on the selected KPI in the dropdown we show different data
if (input$map_sel == "unemployment") {
data$y <- data$Unemployment * 100
fillcolor <- leaflet::colorNumeric("Greens", data$y)
legend_title <- "Unemployment in Percent"
} else {
data$y <- ifelse(data$Republicans2016 > data$Democrats2016, "Republicans", "Democrats")
fillcolor <- leaflet::colorFactor(c("#232066", "#E91D0E"), data$y)
legend_title <- "Winning Party"
}
labels <- create_labels(data, my_leafdown$curr_map_level)
# draw the leafdown object
my_leafdown$draw_leafdown(
fillColor = ~ fillcolor(data$y),
weight = 3, fillOpacity = 1, color = "white", label = labels
) %>%
# set the view to be center on the USA
setView(-95, 39, 4) %>%
# add a nice legend
addLegend(
pal = fillcolor,
values = ~ data$y,
title = legend_title,
opacity = 1
)
})
# plots
output$socio <- renderEcharts4r({
df <- my_leafdown$curr_sel_data()
# check whether any shape is selected, show basic info for the whole usa if nothing is selected
if (nrow(df) > 0) {
if (my_leafdown$curr_map_level == 1) {
df <- df[, c("State", "Hispanic", "White", "Black", "Asian", "Amerindian", "Other")]
df <- df %>%
pivot_longer(2:7, "race") %>%
group_by(State)
df$value <- round(df$value, 2)
} else {
df <- df[, c("County", "Hispanic", "White", "Black", "Asian", "Amerindian", "Other")]
df <- df %>%
pivot_longer(2:7, "race") %>%
group_by(County)
df$value <- round(df$value / 100, 2)
}
} else {
# show basic info for the whole usa as no state is selected
df <- data.frame(
ST = "USA",
race = c("Hispanic", "White", "Black", "Asian", "Amerindian", "Other"),
value = c(0.15, 0.634, 0.134, 0.059, 0.015, 0.027)
) %>%
group_by(ST)
}
# create the graph
df %>%
e_charts(race) %>%
e_bar(value) %>%
e_tooltip(trigger = "axis", axisPointer = list(type = "shadow")) %>%
e_y_axis(
splitArea = list(show = FALSE),
splitLine = list(show = FALSE),
formatter = e_axis_formatter("percent", digits = 2)
) %>%
e_legend(orient = "vertical", right = 10, top = 10) %>%
e_color(brewer.pal(nrow(df), "Set3")) %>%
e_tooltip(formatter = e_tooltip_item_formatter("percent"))
})
output$party <- renderEcharts4r({
df <- my_leafdown$curr_sel_data()
# check whether any shape is selected, show general election-result if nothing is selected
if (nrow(df) > 0) {
if (my_leafdown$curr_map_level == 1) {
df <- df[, c("ST", "Democrats2016", "Republicans2016", "Libertarians2016", "Green2016")]
df <- df %>%
pivot_longer(2:5, "party") %>%
group_by(party)
} else {
df <- df[, c("County", "Democrats2016", "Republicans2016", "Libertarians2016", "Green2016")]
df <- df %>%
pivot_longer(2:5, "party") %>%
group_by(party)
df$value <- df$value
names(df)[1] <- "ST"
}
} else {
# show general election-result as no state is selected
df <- data.frame(
party = c("Democrats2016", "Republicans2016", "Libertarians2016", "Green2016"),
ST = "USA",
value = c(0.153, 0.634, 0.134, 0.059)
) %>%
group_by(party)
}
# create the graph
df %>%
e_charts(ST, stack = "grp") %>%
e_bar(value) %>%
e_y_axis(formatter = e_axis_formatter("percent", digits = 2)) %>%
e_tooltip(trigger = "axis", axisPointer = list(type = "shadow")) %>%
e_legend(right = 10, top = 10) %>%
e_color(c("#232066", "#E91D0E", "#f3b300", "#006900")) %>%
e_tooltip(formatter = e_tooltip_item_formatter("percent", digits = 2))
})
}
shinyApp(ui, server)
```
You can find the [full demo app](https://pega.shinyapps.io/election16/) hosted on shinyapps.io