闪亮:如何根据用户输入数据的位置过滤数据

Shiny: How to filter data based on location of user input data

我有一个闪亮的应用程序 运行 here. It plots around 12 thousand apartments and rooms for rent on a leaflet interactive map and adds a marker on the map based on the address that the user inputs. Here's the code。抱歉,如果它没有很好的记录。

有两种不同的数据框对象:一种用于公寓 (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")
    })

您可以添加一个 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

来自

addCircleMarkers(data = apt_reactive()

(并用 quartos_reactivedf.quartos 重复相同的过程)