SymbolixAU / googleway

R Package for accessing and plotting Google Maps
http://symbolixau.github.io/googleway/
Other
232 stars 46 forks source link

Pin doesn't load when using google_map_update #274

Closed cwilligv closed 1 week ago

cwilligv commented 2 weeks ago

I'm using googleway in an app that has an autocomplete search box outside the map. The first time the app loads and you search for an address it updates the map but it doesn't place the pin on the map, however, after one refreshes the page and search for an address again it works fine and the pin is placed. I've been able to reproduce the error with the following reprex below. Note that it needs to be run with the browser's developer tools open and option 'Disable cache' checkbox activated so it simulates the first load of the page. I think it has to do with the library loading process at start but I'm not sure. Any ideas I could try?

library(shiny)
library(googleway)

key <- "your key"
set_key(key = key)

ui <- shiny::basicPage(

  div(
    textInput(inputId = "my_address", label = "Type An Address")
    ,textOutput(outputId = "full_address")
    ,actionButton(inputId = "btn", "Search")
    ,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
    }
  })

  observeEvent(input$btn, {
    # browser()
    my_address <- my_address()
    not_a_df <- google_geocode(address = my_address)
    my_coords <- geocode_coordinates(not_a_df)
    my_coords <- data.frame(lat = as.numeric(my_coords$lat[1]), lng = as.numeric(my_coords$lng[1]))

    googleway::google_map_update(map_id = "my_map") %>%
      # clear_markers() %>%
      googleway::add_markers(
        data = my_coords,
        lat = "lat",
        lon = "lng",
        update_map_view = T
      ) %>% 
      googleway::google_map_view(location = c(my_coords$lat, my_coords$lng), zoom = 19)
  })

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

  output$my_map <- renderGoogle_map({
    google_map(
      location = c(-27.481080541187133, 153.0122871294947),
      zoom = 16,
      map_type_control = FALSE,
      zoom_control = FALSE,
      street_view_control = FALSE
    )
  })

}

shinyApp(ui, server)

For example: searching for this address 25 Lang Street, Sunnybank Hills returns this image

The js errors in the console are:

Any ideas would be much appreciated.

dcooley commented 1 week ago

notes:

I've stripped down your example; you can see the output of the two print statements to see all the values returned from the search

library(shiny)
library(googleway)

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

ui <- shiny::basicPage(

  div(
    textInput(inputId = "my_address", label = "Type An Address")
    , actionButton(inputId = "btn", "Search")
    , shiny::tags$head(
      shiny::tags$script(
        '
        var mapExists = setInterval(function() {

          if(typeof google !== "undefined") {
            clearInterval(mapExists)

            var autocomplete = new google.maps.places.Autocomplete(document.getElementById("my_address"));
            autocomplete.addListener("place_changed", function() {
               autocomplete.setFields(["place_id", "geometry", "name"]);
               var place = autocomplete.getPlace();

               Shiny.setInputValue("jsPlaceAutocomplete", place);
             });

          }

        }, 100);
        '
      )
    )
    ,google_mapOutput(outputId = "my_map")
  )
)

server <- function(input, output) {

  observeEvent(input$jsPlaceAutocomplete, {
    print(input$jsPlaceAutocomplete)

    res <- input$jsPlaceAutocomplete

    df <- data.frame(
      lng = res$geometry$location$lng
      , lat = res$geometry$location$lat
    )

    googleway::google_map_update(map_id = "my_map") %>%
      googleway::add_markers(
        data = df,
        lat = "lat",
        lon = "lng",
        update_map_view = T
      )
  })

  observeEvent(input$btn, {
    print(input$jsPlaceAutocomplete)
  })

  output$my_map <- renderGoogle_map({
    google_map(
      location = c(-27.481080541187133, 153.0122871294947),
      zoom = 16,
      map_type_control = FALSE,
      zoom_control = FALSE,
      street_view_control = FALSE
    )
  })

}

shinyApp(ui, server)
cwilligv commented 1 week ago

Thanks @dcooley Dave for taking the time. Exciting news about v3.0.0!! looking forward to it :) I've run your code and it solved the api multiple times error, that's great! However, the issue I have is at first load of the app, when I run this in google cloud run the first time I hit the app it throws the error below and the search bar doesn't respond to typing an address, resulting in not being able to get a lat lon for it.

I was able to replicate this error by using the browser's dev tools and setting the option "Disable cache" on under the network menu. Note that this option only gets applied while dev tools is open.

image

If I reload the app (page refresh) then I don't get this error and the app works as expected. Is there a way to change the library loading process differently? Maybe load the libraries from local folder in the project?

dcooley commented 1 week ago

I've updated master branch, and the UI code, with a couple more checks for undefined "google". Let me know if these work for you?

This works for me without console errors, with 'disable cache' checked.

ui <- shiny::basicPage(

  div(
    textInput(inputId = "my_address", label = "Type An Address")
    , actionButton(inputId = "btn", "Search")
    , shiny::tags$head(
      shiny::tags$script(
        '
        var mapExists = setInterval(function() {

          if(typeof google !== "undefined") {

            if(typeof google.maps !== "undefined") {

              clearInterval(mapExists);

              var autocomplete = new google.maps.places.Autocomplete(document.getElementById("my_address"));

              autocomplete.addListener("place_changed", function() {
                 autocomplete.setFields(["place_id", "geometry", "name"]);
                 var place = autocomplete.getPlace();

                 Shiny.setInputValue("jsPlaceAutocomplete", place);
               });
            }
          }

        }, 100);
        '
      )
    )
    ,google_mapOutput(outputId = "my_map")
  )
)
cwilligv commented 1 week ago

Hi @dcooley , This worked exactly as we needed. I have now integrated this on our app and it's working fine with no errors.

Thank you very much for your support here.

Looking forward to version 3.0