Skip to main content
  1. Posts/

Google Autocomplete Address in R Shiny

·362 words·2 mins
Googlemaps Shiny
Andryas/shiny-googlemaps-autocomplete

R
1
0

A quick Shiny tip here.

Little Context
#

I’ve been working with real estate for a while now and often need to add a search input for addresses in applications. I got this answer here some time ago and modified it into the Shiny module below.

Just add your Google Maps key to GCP_TOKEN_MAPS in your .Renviron file, or pass the key directly to the functions googlemap_autocomplete_ui and googlemap_autocomplete_server, and voila!

The module
#

googlemap_autocomplete.R

googlemap_autocomplete_ui <- function(
  id, label = "Type an address", width = NULL, 
  placeholder = NULL, key = NULL, ...) {
  if (is.null(key)) key <- Sys.getenv("GCP_TOKEN_MAPS")

  ns <- NS(id)
  button <- ns("button")
  jsValue <- ns("jsValue")
  jsValueAddressNumber <- ns("jsValueAddressNumber")
  jsValuePretty <- ns("jsValuePretty")
  jsValueCoords <- ns("jsValueCoords")

  # I hide the script part here, you can 
  script <- stringr::str_c("
   ... 
  ")
  script <- stringr::str_interp(script)
  script <- stringr::str_replace_all(script, "\\s+", "")

  shiny::tagList(
    htmltools::div(
      id = stringr::str_c(button, "-layout"),
      shiny::textInput(
        inputId = button,
        label = label, 
        width = width, 
        placeholder = placeholder
      ),
      htmltools::HTML(script),
      ...
    )
  )
}

googlemap_autocomplete_search_server <- function(id, key = NULL) {
  if (is.null(key)) key <- Sys.getenv("GCP_TOKEN_MAPS")

  shiny::moduleServer(
    id,
    function(input, output, session) {
      my_address <- shiny::reactive({
        if (!is.null(input$jsValueAddressNumber)) {
          if (
            length(grep(
              input$jsValueAddressNumber, input$jsValuePretty
            )) == 0
          ) {
            final_address <- c(
              input$jsValueAddressNumber, 
              input$jsValuePretty
            )
          } else {
            final_address <- input$jsValuePretty
          }
          return(final_address)
        }
      })

      address <- reactive({
        shiny::req(my_address())
        result <- googleway::google_geocode(
          my_address(), 
          key = key
        )
        return(result)
      })

      address
    }
  )
}

The App
#

www/custom.css

body {
    height: 100%;
    width: 100%;
}

.container-fluid {
    height: 100vh;
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
}

.leaflet-container {
    border-radius: 15px;
    margin: 27px;
}

#teste-button-layout {
    width: 500px;
}

app.R

library(shiny)
library(shinyjs)

source("googlemap_autocomplete_search.R")

ui <- fluidPage(
  useShinyjs(),

  includeCSS("www/custom.css"),

  googlemap_autocomplete_search_ui(
    "teste",
    width = "100%",
    key = Sys.getenv("GCP_TOKEN_MAPS")
  ),

  leaflet::leafletOutput("map", width = "auto", height = "auto")
)

server <- function(input, output, session) {
  address <- googlemap_autocomplete_server(
    "teste",
    key = Sys.getenv("GCP_TOKEN_MAPS")
  )

  output$map <- leaflet::renderLeaflet({
    shiny::req(address())

    shinyjs::runjs('$("#map").width(500).height(500);')

    results <- address()[["results"]]

    lng <- results[["geometry"]][["location"]][["lng"]]
    lat <- results[["geometry"]][["location"]][["lat"]]

    leaflet::leaflet() |>
      leaflet::addTiles() |>
      leaflet::setView(
        lng = lng,
        lat = lat,
        zoom = 13
      ) |>
      leaflet::addMarkers(
        lng = lng,
        lat = lat,
        popup = "Well done, noble warrior!"
      )
  })
}

shinyApp(ui, server)

Cheers!