Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Integrating time series graphs and leaflet maps using R shiny

I have data/results that contain both a geocode location (latitude/longitude) and a date/time stamp that I would like to interact with using R shiny. I have created R shiny apps that contain several leaflet maps (leaflet R package) and also contain time series graphs (dygraphs R package). I know how to synchronize different dygraphs (https://rstudio.github.io/dygraphs/gallery-synchronization.html), but not sure how to synchronize it to a leaflet map too. My question is how best to link all the graphs together, so when I select a region on a leaflet map or period of time on a dygraph time series graph the other graphs are all updated to show only that filtered data?

One thought I had was to use a leaflet plugin, but not sure how to do this with R/shiny? For example, I see some leaflet plugins offer the capability to animate a map that contains date/time information (http://apps.socib.es/Leaflet.TimeDimension/examples/). Another question is there any documentation/examples showing how to work with leaflet plugins using R shiny?

I think it is possible to extract the time/date that is selected from a time series graph (dygraph), but not sure if/how to extract the region that is displayed on the leaflet map in R shiny. My last question is whether if it is possible how I could extract the region over which the leaflet map is displayed, so I can update the time series graph.

Thanks in advance for any suggestions on how to couple leaflet maps with a time series graphs (i.e., dygraph) using R shiny!

like image 310
Kevin Avatar asked Aug 04 '15 16:08

Kevin


1 Answers

This will probably be more of a continuous discussion than a single answer.

Fortunately, your question involves htmlwidgets created by RStudio who also made Shiny. They have taken extra effort to integrate Shiny communication into both dygraphs and leaflet. This is not the case for many other htmlwidgets. For a broader discussion of intra-htmlwidget communication outside of Shiny, I would recommend following this Github issue.

part 1 - leaflet control dygraph

As my first example, we'll let leaflet control dygraphs, so clicking on a state in Mexico will limit the dygraph plot to just that state. I should give credit to these three examples.

  1. Kyle Walker's Rpub Mexico Choropleth Leaflet
  2. Shiny example included in leaflet
  3. Diego Valle Crime in Mexico project

R Code

  # one piece of an answer to this StackOverflow question
  #  http://stackoverflow.com/questions/31814037/integrating-time-series-graphs-and-leaflet-maps-using-r-shiny

  # for this we'll use Kyle Walker's rpubs example
  #   http://rpubs.com/walkerke/leaflet_choropleth
  # combined with data from Diego Valle's crime in Mexico project
  #   https://github.com/diegovalle/mxmortalitydb

  # we'll also build on the shiny example included in leaflet
  #  https://github.com/rstudio/leaflet/blob/master/inst/examples/shiny.R

  library(shiny)
  library(leaflet)
  library(dygraphs)
  library(rgdal)

  # let's build this in advance so we don't download the
  #    data every time
  tmp <- tempdir()
  url <- "http://personal.tcu.edu/kylewalker/data/mexico.zip"
  file <- basename(url)
  download.file(url, file)
  unzip(file, exdir = tmp)
  mexico <- {
    readOGR(dsn = tmp, layer = "mexico", encoding = "UTF-8")
    #delete our files since no longer need
    on.exit({unlink(tmp);unlink(file)})
  }
  pal <- colorQuantile("YlGn", NULL, n = 5)

  leaf_mexico <- leaflet(data = mexico) %>%
    addTiles() %>%
    addPolygons(fillColor = ~pal(gdp08), 
                fillOpacity = 0.8, 
                color = "#BDBDC3", 
                weight = 1,
                layerId = ~id)

  # now let's get our time series data from Diego Valle
  crime_mexico <- jsonlite::fromJSON(
    "https://rawgit.com/diegovalle/crimenmexico.diegovalle.net/master/assets/json/states.json"
  )

  ui <- fluidPage(
    leafletOutput("map1"),
    dygraphOutput("dygraph1",height = 200),
    textOutput("message", container = h3)
  )

  server <- function(input, output, session) {
    v <- reactiveValues(msg = "")

    output$map1 <- renderLeaflet({
      leaf_mexico
    })

    output$dygraph1 <- renderDygraph({
      # start dygraph with all the states
      crime_wide <- reshape(
        crime_mexico$hd[,c("date","rate","state_code"),drop=F],
        v.names="rate",
        idvar = "date",
        timevar="state_code",
        direction="wide"
      )
      colnames(crime_wide) <- c("date",as.character(mexico$state))
      rownames(crime_wide) <- as.Date(crime_wide$date)
      dygraph(
        crime_wide[,-1]
      )
    })

    observeEvent(input$map1_shape_mouseover, {
      v$msg <- paste("Mouse is over shape", input$map1_shape_mouseover$id)
    })
    observeEvent(input$map1_shape_mouseout, {
      v$msg <- ""
    })
    observeEvent(input$map1_shape_click, {
      v$msg <- paste("Clicked shape", input$map1_shape_click$id)
      #  on our click let's update the dygraph to only show
      #    the time series for the clicked
      state_crime_data <- subset(crime_mexico$hd,state_code == input$map1_shape_click$id)
      rownames(state_crime_data) <- as.Date(state_crime_data$date)
      output$dygraph1 <- renderDygraph({
        dygraph(
          xts::as.xts(state_crime_data[,"rate",drop=F]),
          ylab = paste0(
            "homicide rate ",
            as.character(mexico$state[input$map1_shape_click$id])
          )
        )
      })
    })
    observeEvent(input$map1_zoom, {
      v$msg <- paste("Zoom changed to", input$map1_zoom)
    })
    observeEvent(input$map1_bounds, {
      v$msg <- paste("Bounds changed to", paste(input$map1_bounds, collapse = ", "))
    })

    output$message <- renderText(v$msg)
  }

  shinyApp(ui, server)

part 2 dygraph control leaflet + part 1 leaflet control dygraph

# one piece of an answer to this StackOverflow question
#  http://stackoverflow.com/questions/31814037/integrating-time-series-graphs-and-leaflet-maps-using-r-shiny

# for this we'll use Kyle Walker's rpubs example
#   http://rpubs.com/walkerke/leaflet_choropleth
# combined with data from Diego Valle's crime in Mexico project
#   https://github.com/diegovalle/mxmortalitydb

# we'll also build on the shiny example included in dygraphs
#  https://github.com/rstudio/leaflet/blob/master/inst/examples/shiny.R

library(shiny)
library(leaflet)
library(dygraphs)
library(dplyr)
library(rgdal)

# let's build this in advance so we don't download the
#    data every time
tmp <- tempdir()
url <- "http://personal.tcu.edu/kylewalker/data/mexico.zip"
file <- basename(url)
download.file(url, file)
unzip(file, exdir = tmp)
mexico <- {
  #delete our files since no longer need
  on.exit({unlink(tmp);unlink(file)})  
  readOGR(dsn = tmp, layer = "mexico", encoding = "UTF-8")
}

# now let's get our time series data from Diego Valle
crime_mexico <- jsonlite::fromJSON(
  "https://rawgit.com/diegovalle/crimenmexico.diegovalle.net/master/assets/json/states.json"
)

# instead of the gdp data, let's use mean homicide_rate
#   for our choropleth
mexico$homicide <- crime_mexico$hd %>%
  group_by( state_code ) %>%
  summarise( homicide = mean(rate) ) %>%
  ungroup() %>%
  select( homicide ) %>%
  unlist


pal <- colorBin(
  palette = RColorBrewer::brewer.pal(n=9,"YlGn")[-(1:2)]
  , domain = c(0,50)
  , bins =7
)

popup <- paste0("<strong>Estado: </strong>", 
                      mexico$name, 
                      "<br><strong>Homicide Rate: </strong>", 
                      round(mexico$homicide,2)
          )

leaf_mexico <- leaflet(data = mexico) %>%
  addTiles() %>%
  addPolygons(fillColor = ~pal(homicide), 
              fillOpacity = 0.8, 
              color = "#BDBDC3", 
              weight = 1,
              layerId = ~id,
              popup = popup
              )


ui <- fluidPage(
  leafletOutput("map1"),
  dygraphOutput("dygraph1",height = 200),
  textOutput("message", container = h3)
)

server <- function(input, output, session) {
  v <- reactiveValues(msg = "")

  output$map1 <- renderLeaflet({
    leaf_mexico
  })

  output$dygraph1 <- renderDygraph({
    # start dygraph with all the states
    crime_wide <- reshape(
      crime_mexico$hd[,c("date","rate","state_code"),drop=F],
      v.names="rate",
      idvar = "date",
      timevar="state_code",
      direction="wide"
    )
    colnames(crime_wide) <- c("date",as.character(mexico$state))
    rownames(crime_wide) <- as.Date(crime_wide$date)
    dygraph( crime_wide[,-1])  %>%
      dyLegend( show = "never" )
  })

  observeEvent(input$dygraph1_date_window, {
    if(!is.null(input$dygraph1_date_window)){
      # get the new mean based on the range selected by dygraph
      mexico$filtered_rate <- crime_mexico$hd %>%
      filter( 
              as.Date(date) >= as.Date(input$dygraph1_date_window[[1]]),
              as.Date(date) <= as.Date(input$dygraph1_date_window[[2]])  
            ) %>%
      group_by( state_code ) %>%
      summarise( homicide = mean(rate) ) %>%
      ungroup() %>%
      select( homicide ) %>%
      unlist

      # leaflet comes with this nice feature leafletProxy
      #  to avoid rebuilding the whole map
      #  let's use it
      leafletProxy( "map1", data = mexico  ) %>%
        removeShape( layerId = ~id ) %>%
        addPolygons( fillColor = ~pal( filtered_rate ), 
                    fillOpacity = 0.8, 
                    color = "#BDBDC3", 
                    weight = 1,
                    layerId = ~id,
                    popup = paste0("<strong>Estado: </strong>", 
                        mexico$name, 
                        "<br><strong>Homicide Rate: </strong>", 
                        round(mexico$filtered_rate,2)
                    )
                    )
    }
  })

  observeEvent(input$map1_shape_click, {
    v$msg <- paste("Clicked shape", input$map1_shape_click$id)
    #  on our click let's update the dygraph to only show
    #    the time series for the clicked
    state_crime_data <- subset(crime_mexico$hd,state_code == input$map1_shape_click$id)
    rownames(state_crime_data) <- as.Date(state_crime_data$date)
    output$dygraph1 <- renderDygraph({
      dygraph(
        xts::as.xts(state_crime_data[,"rate",drop=F]),
        ylab = paste0(
          "homicide rate ",
          as.character(mexico$state[input$map1_shape_click$id])
        )
      )
    })
  })

}

shinyApp(ui, server)
like image 80
timelyportfolio Avatar answered Nov 02 '22 12:11

timelyportfolio