Блестящий: как фильтровать данные на основе местоположения данных, введенных пользователем

У меня есть блестящее приложение, работающее здесь. Он отображает около 12 тысяч квартир и комнат для сдачи в аренду на интерактивной карте-буклете и добавляет на карту маркер на основе адреса, вводимого пользователем. Вот код. Извините за то, что это плохо документировано.

Есть два разных объекта фрейма данных: один для квартир (df.apt), а другой - для комнат (df.quartos).

Однако из-за большого количества данных, загружаемых приложением, оно работает немного медленнее. Я хочу добавить ресурс, который будет отображать данные только после того, как пользователь введет адрес, а также выберет диапазон близости (например, показать только квартиры в пределах 10 км от введенного адреса). Как мне это сделать?

library(leaflet)
library(shiny)
library(ggmap)


source("post4-prepararshiny.R") #loads data and helper functions


 ui = bootstrapPage(
   div(class = "outer",
       tags$head(
         # Include our custom CSS
         includeCSS("styles.css"),
         includeScript("gomap.js")
       ),

   tags$style(type = "text/css", "html, body {width:100%;height:100%}"),
   leafletOutput("mymap", width = "100%", height = "100%"),

   absolutePanel(id = "controls",# class = "panel panel-default",
                 fixed = TRUE,
                 draggable = TRUE,
                 top = 60, left = "auto", right = 20, bottom = "auto",
                 width = 330, height = "auto",

                 h2("Buscador OLX"),
                 textInput(inputId = "userlocation",
                           label = "Digite um endereço\n com pelo menos rua, número, bairro e cidade",
                           value = ""),
                 helpText("Exemplo: Rua Dias da Rocha, 85 - Copacabana, Rio de Janeiro - RJ"),

                 sliderInput(inputId = "distancia", label = "Escolha a distância em km:",
                             min = 0, max = 30, value = 15),

                 actionButton("go", "Buscar"),
                 helpText("Encontre imóveis para alugar perto de onde você quiser!"),

                 helpText("Cada ponto no mapa representa um imóvel para alugar.",
                          "A cor de um ponto é determinada pelo valor do aluguel.",
                          "Clique em um ponto para ter mais informações sobre o imóvel."),

                 helpText("Mais informações sobre este app em sillasgonzaga.github.io")

                 )

   ),
   tags$div(id="cite",
            'Dados extraídos do OLX em 12/11/2016.', ' Contato: sillasgonzaga.github.io'
   )
)

server.R

server = function(input, output, session){

  #browser()
  output$mymap <- renderLeaflet({
    map <- leaflet() %>%
    addTiles() %>%
    addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
    # coordenadas de um ponto em específico
    addMarkers(lat = -22.911872, lng = -43.230184,
               popup = "Estádio do Maracanã! <br> Apenas um exemplo!") %>%


    # plotar apartamentos
    addCircleMarkers(data = df.apt,
                     lng = ~lon, lat = ~lat,
                     color = ~vetorCoresApt(preco),
                     opacity = 1.5,
                     popup = textoPopup(df.apt, "apartamento"),
                     # Definir nome do grupo para ser usado na camada
                     group = "Apartamentos") %>%
    # plotar quartos
    addCircleMarkers(data = df.quartos,
                     lng = ~lon, lat = ~lat,
                     color = ~vetorCoresQuarto(preco),
                     opacity = 1.5,
                     popup = textoPopup(df.quartos, "quarto"),
                     group = "Quartos") %>%
    addLayersControl(
      overlayGroups = c("Apartamentos", "Quartos"),
      options = layersControlOptions(collapsed = FALSE),
      position = "bottomright"
    ) %>%
    addLegend(pal = vetorCoresApt, values = df.apt$preco,
              position = "bottomright")
    map
  })


   observeEvent(input$go, {
     v <- geocode(input$userlocation)
     leafletProxy('mymap', session) %>% addMarkers(lng = v$lon,lat = v$lat)
   })


}

Я знаю, что могу использовать функцию geosphere::distm() для вычисления расстояния между матрицей данных и точкой данных, например:

coord <- matrix(data = c(df.apt$lon, df.apt$lat), ncol = 2)
distance_vector <- distm(x = coord, y = c(lon = -43.183447, lat = -22.913912), fun = distVincentySphere)
# insert vector into data frame
df.apt$distance <- distance_vector

Однако как мне сделать это реактивным способом, который позволит мне изменять столбец distance каждый раз, когда пользователи нажимают кнопку, и изменять sliderInput(), который будет использоваться для указания диапазона близости?

P.S .: Извините за код и комментарии на португальском языке.

РЕДАКТИРОВАТЬ: РЕШЕНО

Я смог придумать решение после ответа @HubertL. Вот что я сделал на server.R:

  distance_apt_reactive <- eventReactive(input$go, { 
    address_latlon <- geocode(input$userlocation)
    dist <- distm(x = matrix(data = c(df.apt$lon, df.apt$lat), ncol = 2), 
                  y = c(lon = address_latlon$lon, lat = address_latlon$lat), 
                  fun = distVincentySphere)
    dist <- dist/1000

  })

  apt_reactive <- reactive({df.apt[distance_reactive() < input$distancia,]})

  output$mymap <- renderLeaflet({
    map <- leaflet() %>%
      addTiles() %>%
      addProviderTiles("OpenStreetMap.BlackAndWhite") %>%
      setView(lng = mean(df.apt$lon), lat = mean(df.apt$lat), zoom = 11) %>%
      addLegend(pal = vetorCoresApt, values = df.apt$preco,
                position = "bottomright",
                layerId = "legend")

  map  
  })

     observe({
       leafletProxy("mymap") %>%
         clearMarkers() %>%
         #addMarkers(lng = myadress()$lon, lat = myadress()$lat) %>%
         addCircleMarkers(data = apt_reactive(),
                          lng = ~lon, lat = ~lat,
                          color = ~vetorCoresQuarto(preco),
                          opacity = 1.5,
                          # adicionar popup
                          popup = textoPopup(apt_reactive(), "apartamento"),
                          group = "Apartamentos")
    })

person iatowks    schedule 24.11.2016    source источник


Ответы (1)


Вы можете добавить reactive, который будет фильтровать ваш data.frame в зависимости от расстояния до адреса:

apt_reactive <- reactive({ 
    address_latlon <- geocode(input$userlocation)
    dist <- distm(x = matrix(data = c(df.apt$lon, df.apt$lat), ncol = 2), 
                  y = c(lon = address_latlon$lon, lat = address_latlon$lat), 
                  fun = distVincentySphere)
    apt.df[dist < input$distancia,]
})

Затем замените

addCircleMarkers(data = df.apt

by

addCircleMarkers(data = apt_reactive()

(и повторите тот же процесс с quartos_reactive для df.quartos)

person HubertL    schedule 24.11.2016
comment
Ваше решение неплохое, но проблема все еще есть. Каждый раз, когда пользователь изменяет диапазон расстояний, новая карта будет отображаться renderLeaflet(), что замедляет работу приложения. Есть ли способ сделать более плавным? Интересно, имеет ли это какое-либо отношение к leafletProxy(). - person iatowks; 25.11.2016
comment
Да, но поскольку точек для рендеринга будет гораздо меньше, это будет очень быстро. Но вы правы, возможно, можно использовать leafletProxy. Я посмотрю на эту (новую для меня) функцию - person HubertL; 25.11.2016
comment
Итак, я провел здесь много тестов и кажется, что хочу просто невозможно. Представьте, что вы выбрали 3 км в качестве диапазона расстояний. Он будет отображать только данные в этом диапазоне. Если я использую ползунок для увеличения этого диапазона до 10 км, он отобразит новые данные, включенные в этот новый диапазон. Однако проблема в том, что если я верну его обратно на 3 км, данные в пределах 10 км не исчезнут. Вместо этого он будет отображать данные, которые уже были построены. Пожалуйста, дайте мне знать, если я не понимаю. - person iatowks; 25.11.2016