4 tricks for working with R, Leaflet and Shiny
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.
I recently worked on a dataviz project involving Shiny and the Leaflet library. In this post I give 4 handy tricks we used to improve the app: 1/ how to use leaflet native widgets 2/ how to trigger an action when user clicks on map 3/ how to add a research bar on your map 4/ how to propose a “geolocalize me” button. For each trick, a reproducible code snippet is provided, so you just have to copy and paste it to reproduce the image.
Trick 1: use leaflet native control widget
Shiny is used to add interactivity to your dataviz. Working on maps, it’s great to add a widget to allow users to switch between datasets, using one layer or another… Of course, this can be achieved using a regular RadioButton or any other shiny widget, building a new map each time.
But Leaflet enables you to build the control widget directly while doing the map. Every layer you create must be added to the same map, all attributed to a “group“. Then, the control widget will allow you to switch from one group to another. The code below should help you understand the concept, and everything is explained in more detail on the amazing Rstudio tutorial.
Trick 2: make a graph depending on click position
It is possible to code the map so that clicking on a certain point opens particular information. In this example, the user chooses a marker on the map, which makes either a barplot OR a scatterplot. By adding an ID to every marker of the map, using the layerId argument, the click information is saved in a reactive value than can be used to run specific functions!
Trick 3: add a search bar
The ggmap library allows you to get the coordinates of any place in the world based on a google search. You can use this feature to create a search bar in addition to the leaflet map! The textInput is passed to the geocode function of ggmap. It returns coordinates that are given to leaflet to zoom on the map.
Trick 4: use geolocalization
When the user clicks on the “localize me” button, the leaflet map automatically zooms to the user’s position. This trick uses a Javascript function (but no Javascript knowledge needed). This example is inspired from here.
This post has been first published on the R graph gallery, a website that displays hundreds of R charts, always with the reproducible code! You can follow the gallery on Twitter: @R_Graph_Gallery
Code | Trick1 | Widget
# Load libraries library(shiny) library(leaflet) # Make data with several positions data_red=data.frame(LONG=42+rnorm(10), LAT=23+rnorm(10), PLACE=paste("Red_place_",seq(1,10))) data_blue=data.frame(LONG=42+rnorm(10), LAT=23+rnorm(10), PLACE=paste("Blue_place_",seq(1,10))) # Initialize the leaflet map: leaflet() %>% setView(lng=42, lat=23, zoom=8 ) %>% # Add two tiles addProviderTiles("Esri.WorldImagery", group="background 1") %>% addTiles(options = providerTileOptions(noWrap = TRUE), group="background 2") %>% # Add 2 marker groups addCircleMarkers(data=data_red, lng=~LONG , lat=~LAT, radius=8 , color="black", fillColor="red", stroke = TRUE, fillOpacity = 0.8, group="Red") %>% addCircleMarkers(data=data_blue, lng=~LONG , lat=~LAT, radius=8 , color="black", fillColor="blue", stroke = TRUE, fillOpacity = 0.8, group="Blue") %>% # Add the control widget addLayersControl(overlayGroups = c("Red","Blue") , baseGroups = c("background 1","background 2"), options = layersControlOptions(collapsed = FALSE))
Code | Trick2 | Graph depends on click
library(shiny) library(leaflet) server <- function(input, output) { # build data with 2 places data=data.frame(x=c(130, 128), y=c(-22,-26), id=c("place1", "place2")) # create a reactive value that will store the click position data_of_click <- reactiveValues(clickedMarker=NULL) # Leaflet map with 2 markers output$map <- renderLeaflet({ leaflet() %>% setView(lng=131 , lat =-25, zoom=4) %>% addTiles(options = providerTileOptions(noWrap = TRUE)) %>% addCircleMarkers(data=data, ~x , ~y, layerId=~id, popup=~id, radius=8 , color="black", fillColor="red", stroke = TRUE, fillOpacity = 0.8) }) # store the click observeEvent(input$map_marker_click,{ data_of_click$clickedMarker <- input$map_marker_click }) # Make a barplot or scatterplot depending of the selected point output$plot=renderPlot({ my_place=data_of_click$clickedMarker$id if(is.null(my_place)){my_place="place1"} if(my_place=="place1"){ plot(rnorm(1000), col=rgb(0.9,0.4,0.1,0.3), cex=3, pch=20) }else{ barplot(rnorm(10), col=rgb(0.1,0.4,0.9,0.3)) } }) } ui <- fluidPage( br(), column(8,leafletOutput("map", height="600px")), column(4,br(),br(),br(),br(),plotOutput("plot", height="300px")), br() ) shinyApp(ui = ui, server = server)
Code | Trick3 | Search Bar
library(shiny) library(leaflet) library(ggmap) server <- function(input, output) { output$map <- renderLeaflet({ # Get latitude and longitude if(input$target_zone=="Ex: Bamako"){ ZOOM=2 LAT=0 LONG=0 }else{ target_pos=geocode(input$target_zone) LAT=target_pos$lat LONG=target_pos$lon ZOOM=12 } # Plot it! leaflet() %>% setView(lng=LONG, lat=LAT, zoom=ZOOM ) %>% addProviderTiles("Esri.WorldImagery") }) } ui <- fluidPage( br(), leafletOutput("map", height="600px"), absolutePanel(top=20, left=70, textInput("target_zone", "" , "Ex: Bamako")), br() ) shinyApp(ui = ui, server = server)
Code | Trick4 | Geolocalization
# ==== libraries library(shiny) library(leaflet) library(shinyjs) # ==== fonction allowing geolocalisation jsCode <- ' shinyjs.geoloc = function() { navigator.geolocation.getCurrentPosition(onSuccess, onError); function onError (err) { Shiny.onInputChange("geolocation", false); } function onSuccess (position) { setTimeout(function () { var coords = position.coords; console.log(coords.latitude + ", " + coords.longitude); Shiny.onInputChange("geolocation", true); Shiny.onInputChange("lat", coords.latitude); Shiny.onInputChange("long", coords.longitude); }, 5) } }; ' # ==== server server <- function(input, output) { # Basic map output$map <- renderLeaflet({ leaflet() %>% setView(lng=0, lat=0, zoom=2 ) %>% addProviderTiles("Esri.WorldImagery") }) # Find geolocalisation coordinates when user clicks observeEvent(input$geoloc, { js$geoloc() }) # zoom on the corresponding area observe({ if(!is.null(input$lat)){ map <- leafletProxy("map") dist <- 0.2 lat <- input$lat lng <- input$long map %>% fitBounds(lng - dist, lat - dist, lng + dist, lat + dist) } }) } # ==== UI ui <- fluidPage( # Tell shiny we will use some Javascript useShinyjs(), extendShinyjs(text = jsCode), # One button and one map br(), actionButton("geoloc", "Localize me", class="btn btn-primary", onClick="shinyjs.geoloc()"), leafletOutput("map", height="600px") ) shinyApp(ui = ui, server = server)1
R-bloggers.com offers daily e-mail updates about R news and tutorials about learning R and many other topics. Click here if you're looking to post or find an R/data-science job.
Want to share your content on R-bloggers? click here if you have a blog, or here if you don't.