Search code examples
javascriptformsgoogle-maps-api-3autocompletegeocoding

how do I combine autocomplete with geocode to get Lat. & Lng. from form input in this autocomplete function?


I'm making this form where the user types in their postcode and it autocompletes the full address from google.maps.places dropdown list. The only thing that's missing is the latitude and longitude which I intend to make hidden fields for and then onsubmit post to my google sheets spreadsheet in the appropriate fields.

What I'm stuck on, is combining the Geocoding API call, with the Autocomplete Javascript function. What I'm trying to do, is add in a Geocoding API call with the autocomplete function so that the Lat. and Lng is sent to the hidden fields, I've been trying to figure this out for 3 days, I wonder if anyone has a solution.

Here is the code I have:

var placeSearch, autocomplete;
  var componentForm = {
    street_number: 'short_name',
    route: 'long_name',
    locality: 'long_name',
    administrative_area_level_1: 'short_name',
    country: 'long_name',
    postal_code: 'short_name'
  };

function initAutocomplete() {
  // Create the autocomplete object, restricting the search to geographical
  // location types.
  autocomplete = new google.maps.places.Autocomplete(
    /** @type {!HTMLInputElement} */(document.getElementById('autocomplete')),
    {types: ['geocode']});

  // When the user selects an address from the dropdown, populate the address
  // fields in the form.
  autocomplete.addListener('place_changed', fillInAddress);
}

function fillInAddress() {
  // Get the place details from the autocomplete object.
  var place = autocomplete.getPlace();

  for (var component in componentForm) {
    document.getElementById(component).value = '';
    document.getElementById(component).disabled = false;
  }

  // Get each component of the address from the place details
  // and fill the corresponding field on the form.
  for (var i = 0; i < place.address_components.length; i++) {
    var addressType = place.address_components[i].types[0];
    if (componentForm[addressType]) {
      var val = place.address_components[i][componentForm[addressType]];
      document.getElementById(addressType).value = val;
    }
  }
}

// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(function(position) {
      var geolocation = {
        lat: position.coords.latitude,
        lng: position.coords.longitude
      };
      var circle = new google.maps.Circle({
        center: geolocation,
        radius: position.coords.accuracy
      });
      autocomplete.setBounds(circle.getBounds());
    });
  }
}
<form id="myform" method="POST" 
              action="https://script.google.com/macros/s/AKfycbzF_r-4ENEXSMD36Ry_gQ9iSQ5dqfpZiw3VytR7LmpSAwxbEcRelxhGJroi8QCBavEI/exec"
              autocomplete="off" class="form" role="form">
                <input autocomplete="false" name="hidden" type="text" style="display:none;">
                
                <div class="form-group row">
                  
                  <label>Business address</label>
                  <div class="col-sm-4">
                    
                    <input id="autocomplete" autocomplete="off" autofill="off" placeholder="Postcode" onFocus="geolocate()" type="text"
                      class="form-control">
                  </div>
                </div>
                <br>
                <div class="form-group row">
                  <label>Number</label>
                  <div class="col-sm-4">
                    <input name="No."  class="form-control" id="street_number" disabled="false" required>
                  </div>
                  <label>Street</label>
                  <div class="col-sm-4">
                    <input name="Street"  class="form-control" id="route" disabled="true" required>
                  </div>
                </div>
                <br>
                <div class="form-group row">
                  <label>City</label>
                  <div class="col-sm-4">
                    <input name="City"  class="form-control field" id="locality" disabled="true" required>
                  </div>
                  <label>State</label>
                  <div class="col-sm-4">
                    <input name="State" class="form-control" id="administrative_area_level_1" disabled="true">
                  </div>
                </div>
                <br>
                <div class="form-group row">
                  <label>Postal/zipcode</label>
                  <div class="col-sm-4">
                    <input name="Postcode"  class="form-control" id="postal_code" disabled="true" required>
                  </div>
                  <label>Country</label>
                  <div class="col-sm-4">
                    <input name="Country"  class="form-control" id="country" disabled="true">
                  </div>
                </div>

                <div class="form-group row">
                  <label>Lat</label>
                  <div class="col-sm-4">
                    <input name="Lat"  class="form-control" id="Lat" disabled="true" required>
                  </div>
                  <label>Lng</label>
                  <div class="col-sm-4">
                    <input name="Lng"  class="form-control" id="Lng" disabled="true">
                  </div>
                </div>

              </div>
            <div class="form-group row">
              
              <div id="submit">
                <button type="submit" value="Submit" >Submit</button>
              </div>
            </div>
            </form>
            <script
        src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places&callback=initAutocomplete"
        async defer></script>

Now I also have this code snippet for calling the google geocoding API

 document.getElementById('myform').addEventListener('submit', function(e){
    e.preventDefault(); //prevent form submit
    const place = autocomplete.getPlace(); //get place from autocomplete
    if (!place || !place.geometry) { //check if valid location
      swal("You have provided an Invalid address","Enter a valid Address", "warning");
      return;
    }
    // It is valid, proceed to geocode!
    else {
      // Listen for form submit
      document.getElementById('myForm').addEventListener('submit', geocode);
      function geocode(e){
        // Prevent actual submit
        e.preventDefault();
        var location = document.getElementById('location-input').value; //* I think this is where I might have made a mistake
        axios.get('https://maps.googleapis.com/maps/api/geocode/json', {
            params:{
                address: location,
                key: 'YOUR_API_KEY'
            }
        })
        .then(function(response){
            // Log full response
            console.log(response);
    
            // Formatted Address
            var formattedAddress = response.data.results[0].formatted_address;
            
            // Address Components
            var addressComponents = response.data.results[0].address_components;
    
            // Get values from the input fields
            var veg_planted = getInputVal('veg_planted');
        
            // Get geometry 
            var lat = response.data.results[0].geometry.location.lat;
            var lng = response.data.results[0].geometry.location.lng;
            var coords= (formattedAddress + ": " + lat + "," + lng);
            console.log(coords);
    
            // Save messages
            saveMessage(veg_planted, coords);
            
        })
        .catch(function(error){
            console.log(error);
        });
    }
    }
  });

Can this function be integrated with the autocomplete function I have to make the form fill out Lat. and Lng. values? If anyone out there has the solution it would help me a lot. Thank you.


Solution

  • You don't need to use the geocoder. The places API returns the lat/lng coordinates in its response (if it has them).

    PlaceResult.geometry
    geometry optional
    Type: PlaceGeometry optional
    The Place’s geometry-related information.

    PlaceGeometry interface
    Defines information about the geometry of a Place.
    Properties
    location optional
    Type: LatLng optional
    The Place’s position.

    document.getElementById("Lat").value = place.geometry.location.lat();
    document.getElementById("Lng").value = place.geometry.location.lng();
    

    working code snippet:

    var placeSearch, autocomplete;
    var componentForm = {
      street_number: 'short_name',
      route: 'long_name',
      locality: 'long_name',
      administrative_area_level_1: 'short_name',
      country: 'long_name',
      postal_code: 'short_name'
    };
    
    function initAutocomplete() {
      // Create the autocomplete object, restricting the search to geographical
      // location types.
      autocomplete = new google.maps.places.Autocomplete(
        /** @type {!HTMLInputElement} */
        (document.getElementById('autocomplete')), {
          types: ['geocode']
        });
    
      // When the user selects an address from the dropdown, populate the address
      // fields in the form.
      autocomplete.addListener('place_changed', fillInAddress);
    }
    
    function fillInAddress() {
      // Get the place details from the autocomplete object.
      var place = autocomplete.getPlace();
    
      for (var component in componentForm) {
        document.getElementById(component).value = '';
        document.getElementById(component).disabled = false;
      }
      document.getElementById("Lat").value = place.geometry.location.lat();
      document.getElementById("Lng").value = place.geometry.location.lng();
    
      // Get each component of the address from the place details
      // and fill the corresponding field on the form.
      for (var i = 0; i < place.address_components.length; i++) {
        var addressType = place.address_components[i].types[0];
        if (componentForm[addressType]) {
          var val = place.address_components[i][componentForm[addressType]];
          document.getElementById(addressType).value = val;
        }
      }
    }
    
    // Bias the autocomplete object to the user's geographical location,
    // as supplied by the browser's 'navigator.geolocation' object.
    function geolocate() {
      if (navigator.geolocation) {
        navigator.geolocation.getCurrentPosition(function(position) {
          var geolocation = {
            lat: position.coords.latitude,
            lng: position.coords.longitude
          };
          var circle = new google.maps.Circle({
            center: geolocation,
            radius: position.coords.accuracy
          });
          autocomplete.setBounds(circle.getBounds());
        });
      }
    }
    <form id="myform" method="POST" action="https://script.google.com/macros/s/AKfycbzF_r-4ENEXSMD36Ry_gQ9iSQ5dqfpZiw3VytR7LmpSAwxbEcRelxhGJroi8QCBavEI/exec" autocomplete="off" class="form" role="form">
      <input autocomplete="false" name="hidden" type="text" style="display:none;">
    
      <div class="form-group row">
    
        <label>Business address</label>
        <div class="col-sm-4">
    
          <input id="autocomplete" autocomplete="off" autofill="off" placeholder="Postcode" onFocus="geolocate()" type="text" class="form-control">
        </div>
      </div>
      <br>
      <div class="form-group row">
        <label>Number</label>
        <div class="col-sm-4">
          <input name="No." class="form-control" id="street_number" disabled="false" required>
        </div>
        <label>Street</label>
        <div class="col-sm-4">
          <input name="Street" class="form-control" id="route" disabled="true" required>
        </div>
      </div>
      <br>
      <div class="form-group row">
        <label>City</label>
        <div class="col-sm-4">
          <input name="City" class="form-control field" id="locality" disabled="true" required>
        </div>
        <label>State</label>
        <div class="col-sm-4">
          <input name="State" class="form-control" id="administrative_area_level_1" disabled="true">
        </div>
      </div>
      <br>
      <div class="form-group row">
        <label>Postal/zipcode</label>
        <div class="col-sm-4">
          <input name="Postcode" class="form-control" id="postal_code" disabled="true" required>
        </div>
        <label>Country</label>
        <div class="col-sm-4">
          <input name="Country" class="form-control" id="country" disabled="true">
        </div>
      </div>
    
      <div class="form-group row">
        <label>Lat</label>
        <div class="col-sm-4">
          <input name="Lat" class="form-control" id="Lat" disabled="true" required>
        </div>
        <label>Lng</label>
        <div class="col-sm-4">
          <input name="Lng" class="form-control" id="Lng" disabled="true">
        </div>
      </div>
    
      </div>
      <div class="form-group row">
    
        <div id="submit">
          <button type="submit" value="Submit">Submit</button>
        </div>
      </div>
    </form>
    <script src="https://maps.googleapis.com/maps/api/js?key=AIzaSyB_MrpX85obMpsk_eEdfE-iPIt06qbHyt0&libraries=places&callback=initAutocomplete" async defer></script>