Search code examples
rshinyr-leaflet

How to map a range of values on a leaflet map with Shiny?


I tried to make an app where the user can set a range of value and see the results in a map and in the table. Here I used quakes dataset to make a reproducible example.

The datatable part works fine but not the leaflet part which seems to show an unique marker. I know this is probably the min_depth input I put in the reactive function because the app crash when I play with. I can't figure out what to do.

Error message

Warning: Error in : Problem with `filter()` input `..1`.
x Input `..1` must be of size 988 or 1, not size 1000.
i Input `..1` is `obj$depth <= as.numeric(input$max_depth)`.
  143: <Anonymous>
Warning: Error in : Problem with `filter()` input `..1`.
x Input `..1` must be of size 988 or 1, not size 1000.
i Input `..1` is `obj$depth <= as.numeric(input$max_depth)`.
  53: <Anonymous>
Warning: Error in : Problem with `filter()` input `..1`.
x Input `..1` must be of size 988 or 1, not size 1000.
i Input `..1` is `obj$depth <= as.numeric(input$max_depth)`.
  105: <Anonymous>

Shiny app code

library(shiny)
library(DT)
library(dplyr)
library(leaflet)
library(leaflet.extras)


# Define UI
ui <- fluidPage(

    # Application title
    titlePanel("Quakes Test"),

    # Sidebar with numericInput for quakes depth range 
    sidebarLayout(
        sidebarPanel(
            numericInput(inputId = "min_depth", label = "Mininum depth", value = min(quakes$depth), min = min(quakes$depth), max = max(quakes$depth)),
            numericInput(inputId = "max_depth", label = "Maximum depth", value = max(quakes$depth), min = min(quakes$depth), max = max(quakes$depth))
        ),

        # Show a map
        mainPanel(
            fluidRow(
                leafletOutput("mymap_occ", width = "98%", height = 500))
        )
    ),
    fluidRow(DT::dataTableOutput(outputId = "prop_table"))
)


server <- function(input, output) {

    #filter terrains
    depth_final <- reactive({
        obj <- quakes
        if (input$min_depth != "All") {
            obj <- quakes %>% 
                filter(obj$depth >= as.numeric(input$min_depth)) %>% 
                filter(obj$depth <= as.numeric(input$max_depth))
        }
    })
    
    output$prop_table <- renderDT({
        datatable(depth_final(), extensions = 'Buttons', rownames = FALSE, escape = FALSE, selection = 'none')
        
    })
    
    observe({
        leafletProxy("mymap_occ", data = depth_final()) %>%
            removeMarker(layerId = "FOO") %>% 
            addCircleMarkers(lng = ~long, lat = ~lat, clusterOptions = markerClusterOptions(),
                             weight= 4, opacity = 1, color = "yellow", layerId = "FOO")
    })
    
    output$mymap_occ <- renderLeaflet({
        leaflet(depth_final()) %>%
            fitBounds(~min(long), ~min(lat), ~max(long), ~max(lat)) %>% 
            addProviderTiles(providers$Esri.WorldImagery, group = "Vue satellite") %>%
            addProviderTiles(providers$Stamen.TonerLabels, group = "Vue satellite")
    })
    
    
}
# Run the application 
shinyApp(ui = ui, server = server)

Solution

  • There are two issues with your code:

    1. The error message results from using obj$depth in the filter statement. Simply use depth. By using obj$depth you are referring to the depth in the original obj. While this works in the first filter it is not recommended to do so. In the second filter however you already dropped some rows, hence you got an error message as obj$depth refers to depth column in the unfiltered df.

    2. Only one marker shows up because you use layerID instead of group. See here on the difference between the two concepts.

    Try this:

    server <- function(input, output) {
      
      #filter terrains
      depth_final <- reactive({
        obj <- quakes
        if (input$min_depth != "All") {
          obj <- quakes %>% 
            filter(depth >= as.numeric(input$min_depth)) %>% 
            filter(depth <= as.numeric(input$max_depth))
        }
        obj
      })
      
      output$prop_table <- renderDT({
        datatable(depth_final(), extensions = 'Buttons', rownames = FALSE, escape = FALSE, selection = 'none')
        
      })
      
      observe({
        leafletProxy("mymap_occ", data = depth_final()) %>%
          clearGroup(group = "FOO") %>%
          addCircleMarkers(lng = ~long, lat = ~lat, clusterOptions = markerClusterOptions(),
                           weight= 4, opacity = 1, color = "yellow", group = "FOO")
      })
      
      output$mymap_occ <- renderLeaflet({
        leaflet(depth_final()) %>%
          fitBounds(~min(long), ~min(lat), ~max(long), ~max(lat)) %>% 
          addProviderTiles(providers$Esri.WorldImagery, group = "Vue satellite") %>%
          addProviderTiles(providers$Stamen.TonerLabels, group = "Vue satellite")
      })
      
      
    }