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!