Search code examples
rshinyr-leaflet

Prevent view from resetting when input is changed


I have a map which has a series of lines with associated values, which is rendered based on a reactive dataframe, filtered by a sliderInput.

When a line is clicked, the map view is updated with new lng, lat and zoom values, based on that line. What I would like is that the view does not reset when the slider is updated. I understand that it's resetting to the values in the initial setView() because the map is rerendering when the slider is changed, but I'm unsure how I can prevent this behaviour.

In short, when a user clicks on the line, I want it to stay at that lng/lat/zoom even as the slider changes. How can I accomplish this?

Below is a simplified reproducible example, with only one line:


library(shiny)
library(leaflet)
library(tidyverse)
library(RColorBrewer)

# Example data frame
line1 <- data.frame(
  lng = rep(c(13.35011, 13.21514), 4),
  lat = rep(c(52.51449, 52.48042), 4),
  id = rep("10351A", 8),
  period = rep(c(1, 2, 3, 4), each = 2),
  value = rep(c(1200, 2300, 3140, 1111), each = 2)
)


ui <- fluidPage(
  sidebarPanel(
    sliderInput(
      inputId = "period_picker",
      label = "Period",
      min = 1,
      max = 4,
      value = 1
    ),
    uiOutput("clicked_info")
  ),
  mainPanel(
    leafletOutput("map")
    )
)


server <- function(input, output) {
  
  # Reactive dataframe based on period_picker
  dat <- reactive({
    filtered <- line1 %>% 
      filter(period == input$period_picker)
    
    return(filtered)
  })
  
  
  # Render map
  output$map <- renderLeaflet({
    # Create color palette based on reactive frame
    pal <- colorNumeric(palette = "Purples", domain = c(0, max(line1$value)))
    
    # Render leaflet map
    leaflet(data = dat()) %>%
      addTiles() %>%
      setView(lng = 13.38049, lat = 52.51873, zoom = 13)  %>%
      addPolylines(
        lng = ~lng,
        lat = ~lat,
        layerId = ~id,
        color = ~pal(dat()$value),
        opacity = 1
        )
    })
    
    # Zoom in and readjust view if shape matching id is clicked - this is the 
    # lng/lat/zoom value I want to keep when the sliderInput is changed
    observeEvent(input$map_shape_click, {
      x <- input$map_shape_click 
      if(x$id == "10351A") {
        leafletProxy(
          mapId = "map",
        ) %>%
          flyTo(
            lng = 13.282625,
            lat = 52.497455,
            zoom = 12
          )
      }
      
      
      # Render dataset in the UI
      output$clicked_info <- renderUI({
        div(
          tags$span("Line ID:", dat()$id[1]),
          br(),
          tags$span("Period:", dat()$period[1]),
          br(),
          tags$span("Value:", dat()$value[1])
        )
      })
    })
}


shinyApp(ui = ui, server = server)


Solution

  • You need to put the addPolylines not in the map rendering, but in another observeEvent with leafletProxy. Replace your output$map block of code by the following lines :

      # Render map
      output$map <- renderLeaflet({
        leaflet() %>%
          addTiles() %>%
          setView(lng = 13.38049, lat = 52.51873, zoom = 13)
      })
      
      observeEvent(dat(), {
        req(dat())
        pal <- colorNumeric(palette = "Purples", domain = c(0, max(line1$value)))
        
        leafletProxy("map") %>%
          addPolylines(
            data = dat(),
            lng = ~lng,
            lat = ~lat,
            layerId = ~id,
            color = ~pal(dat()$value),
            opacity = 1
          )
      })