简体   繁体   中英

Can't get Leaflet.Spin plugin working in R Shiny

I have a shiny app which involves drawing a large number of lines on a map. I would like to use a spinner to show the user that rendering is underway. Most shiny approaches don't work because they only show the spinner while the data is being sent to leaflet, not when leaflet is rendering. The Leaflet.Spin plugin looks promising but I have been struggling to get it to work. The examples I have been following are

https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92

leaflet plugin and leafletProxy with polylineDecorator as Example

How do I get the js events to fire properly and show Leaflet.Spin when the lines (circles in this example) are rendering? Thanks!

Update: Spinner now works, but events fire for each individual circle added, so if number of circles declines, spinner doesn't turn off correctly.

library(shiny)
library(leaflet)
library(htmltools) # for htmlDependency
library(htmlwidgets) # for onRender

# https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
# https://stackoverflow.com/questions/52846472/leaflet-plugin-and-leafletproxy-with-polylinedecorator-as-example
spinPlugin <- htmlDependency(
  "spin.js", 
  "2.3.2",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2"),
  script = "spin.min.js") # there's no spin.css

leafletspinPlugin <- htmlDependency(
  "Leaflet.Spin", 
  "1.1.2",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2"),
  script = "leaflet.spin.min.js")

registerPlugin <- function(map, plugin) {
  map$dependencies <- c(map$dependencies, list(plugin))
  map
}

# Note: Ctrl-Shift-J opens the javascript console in the browser
spin_event <- "function(el, x) {
  console.log('spin event added'); 
  var mymap = this;
  mymap.on('layerremove', function(e) {
    console.log('layerremove fired');
    mymap.spin(true);
  });
  mymap.on('layeradd', function(e) {
    console.log('layeradd fired');
    mymap.spin(false); 
  });
}"

dlat <- 1 / 111000 * 100 # degrees per metre

ui <- fluidRow(
  tags$h2("Using Leaflet.Spin in Shiny"),
  actionButton("plotbutton", label = "Show Spinner While Adding Markers"),
  leafletOutput("map")
)

server <- function(input, output, session) {
  output$map <- renderLeaflet({
    cat("renderLeaflet\n")
    leaflet() %>%
      addTiles() %>%
      setView(175.322, -37.789, zoom = 17) %>% 
      registerPlugin(spinPlugin) %>% 
      registerPlugin(leafletspinPlugin) %>% 
      onRender(spin_event) %>% 
      clearShapes() %>% # initialise spinner
      addCircles(
        lng = 175.322,
        lat = -37.789,
        radius = 0,
        opacity = 0
      )
  })

  observeEvent(input$plotbutton, {
    cat("input$plotbutton\n")
    n <- ceiling(runif(1) * 10000)
    leafletProxy("map") %>%
      clearShapes() %>% 
      addCircles(
        lng = 175.322 + (runif(n) * 2 - 1) * dlat * 6,
        lat = -37.789 + (runif(n) * 2 - 1) * dlat * 1.5,
        radius = dlat * runif(n) * dlat
      )
  })
}

shinyApp(ui = ui, server = server)

One year late, but I was looking for a way to have a busy spinner for leaflet.

I realized that your original code would create x number of spin instances on clearShapes() . If the following addCircles() has less than x number of circles, there would be spin instances left running.

My solution is to use a dummy layer to watch for from Javascript. In the example below, a circle with layerId = 'spinnerMarker' . To start the leafletproxy update, that layer is removed with removeShape(layerId = 'spinnerMarker') which triggers JS layerremove . The data circles are then added. The update is ended by adding a circle with layerId = 'spinnerMarker' to trigger the JS layeradd .

In JS, the layer being added or removed is checked with e.layer.options.layerId == 'spinnerMarker' to run mymap.spin() .

This way there is only one spin instance running.

library(shiny)
library(leaflet)
library(htmltools) # for htmlDependency
library(htmlwidgets) # for onRender

# https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
# https://stackoverflow.com/questions/52846472/leaflet-plugin-and-leafletproxy-with-polylinedecorator-as-example
spinPlugin <- htmlDependency(
  "spin.js", 
  "2.3.2",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2"),
  script = "spin.min.js") # there's no spin.css

leafletspinPlugin <- htmlDependency(
  "Leaflet.Spin", 
  "1.1.2",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2"),
  script = "leaflet.spin.min.js")

registerPlugin <- function(map, plugin) {
  map$dependencies <- c(map$dependencies, list(plugin))
  map
}

# Note: Ctrl-Shift-J opens the javascript console in the browser
spin_event <- "function(el, x) {
  console.log('spin event added'); 
  var mymap = this;
  mymap.on('layerremove', function(e) {
    console.log('layerremove fired');
    if (e.layer.options.layerId == 'spinnerMarker') {
      console.log(e.layer.options.layerId);
      mymap.spin(true);
    }
  });
  mymap.on('layeradd', function(e) {
    console.log('layeradd fired');
    if (e.layer.options.layerId == 'spinnerMarker') {
      console.log(e.layer.options.layerId);
      mymap.spin(false);
    }
  });
}"

dlat <- 1 / 111000 * 100 # degrees per metre

ui <- fluidRow(
  tags$h2("Using Leaflet.Spin in Shiny"),
  actionButton("plotbutton", label = "Show Spinner While Adding Markers"),
  leafletOutput("map")
)

server <- function(input, output, session) {
  output$map <- renderLeaflet({
    cat("renderLeaflet\n")
    leaflet() %>%
      addTiles() %>%
      setView(175.322, -37.789, zoom = 17) %>% 
      registerPlugin(spinPlugin) %>% 
      registerPlugin(leafletspinPlugin) %>% 
      onRender(spin_event) %>% 
      clearShapes() %>% # initialise spinner
      addCircles(     # invisible placeholder
        lng = 175.322,
        lat = -37.789,
        radius = 0,
        opacity = 0,
        layerId = 'spinnerMarker'   # identifier, can be found in js: e.layer.options.layerId
      )
  })
  
  observeEvent(input$plotbutton, {
    cat("input$plotbutton\n")
    n <- ceiling(runif(1) * 10000)
    leafletProxy("map") %>%
      removeShape(layerId = 'spinnerMarker') %>%    # this triggers mymap.spin(true)
      clearShapes() %>% 
      addCircles(
        lng = 175.322 + (runif(n) * 2 - 1) * dlat * 6,
        lat = -37.789 + (runif(n) * 2 - 1) * dlat * 1.5,
        radius = dlat * runif(n) * dlat
      ) %>%
      addCircles(         # invisible placeholder to trigger the mymap.spin(false)
        lng = 175.322,
        lat = -37.789,
        radius = 0,
        opacity = 0,
        layerId = 'spinnerMarker'   # identifier, can be found in js: e.layer.options.layerId
      )
  })
}

shinyApp(ui = ui, server = server)

The URLs you provide are not valid. Try

spinPlugin <- htmlDependency(
  "spin.js", 
  "4.1.0",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/spin.js/2.3.2"),
  script = "spin.min.js") # there's no spin.css

leafletspinPlugin <- htmlDependency(
  "Leaflet.Spin", 
  "1.1.2",
  src = c(href = "https://cdnjs.cloudflare.com/ajax/libs/Leaflet.Spin/1.1.2"),
  script = "leaflet.spin.min.js")

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM