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.
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.
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.
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)
# 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)