Search code examples
rshinyr-leaflet

How to click on the map and add a circle?


I have been trying this but I couldn't manage to get it done. Online resources for R leaflet are not enough too. Really need to get this done.

ui.R -->

library(shiny)
library(ggmap)
library(leaflet)

shinyUI(bootstrapPage(
  leafletOutput("map"),
  br(),
  verbatimTextOutput("out")
)
)

server.R -->

library(shiny)
library(ggmap)
library(leaflet)


shinyServer(function(input, output, session) {


output$map <- renderLeaflet({

 p <- input$map_click
     
 if(is.null(p)){
   leaflet() %>% setView(lng = -43.1729, lat = -22.9068, zoom = 11) %>%
     addTiles(options = providerTileOptions(noWrap = TRUE)) 
 }
 
 else{
   address <- revgeocode(c(p$lng,p$lat))
   
   leaflet() %>% setView(lng = p$lng, lat = p$lat, zoom = 16) %>%
   addTiles(options = providerTileOptions(noWrap = TRUE)) %>%
   addCircles(p$lng, p$lat, weight = 1, radius = 100, color =  "black",
                    fillColor = "orange", popup = address, fillOpacity=0.5, opacity=1)
 }


})

output$out <- renderPrint({
validate(need(input$map_click, FALSE))
click <- input$map_click
clat <- click$lat
clng <- click$lng
address <- revgeocode(c(clng,clat))
print(clat)
print(clng)
print(address)

})

})

Solution

  • TL; DR

    1. Create the initial map so it is not dependent on user input.
    2. Use an observer that responds to user clicks to update the map.
    3. Use leafletProxy to update the map without rendering everything all over again.

    I would do this by making your original map and using the leafletProxy function to update the map when the user clicks on locations. There is a tutorial on the Rstudio site somewhere where they show how to do this. It will hopefully save some computation, since the map won't be re-rendered every time a circle is added.

    I also add a couple additional things I would consider: putting the circle data in a reactive dataset, and maintaining the circles in a group, thus allowing you to easily hide/show them with an additional observer/button.

    Here, is a working example. For the record, I'm using the leaflet version from github (and recommend this since this package is under active development). You can get it with devtools::install_github('rstudio/leaflet'). There are at least a couple new features that I don't think are on CRAN yet -- like easily being able to create custom markers.

    library(shiny)
    library(ggmap)
    library(leaflet)
    
    ui <- shinyUI(bootstrapPage(
      leafletOutput("map")
    ))
    
    server <- shinyServer(function(input, output, session) {
      ## One alternative: store circles data?
      ## I dont actually implement this, but you would do this in the observer as well
      dat <- reactiveValues(circs = data.frame(lng=numeric(), lat=numeric()))
    
      ## Make your initial map
      output$map <- renderLeaflet({
        leaflet() %>%
          setView(lng = -43.1729, lat = -22.9068, zoom = 11) %>%
            addTiles(options = providerTileOptions(noWrap = TRUE)) 
      })
    
      ## Observe mouse clicks and add circles
      observeEvent(input$map_click, {
        ## Get the click info like had been doing
        click <- input$map_click
        clat <- click$lat
        clng <- click$lng
        address <- revgeocode(c(clng,clat))
    
        ## Add the circle to the map proxy
        ## so you dont need to re-render the whole thing
        ## I also give the circles a group, "circles", so you can
        ## then do something like hide all the circles with hideGroup('circles')
        leafletProxy('map') %>% # use the proxy to save computation
          addCircles(lng=clng, lat=clat, group='circles',
                     weight=1, radius=100, color='black', fillColor='orange',
                     popup=address, fillOpacity=0.5, opacity=1)
      })
    
    })
    
    shinyApp(ui=ui, server=server)
    

    The result should look something like this (I've zoomed in, added some circles and activated a popup).

    enter image description here