4 tricks for working with R, Leaflet and Shiny

[This article was first published on Blog – The R graph Gallery, and kindly contributed to R-bloggers]. (You can report issue about the content on this page here)
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

To leave a comment for the author, please follow the link and comment on their blog: Blog – The R graph Gallery.

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.

Never miss an update!
Subscribe to R-bloggers to receive
e-mails with the latest R posts.
(You will not see this message again.)

Click here to close (This popup will not appear again)