Search code examples
rshinyr-leafletshiny-reactivity

filter data from API using selectInput in Shiny


since many days I can't solve my problem. At the beginning I get some data from API (I refresh API call every 5 sec to get the newest data). The data contain information about locations (lat and long) and some labels written to those locations. I want to create an object of selectInput and use choices assigned to labels. If I choose a label from a drop-down list in the selectInput object I want to filter simultaneously the next API call within 5 seconds. The main task is to filter the data visible on the map after choosing a value from the drop-down list.

Labels change every few minutes, but the location coordinates change every few seconds.

I'm using renderUI on server side and uiOutput on UI side. Looking forward for some help, thanks.

library("httr")
library("jsonlite")
library("shiny")
library("leaflet")
library("dplyr")


ui <- shinyUI(fluidPage(
  navbarPage("Title",
             tabPanel("MAP",

                      leafletOutput("mymap", width = "auto", height = "560px")
             )
  ),
  uiOutput("loc")
  )
  )

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

  autoInvalidate <- reactiveTimer(5000)

  reData <- eventReactive(autoInvalidate(), {

    # # example data
    # lat <- c(20.51,20.52,20.65)
    # long <- c(10.33,13.43,23.54)
    # labels <- c('John','Peter','Jolie')
    # data <- data.frame(lat, long, labels)

    # API call #1 response
    get_data <- GET(call1)
    get_data_text <- content(get_data, "text")
    get_data_json <- fromJSON(get_data_text, flatten = TRUE)
    data <- get_data_json$result

    # handling empty API response
    while(class(data) == "list"){
      Sys.sleep(1)
      get_trams <- GET(call1)
      get_data_text <- content(get_data, "text")
      get_data_json <- fromJSON(get_data_text, flatten = TRUE)
      data <- get_data_json$result
    }


    # saving data before filtering - purpose of getting labels for the drop-down list and
    # creating a sorted list for selectInput function
    list_of_vals <- data
    uniq_first_lines <- c("all", unique(as.character(sort(as.numeric(list_of_vals$FirstLine)))))
    sorted_factor <- factor(uniq_first_lines, levels=uniq_first_lines)
    my_new_list <- split(uniq_first_lines, sorted_factor)


    # filter data
    if(input$loc != "all") {
      data <- data %>%
      filter_at(
        vars(one_of("FirstLine")),
        any_vars(.==input$loc))
    }

    rownames(data) <- NULL


    return(list(data=data, my_new_list=my_new_list))
  }, ignoreNULL = FALSE)


  output$loc <-renderUI({
    selectInput("loc", label = h4("Choose location"),
                choices = reData()$my_new_list ,selected = "all"
    )
  })


  points <- eventReactive(autoInvalidate(), {
    cbind(reData()$trams_data$Lon, reData()$trams_data$Lat)
  },ignoreNULL = FALSE)

  labels <- eventReactive(autoInvalidate(), {
    paste("line: ", reData()$trams_data$FirstLine)
  },ignoreNULL = FALSE)

  output$mymap <- renderLeaflet({
    leaflet() %>%
      addTiles()
  })

  observeEvent(autoInvalidate(), {
    leafletProxy("mymap") %>%
      clearMarkers() %>%
      addMarkers(
        data = points(),
        label = labels()
      )
  },ignoreNULL = FALSE)
})


shinyApp(ui, server)

Solution

  • I am going with a minimal example and with the small dataset you provided. You can adapt my example for your needs but I want to show you the use of a reactive dataset so you can filter by labels.

    Are you looking for something like that:

    library("httr")
    library("jsonlite")
    library("shiny")
    library("leaflet")
    library("dplyr")
    
    
    # # example data
    lat <- c(20.51,20.52,20.65)
    long <- c(10.33,13.43,23.54)
    labels <- c('John','Peter','Jolie')
    data <- data.frame(lat, long, labels)
    
    
    ui <- shinyUI(fluidPage(
      navbarPage("Title",
                 tabPanel("MAP",
                          leafletOutput("mymap", width = "auto", height = "560px")
                 )
      ),
      uiOutput("labels")
    )
    )
    
    server <- shinyServer(function(input, output) {
    
    
      output$labels <- renderUI({
           selectInput("labels", label = h4("Choose label"), choices = c("John", "Peter", "Jolie") ,selected = "John")
    
      })
    
    
      reData <- reactive({
    
        autoInvalidate <- reactiveTimer(5000)
        data <- data %>% dplyr::filter(input$labels == labels)
    
      })
    
      output$mymap <- renderLeaflet({
        leaflet(reData()) %>%
          setView(10, 20, zoom = 5) %>%
          addTiles() %>%
        addMarkers()
      })
    
    #  observeEvent(autoInvalidate(), {
     #   leafletProxy("mymap") %>%
      #    clearMarkers() %>%
       #   addMarkers(
        #    data = points(),
         #   label = labels()
        #  )
    #  },ignoreNULL = FALSE)
    
    
    })
    
    
    shinyApp(ui, server)