R Shiny map search input box

2020-06-06 01:30发布

In google maps, the search input box auto completes addresses as a user types. Is there a way to do this in R Shiny with access to the autocomplete value in order to use with a mapping package?

enter image description here

There is a javascript method here. I've tried to use this method in R Shiny in the code below. SymbolixAU pointed out using google_map( search_box = TRUE ) which is a simple solution. Unfortunately it doesn't work in my code and also because I would like the search box to be separate from the map.

The attempt below has a text input my_address, text output copy_of_address and a googleway map my_map in this order on the page.

The intended behaviour is for a user to enter text into text input my_address, have it autocomplete with an address (this works), the address will be copied into text output copy_of_address (this only shows what was typed, not the autocompleted version) and finally the map is to be centred on this address.

The shiny app

See that the input box has the autocomplete address, however the copy of the address and map is using only the user input text.

In the code below, replace MyKey with your google api key (sometimes an empty string works).

library(shiny)
library(googleway)

key <- "MyKey"
set_key(key = key)
google_keys()

ui <- shiny::basicPage(

  div(
    textInput(inputId = "my_address", label = "")    
    ,textOutput(outputId = "copy_of_address")
    ,HTML(paste0("
          <script>
            function initAutocomplete() {
            new google.maps.places.Autocomplete(
            (document.getElementById('my_address')),
            {types: ['geocode']}
            );
            }
            </script>
            <script src='https://maps.googleapis.com/maps/api/js?key=", key,"&libraries=places&callback=initAutocomplete'
            async defer></script>
    "))
    ,google_mapOutput(outputId = "my_map")
  )

)

server <- function(input, output) {

  my_address <- reactive({
    input$my_address
  })

  output$copy_of_address <- renderText({
    my_address()
  })

  output$my_map <- renderGoogle_map({
    my_address <- my_address()
    validate(
      need(my_address, "Address not available")
    )

    df <- google_geocode(address = my_address)
    my_coords <- geocode_coordinates(df)
    my_coords <- c(my_coords$lat[1], my_coords$lng[1])

    google_map(
      location = my_coords,
      zoom = 12,
      map_type_control = FALSE,
      zoom_control = FALSE,
      street_view_control = FALSE
    )
  })

}

shinyApp(ui, server)

1条回答
做个烂人
2楼-- · 2020-06-06 02:20

This wasn't easy for me to figure out, but I think I have a reasonable solution. It ultimately came down to copying and pasting the example from Google very carefully, reading about sending messages from Javascript to Shiny, putting the ideas together, and getting lucky on the 200th attempt...

To Give Credit Where It's Due:

Google's link: https://developers.google.com/maps/documentation/javascript/examples/places-autocomplete

RStudio's Link: https://shiny.rstudio.com/articles/js-send-message.html

My potential solution:

library(shiny)
library(googleway)

key <- "MyKey"
set_key(key = key)
#google_keys()

ui <- shiny::basicPage(

  div(
    textInput(inputId = "my_address", label = "Type An Address")    
    ,textOutput(outputId = "full_address")
    ,HTML(paste0(" <script> 
                function initAutocomplete() {

                 var autocomplete =   new google.maps.places.Autocomplete(document.getElementById('my_address'),{types: ['geocode']});
                 autocomplete.setFields(['address_components', 'formatted_address',  'geometry', 'icon', 'name']);
                 autocomplete.addListener('place_changed', function() {
                 var place = autocomplete.getPlace();
                 if (!place.geometry) {
                 return;
                 }

                 var addressPretty = place.formatted_address;
                 var address = '';
                 if (place.address_components) {
                 address = [
                 (place.address_components[0] && place.address_components[0].short_name || ''),
                 (place.address_components[1] && place.address_components[1].short_name || ''),
                 (place.address_components[2] && place.address_components[2].short_name || ''),
                 (place.address_components[3] && place.address_components[3].short_name || ''),
                 (place.address_components[4] && place.address_components[4].short_name || ''),
                 (place.address_components[5] && place.address_components[5].short_name || ''),
                 (place.address_components[6] && place.address_components[6].short_name || ''),
                 (place.address_components[7] && place.address_components[7].short_name || '')
                 ].join(' ');
                 }
                 var address_number =''
                 address_number = [(place.address_components[0] && place.address_components[0].short_name || '')]
                 var coords = place.geometry.location;
                 //console.log(address);
                 Shiny.onInputChange('jsValue', address);
                 Shiny.onInputChange('jsValueAddressNumber', address_number);
                 Shiny.onInputChange('jsValuePretty', addressPretty);
                 Shiny.onInputChange('jsValueCoords', coords);});}
                 </script> 
                 <script src='https://maps.googleapis.com/maps/api/js?key=", key,"&libraries=places&callback=initAutocomplete' async defer></script>"))
    ,google_mapOutput(outputId = "my_map")
    )

    )

server <- function(input, output) {

  my_address <- reactive({
    if(!is.null(input$jsValueAddressNumber)){
      if(length(grep(pattern = input$jsValueAddressNumber, x = input$jsValuePretty ))==0){
        final_address<- c(input$jsValueAddressNumber, input$jsValuePretty)
      } else{
        final_address<- input$jsValuePretty
      }
      final_address
    }
  })


  output$full_address <- renderText({
    if(!is.null(my_address())){
      my_address()
    }
  })

  output$my_map <- renderGoogle_map({
    my_address <- my_address()
    shiny::validate(
      need(my_address, "Address not available")
    )

    not_a_df <- google_geocode(address = my_address)
    my_coords <- geocode_coordinates(not_a_df)
    my_coords <- c(my_coords$lat[1], my_coords$lng[1])

    google_map(
      location = my_coords,
      zoom = 12,
      map_type_control = FALSE,
      zoom_control = FALSE,
      street_view_control = FALSE
    )
  })

}

shinyApp(ui, server)

I hope this helps. Cheers! --nate

查看更多
登录 后发表回答