根据Shinydashboard中的计时器更新Leaflet标记

6
我有一个数据框 df,其中包含两个变量 latlon。现在我需要创建一个 Shinydashboard,在每 10 秒 后从数据框中取出下一行的值更新地图。
 df <- data.frame("Lat" = c(12.8882, 12.890, 12.891), "Lon" = c(77.58195,77.58190,77.581958))

Ui.R

library(shiny)
library(leaflet)
shinyUI( fluidPage(
                leafletOutput("map1")
                   )
        )

server.R

library(shiny)

 shinyServer(function(input, output, session) {

 output$mymap <- renderLeaflet({
                      leaflet() %>%
                        addTiles() %>%  # Add default OpenStreetMap map tiles
                        addMarkers(lng=df$lon, lat=df$lat)})
                               })

我知道的唯一一件事是我可以使用invalidateLater()来调用计时器,但我不知道如何实现对数据框中行的渐进式读取。

期望结果

我需要一个地图,在每经过10秒钟后,标记(marker)移动到下一个位置。通过数据帧df给出移动标记的坐标。

1个回答

8
您可以使用reactiveVal()来跟踪当前显示的标记,并结合observe()invalidateLater()leafletProxy()来删除先前的标记并添加新的标记。为此,我们可以在添加标记时给图层一个layerId,然后在绘制下一个标记时再次使用该layerId来删除标记。
以下是一个工作示例,我添加了一些注释以说明正在发生的情况。希望这可以帮助您!

enter image description here


library(shiny)
library(leaflet)
set.seed(1)

df <- cbind(rnorm(40) * 2 + 13, rnorm(40) + 48)

ui <- fluidPage(
  leafletOutput("mymap")
)

server <- function(input, output, session) {

  # Create the base map
  output$mymap <- renderLeaflet({
    leaflet() %>%
      addProviderTiles(providers$Stamen.TonerLite,
                       options = providerTileOptions(noWrap = TRUE)
      ) %>%
      setView(lng = mean(rnorm(1000) * 2 + 13), lat =  mean(rnorm(1000) + 48), zoom = 7)
  })

  # Initialize a reactiveVal to keep track of which point is currently selected
  point_to_plot <- reactiveVal(1)

  observe({
    # invalidate every 2 seconds
    invalidateLater(2000)
    isolate({
      # update point_to_plot() to next value. If next value is higher than the amount of rows
      # in df, set it to 1.
      point_to_plot(ifelse(point_to_plot()+1<=nrow(df),point_to_plot()+1,1))
      # Use leafletProxy to remove our previous marker, and add the new one.
      leafletProxy('mymap') %>%
        removeMarker('my_marker') %>%
        addMarkers(layerId = 'my_marker',data = df[point_to_plot(),,drop=F])
    })
  })

}

shinyApp(ui, server)

编辑:使用您的数据的工作示例:
library(shiny)
library(leaflet)
set.seed(1)

df <- data.frame("Lat" = c(12.8882, 12.890, 12.891), "Lon" = c(77.58195,77.58190,77.581958))

ui <- fluidPage(
  leafletOutput("mymap")
)

server <- function(input, output, session) {

  # Create the base map
  output$mymap <- renderLeaflet({
    leaflet() %>%
      addProviderTiles(providers$Stamen.TonerLite,
                       options = providerTileOptions(noWrap = TRUE)
      ) %>%
      setView(lat = 12.89, lng = 77.58195, zoom = 14)
  })

  # Initialize a reactieVal to keep trakc of which point is currently selected
  point_to_plot <- reactiveVal(1)

  observe({
    # invalidate every 2 seconds
    invalidateLater(2000)
    isolate({
      # update point_to_plot() to next value. If next value is higher than the amount of rows
      # in df, set it to 1.
      point_to_plot(ifelse(point_to_plot()+1<=nrow(df),point_to_plot()+1,1))
      # Use leafletProxy to remove our previous marker, and add the new one.
      leafletProxy('mymap') %>%
        removeMarker('my_marker') %>%
        addMarkers(layerId = 'my_marker',data = df[point_to_plot(),,drop=F])
    })
  })

}

shinyApp(ui, server)

@ANmike,请查看我的编辑,我添加了一个使用您提供的数据的工作示例。 - Florian
太好了,很高兴能帮忙! :) - Florian
这非常有帮助,谢谢@Florian!我试图修改它以使用多个点。作为一个快速的修改,如果我将df[point_to_plot(),,drop=F]改为df[point_to_plot() + 0:1,,drop=F]它仍然只绘制一个点。(它最终也会因为下标超出范围而崩溃,但我可以修复这个问题)。你知道还需要编辑什么吗? - bmacGTPM
1
嗨@bmac,这个问题比较复杂,评论区讨论不太方便,而且我也不是很清楚你的期望结果。建议你在SO上开一个新的问题,我会尽力帮助你的 :) - Florian
好的,非常感谢 @Florian!我会在接下来的一天左右撰写问题,并在写完后在这里发布链接。基本上,我想展示多个点/标记沿着不同的直线路径移动。通过改编上面的代码,可以实现在每个帧中显示多个点,并将invalidLater减少到一个更小的数字,从而实现这个目标。 - bmacGTPM

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