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?
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.
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)
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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With