Search code examples
rshinyr-leaflet

Replace marker when button clicked


I'm starting a Shiny app with a leaflet map in it.

I need the user to be able to place two separate markers (origin & destination) on the map, and potentially replace them later on.

So what I did is create an origin button and a destination button, so that when the user clicks one of them, they'll place or update the corresponding marker with their next click on the map.

library(shiny)
library(shinyWidgets)
library(leaflet)
library(RColorBrewer)
library(osrm)

ui <- bootstrapPage(
  tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
  leafletOutput("map", width = "100%", height = "100%"),
  absolutePanel(top = 10, right = 10,
                actionButton("change_orig", "Origine"),
                actionButton("change_dest", "Destination"),
  )
)

server <- function(input, output, session) {
  
  origin_icon = makeIcon("https://static.thenounproject.com/png/1477944-200.png",
                         iconWidth=24, iconHeight=30)
  destination_icon = makeIcon("https://static.thenounproject.com/png/924206-200.png",
                              iconWidth=24, iconHeight=30)
  
  output$map <- renderLeaflet({
    leaflet() %>% addTiles(providers$OpenStreetMap) %>%
      addProviderTiles(providers$OpenStreetMap, group="Open Street Map") %>% 
      addProviderTiles(providers$OpenTopoMap, group="Open Topo Map") %>% 
      fitBounds(2.907730, 45.856550, 3.310954, 45.714034) %>%
      addLayersControl(
        baseGroups = c("Open Street Map", "Open Topo Map")
      )
    
  })
  
  # When "Origin" is selected, 
  # a click on the map will (re)place the origin marker.
  observeEvent(input$change_orig, {
    observeEvent(input$map_click, {
      click = input$map_click
      leafletProxy('map')%>%
        clearGroup("origin") %>%
        addMarkers(lng=click$lng, lat=click$lat, icon=origin_icon,
                   group="origin")
      })
  })
  
  # When "Destination" is selected, 
  # a click on the map will (re)place the destination marker.
  observeEvent(input$change_dest, {
    observeEvent(input$map_click, {
      click = input$map_click
      leafletProxy('map') %>%
        clearGroup("destination") %>%
        addMarkers(lng=click$lng, lat=click$lat, icon=destination_icon,
                   group="destination")
    })
  })
  
  
}

shinyApp(ui, server)

And I'm facing a couple problems here.

  • If I have already clicked on the map before clicking on one of the buttons, the app will consider the previous click and add a marker there.
  • Most importantly: even though I create origin marker and destination markers in separate groups, clearGroup deletes both destination and origin markers and recreates both at the same location.

How could I make sure to only create or replace one marker without affecting the other?

And how could I only listen to the click event after button's been selected?


Solution

  • So I ended up going for a radioButtons instead of two actionButtons.

    Then in the observeEvent for map_click, I used a simple if to check which of the radio buttons was selected:

    library(shiny)
    library(shinyWidgets)
    library(leaflet)
    library(RColorBrewer)
    library(osrm)
    
    ui <- bootstrapPage(
      tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
      leafletOutput("map", width = "100%", height = "100%"),
      absolutePanel(top = 10, right = 10,
                    radioButtons(
                      'orig_dest_switch',
                      "Changer l'origine ou la destination",
                      choices = c("Origine", "Destination"),
                      choiceValues = c("orig", "dest"),
                      inline = TRUE),
      )
    )
    
    server <- function(input, output, session) {
      
      origin_icon = makeIcon("https://static.thenounproject.com/png/1477944-200.png",
                             iconWidth=24, iconHeight=30)
      destination_icon = makeIcon("https://static.thenounproject.com/png/924206-200.png",
                                  iconWidth=24, iconHeight=30)
      
      
      output$map <- renderLeaflet({
        leaflet() %>% addTiles(providers$OpenStreetMap) %>%
          addProviderTiles(providers$OpenStreetMap, group="Open Street Map") %>% 
          addProviderTiles(providers$OpenTopoMap, group="Open Topo Map") %>% 
          fitBounds(2.907730, 45.856550, 3.310954, 45.714034) %>%
          addLayersControl(
            baseGroups = c("Open Street Map", "Open Topo Map")
          )
        
      })
      
      observeEvent(input$map_click, {
        click = input$map_click
        # Vérifier si l'on update le marker d'origine ou de destination
        if(input$orig_dest_switch=="Origine"){
          origin = list(lng=click$lng, lat=click$lat)
          leafletProxy('map')%>%
            clearGroup("origin") %>%
            addMarkers(lng=click$lng, lat=click$lat, icon=origin_icon,
                       group="origin")
        } else {
          destination = list(lng=click$lng, lat=click$lat)
          leafletProxy('map')%>%
            clearGroup("destination") %>%
            addMarkers(lng=click$lng, lat=click$lat, icon=destination_icon,
                       group="destination")
        }
      })
      
    }
    
    shinyApp(ui, server)