无法在R Shiny中使用Leaflet.Spin插件

3
我有一个闪亮的应用程序,涉及在地图上绘制大量线条。我想使用旋转器向用户显示渲染正在进行中。大多数闪亮的方法都不起作用,因为它们只在数据被发送到leaflet时显示旋转器,而不是在leaflet渲染时显示。Leaflet.Spin插件看起来很有前途,但我一直在努力使其工作。我一直在遵循的示例是

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

使用polylineDecorator作为示例的leaflet插件和leafletProxy

我该如何正确触发js事件并在渲染线条(例如此示例中的圆形)时显示Leaflet.Spin?谢谢!

更新:现在Spinner可以工作,但是每个添加的单独圆形都会触发事件,因此如果圆形数量减少,则Spinner无法正确关闭。

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

# https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
# https://dev59.com/NK_la4cB1Zd3GeqPoic2polylinedecorator-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)
2个回答

5

虽然晚了一年,但我一直在寻找一种让叶片拥有繁忙旋转器的方法。

我意识到你原来的代码会在 clearShapes() 中创建 x 个旋转实例。如果下面的 addCircles() 添加的圆形少于 x 个,就会有旋转实例仍在运行。

我的解决方案是使用一个哑层来监视 Javascript。在下面的示例中,一个圆形具有 layerId = 'spinnerMarker'。要启动 leafletproxy 更新,需要使用 removeShape(layerId = 'spinnerMarker') 删除该图层,从而触发 JS 的 layerremove。然后再添加数据圆。通过添加一个具有 layerId = 'spinnerMarker' 的圆形来结束更新,从而触发 JS 的 layeradd

在 JS 中,使用 e.layer.options.layerId == 'spinnerMarker' 来检查正在添加或删除的图层以运行 mymap.spin()

这样就只有一个旋转实例在运行。

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

# https://gist.github.com/jcheng5/c084a59717f18e947a17955007dc5f92
# https://dev59.com/NK_la4cB1Zd3GeqPoic2
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)

不用客气。我相信使用leaflet r的invokeMethod()而不是leaflet js的layeradd有更好的方法来完成这个任务。 - rbasa
所以,这种方法是通过添加一个额外的不可见形状附加到旋转器上,并与其他形状一起按顺序绘制的。很好! - Simon Woodward
1
是的,我承认这是一种取巧的方法。显然,每个形状都是一个图层。因此,如果您的addCircles()有1000个元素,则有1000个图层。Leafletjs layeradd方法被触发了1000次。clearShapes()将删除这1000个图层。Leafletjs layerremove被触发1000次,创建1000个旋转器实例。然后,您画了500个圆,只清除了500个旋转器。通过仅观察那一个不可见的形状,我们只会有一个旋转器在运行。 - rbasa
2
@SimonWoodward,我继续在Spinner上工作。不再需要一个不可见的对象了。我已经将其编写为插件并提交到leaflet.extras2。如果你想看一下,可以访问https://github.com/radbasa/leaflet.extras2/tree/spin - rbasa
1
Spin 插件已经被合并到 leaflet.extras2 中。https://github.com/trafficonese/leaflet.extras2 - rbasa
@rbasa 哇,你在leaflet.extras2中的代码对我来说完美无缺。我已经寻找解决这个问题的方法将近一年了。SimonWoodward和rbasa,感谢你们为此付出的所有努力。 - VvdL

2
您提供的URL无效,请尝试:
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")

2
@SimonWoodward 什么不起作用?你的意思是在添加圆圈后,旋转器没有消失吗? - Stéphane Laurent
旋转器根本没有出现。我的 reprex 对您有效吗? - Simon Woodward
1
@SimonWoodward 是的,它可以工作,但我将圆的数量减少到了1000个。 - Stéphane Laurent
谢谢,这解决了一个问题,但仍然存在事件问题,就像上面所解释的那样。 - Simon Woodward
Stephane Laurent,你能帮忙解决上面的事件触发问题吗?我已经为此设置了赏金。谢谢。 - Simon Woodward

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接