11  shiny and leaflet for korea death rate

In this tutorial, we’ll create a web-based interactive map to visualize Korean death rates by region, disease, year, and gender. We’ll leverage the power of R’s Shiny framework for creating web applications and the leaflet library for mapping. This combination allows users to explore the data dynamically.

11.1 Setting Up Your Environment

1 Installing Required Packages

Before we begin, ensure you have the necessary R packages installed:

if(!require("readxl")) install.packages("readxl")
if(!require("tictoc")) install.packages("tictoc")
if(!require("tidyverse")) install.packages("tidyverse")
if(!require("sf")) install.packages("sf", dependencies = TRUE)
if(!require("leaflet")) install.packages("leaflet")
if(!require("leafgl")) install.packages("leafgl")
library(sf)
library(leaflet)
library(leafgl)
library(readxl)
library(tictoc)
  1. Loading and Preparing Data

For this tutorial, I’ve uploaded a pre-processed version of the data to GitHub as an RDS (R Data Serialization) file ( same to previous chapter).

Download the provided preprocessed data files kdth1.rds (death rate data) and sf_tr.rds (spatial data for Korean regions) from the specified GitHub repository. Load these files into R using readRDS(). Briefly inspect the data using functions like head(), str(), or summary().

kdeath1 <- "https://raw.githubusercontent.com/jinhaslab/opendata/main/data/kdth1.rds"
sf_tr <- "https://raw.githubusercontent.com/jinhaslab/opendata/main/data/sf_tr.rds"
if (!dir.exists("data")) {dir.create("data",  recursive = TRUE)}
download.file(kdeath1, "data/kdth1.rds")
download.file(sf_tr, "data/sf_tr.rds")

11.2 Create a Shiny App for Interactive Visualization

Now we can create a Shiny app to allow interactive visualization of the data:

  1. reusable function

Create a reusable function mapf() in a separate R script file named mapf.R (located in a ‘source’ folder). This function will generate the leaflet map based on user input:

mapf = function(dzchoice, yearchoice, sexchoice){
  rate = kdth1 %>%
    filter(cause == dzchoice, 
           year  == yearchoice, 
           sex   == sexchoice
    )
  sf_tr_m = sf_tr %>% 
    left_join(rate, by=c("CTP_ENG_NM"="region")) %>%
    mutate(rate = as.numeric(rate))
  
  rate_colors = 
    colorNumeric(
      palette = "YlOrRd",
      domain = sf_tr_m$rate
    )
  label_text = sprintf("Region: %s, <br> Rate: %s", 
                       sf_tr_m$CTP_ENG_NM, sf_tr_m$rate
  )
  
  korea_lat = 36 ; korea_lng = 128
  
  leaflet(data = sf_tr_m) %>%
    addProviderTiles("CartoDB.Positron") %>%  # Adds a light-themed base map from CartoDB
    setView(lng = korea_lng, lat = korea_lat, zoom = 7) %>%
    addPolygons(
      fillColor = ~rate_colors(rate),
      color = "#444444",   # Border color of the polygons
      weight = 1,          # Border width
      opacity = 1,
      fillOpacity = 0.7,   # Fill opacity of the polygons
      smoothFactor = 0.5,
      highlightOptions = highlightOptions(
        color = "white", 
        weight = 2,
        bringToFront = TRUE
      ), 
      label =  lapply(label_text, htmltools::HTML)
    ) %>%
    addLegend(
      pal = rate_colors, 
      values = sf_tr_m$rate
    )
  
  
}
  1. global
if(!require("sf")) install.packages("sf", dependencies = TRUE)
if(!require("leaflet")) install.packages("leaflet")
if(!require("leafgl")) install.packages("leafgl")
if(!require("shinydashboard")) install.packages("shinydashboard")
if (!require("shinyjqui")) install.packages("shinyjqui")
if(!require(shinyjs)) install.packages('shinyjs')
library(tidyverse)
library(sf)
library(leaflet)
library(leafgl)
library(readxl)
library(tictoc)
library(shinydashboard)
library(shinyjqui)
library(shinyjs)

tic()
sf_tr = readRDS("data/sf_tr.rds")
kdth1 = readRDS("data/kdth1.rds")
sf_tr$CTP_ENG_NM %>% sort()
toc()


dzchoice = kdth1 %>% pull(cause) %>% unique()
yearchoice= kdth1 %>% pull(year) %>% unique()
sexchoice = c("Total", "Male", "Female")

source("source/mapf.R")
  1. ui

Design the UI of the Shiny app:

We create a dashboardPage with a header, sidebar, and body. The body contains the leaflet map (leafglOutput(“map1”)) and input controls (disease, year, and gender selection) in an absolutePanel.

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Map", tabName = "map1", icon = icon("map"))
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(
        tabName = "map1",
        leafglOutput("map1"), 
        absolutePanel(
          selectInput("dzchoice",   "Disease", dzchoice), 
          selectInput("yearchoice", "Years",   yearchoice), 
          selectInput("sexchoice",  "Gender",  sexchoice) 
        )
        
        
        )
      )
    )
  )
  1. server

Define how the app reacts to user input:

server <- function(input, output, session) {
  output$map1 <- renderLeaflet({
    mapf(
      dzchoice = input$dzchoice,
      yearchoice = input$yearchoice,
      sexchoice = input$sexchoice
    )
  })
}

renderLeaflet: Renders the map generated by the mapf function based on the values the user selects in the UI controls.

11.3 update and customizing shiny

In this tutorial, we’ll take our interactive map to the next level! We’ll customize the user interface (UI) with CSS to give the app a more polished and visually appealing look. We’ll focus on:

  • Customizing fonts and styles.
  • Creating a draggable and collapsible options panel.
  • Making the map responsive to different screen sizes.

1: Setting Up and Customizing the UI

1.1 Include Custom CSS

Create a file named styles.css in a www folder within your Shiny app directory. Paste the following CSS code into it

  • Creates the appearance and hover effects for the controls panel (#controls).
.main-header .logo {
  font-family: "Georgia", Times, "Times New Roman", serif;
  font-weight: bold;
  font-size: 24px;
}


input[type="number"] {
  max-width: 80%;
}

div.outer {
  position: fixed;
  top: 41px;
  left: 0;
  right: 0;
  bottom: 0;
  overflow: hidden;
  padding: 0;
}

/* Customize fonts */
body, label, input, button, select { 
  font-family: 'Helvetica Neue', Helvetica;
  font-weight: 200;
}
h1, h2, h3, h4 { font-weight: 400; }

#controls {
  /* Appearance */
  background-color: white;
  padding: 0 20px 20px 20px;
  cursor: move;
  /* Fade out while not hovering */
  opacity: 0.65;
  zoom: 0.8;
  transition: opacity 500ms 1s;
}
#controls:hover {
  /* Fade in while hovering */
  opacity: 0.95;
  transition-delay: 0;
}

/* Position and style citation */
#cite {
  position: absolute;
  bottom: 10px;
  left: 10px;
  font-size: 12px;
}

/* If not using map tiles, show a white background */
.leaflet-container {
  background-color: white !important;
}

2 Modify the UI

Update your ui.R file to include the custom CSS and apply the necessary changes to your UI elements:

ui <- dashboardPage(
  dashboardHeader(),
  dashboardSidebar(
    sidebarMenu(
      menuItem("Map", tabName = "map1", icon = icon("map"))
    )
  ),
  dashboardBody(
    tabItems(
      tabItem(
        tabName = "map1",
        div(class="outer",
            tags$head(
              # Include our custom CSS
              includeCSS("www/styles.css"),
              tags$head(name="viewport", content="width=device-width, initial-scale=1.0")
            )),
        leafglOutput("map1", height="1000px"), 
        absolutePanel(
          id = "controls", class = "panel panel-default", fixed = TRUE,
          draggable = TRUE, top = "130", left = "auto", right = "50", bottom = "auto",
          width = "330", height = "auto",
          HTML('<button data-toggle="collapse" data-target="#demo">Options</button>'),
          tags$div(id='demo', class="collapse",
                   h2('interractive options'),
          selectInput("dzchoice",   "Disease", dzchoice), 
          selectInput("yearchoice", "Years",   yearchoice), 
          selectInput("sexchoice",  "Gender",  sexchoice) 
        ))
        
        
        )
      )
    )
  )
  • CSS Inclusion: We use includeCSS(“www/styles.css”) to link our custom CSS file to the Shiny app.

  • Outer Container (div.outer):

    • This div is positioned to fill the available space below the Shiny header and sidebar, allowing the map to occupy the entire viewport.
    • It also sets overflow: hidden to prevent content from spilling outside its boundaries.
  • Viewport Meta Tag: The

    tag is essential for making the app responsive on mobile devices.

  • Collapsible Options:

    • The options panel is made collapsible using a Bootstrap collapse element. The “Options” button is used to toggle its visibility. This provides a cleaner look when the options aren’t needed.
    • The absolutePanel is made draggable using draggable = TRUE.