Search code examples
rshinyr-leaflet

Update Leaflet Marker based on timer in Shinydashboard


I have data frame df which has two variables lat and lon, Now I need to create a Shinydashboard which updates the map by taking next row value in from the data frame after every 10 seconds.

df

 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)})
                               })

Only thing I know is I can use invalidateLater() to call the timer but I do not know how to implement that for the incremental reading of the rows in the data frame.

Expected Result

I need a map where the marker moves to the next position after every 10 Seconds, The coordinates for moving the marker is given through the data frame df.


Solution

  • You can use a reactiveVal() to keep track of the currently displayed marker, and use observe() in combination with invalidateLater() and leafletProxy() to remove the previous marker and add the new one. To do so we can give the layer a layerId everytime we add our marker, which we can then use to remove the marker again when plotting the next marker.

    A working example is given below, I added some comments to illustrate what is happening. Hope this helps!


    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)
    

    EDIT: Working example with your data:

    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)