Search code examples
javascriptrshinyr-leaflet

Manipulate existing Leaflet map in a shiny app with javascript using shinyjs


I have a shiny app with an existing leaflet map in it. I want to be able to manipulate this map after rendering, using custom javascript through the shinyjs package. A minimal example is below:

app.R

# packages ----------------------------------------------------------------

library(dplyr)
library(leaflet)
library(shiny)
library(shinyjs)

# ui ----------------------------------------------------------------------

ui <- fluidPage(
  
  useShinyjs(),
  extendShinyjs(script = "my_js.js"),
  
  leafletOutput(outputId = "map", height = "80vh"),
  
  tags$hr(),
  tags$p("Button to run the JS code"),
  actionButton(inputId = "go", label = "Add a Marker")
  
)

# server ------------------------------------------------------------------

server <- function(input, output, session){
  
  # define a simple map
  output$map <- renderLeaflet({

    leaflet() %>%
      addProviderTiles("CartoDB.Positron")
  })
  
  # observe the go button and run the shinyjs.addMarker function
  observeEvent(
    eventExpr = input$go,
    handlerExpr = js$addMarker()
  )
  
}

# run ---------------------------------------------------------------------

shinyApp(ui = ui, server = server)

my_js.js

shinyjs.addMarker = function(){
  
  // get the map - this bit doesn't work!
  var map = document.getElementById('map');
  
  // create a marker and add to map
  var marker = new L.marker([53, -1]).addTo(map);
  
  // really I'd be going off and querying an API, or doing
  // something else for which there is no handy R function.

};

The question really is how to access the map object after it has been created. Obviously with this example I'm just adding a marker, which I could do with leafletProxy(), but in reality I want to query an API and bring extra data onto the map when the user performs an action.


Solution

  • You can reach the map object with htmlwidtget's onRender() function. Then, you might save it to the globally scoped variable (by skipping the var keyword while creating the variable). This becomes available anywhere in the JavaScript code.

    output$map <- renderLeaflet({
        leaflet() %>%
            addProviderTiles("CartoDB.Positron") %>%
            htmlwidgets::onRender("
                function(el,x) {
                    map = this;
                }
            ")
    })
    

    Your my_js.js would then look as follows:

    shinyjs.addMarker = function(){
    
      // create a marker and add to map
      var marker = new L.marker([53, -1]).addTo(map);
    
      // really I'd be going off and querying an API, or doing
      // something else for which there is no handy R function.
    
    };