đŸ‘©â€đŸ’»

Bulletproof Address Autocomplete

🏡 Home 📖 Chapter 👈 Prev 👉 Next
⚡  GMapsBook.com is crafted by Jozef Sorocin (🟱 Book a consulting hour) and powered by:
  • g-Xperts (Google Cloud & Business Profile Partner)
 
 
So far we’ve talked about purely visual Maps APIs — the Javascript API and the Static API.
The Google Maps Platform suite offers two more incredibly useful APIs:
  • the Geocoding Service & API
  • and the Places Autocomplete & API.
The scopes of these two APIs overlap heavily so in this chapter, you will learn how to distinguish between them, how to use them in conjunction, and when to deploy each independently.
 
First off, let’s clarify some terminology.

The Geocoding API

Geocoding is the process of turning an address into coordinates (latitude & longitude.)
Generally speaking, you’d use the Geocoding API to transform:
  • unambiguous postal addresses
  • as well as incomplete, poorly formatted, or misspelled addresses
into full, properly formatted addresses with latitude & longitude and clearly separated address components — street name & house number, district, postal code, administrative area, country name, and the 2-letter country ISO code.
For instance, given the query Cobrugurgbastei 1 (purposefully misspelled "Coburgbastei", a street in Vienna, Austria), the Geocoding API will respond with the formatted address Coburgbastei 1, 1010 Wien, Austria and the individual address components:
street: Coburgbastei house number: 1 district: Innere Stadt postal code: 1010 administrative area: Vienna city: Vienna country: Austria country ISO code: AT
Plus the coordinates:
latitude: 48.20585690 longitude: 16.3775195
Note that the available address components vary by country and region. You’ll learn how to transform Google’s geocoding response into a structure like this later on.

Client-side

You can geocode address strings using the client-side Javascript Geocoder module:
(async () => {
		// instantiate the Geocoder class
    const geocoder = new google.maps.Geocoder();
		// asynchronously obtain the 'results'
    const { results } = await geocoder.geocode({
      address: 'Coburgbastei Wien',
    });
    const result = results[0];
		// extract the fully matched address and the related geometry
    const { formatted_address, address_components, geometry } = result;
    const { location } = geometry;

    console.info({ formatted_address, address_componets, location });
  })();
 

Server-side

Or, reach for the corresponding server-side Node.js @googlemaps/google-maps-services-js module:
import { Client } from '@googlemaps/google-maps-services-js';

const geoClient = new Client();

const { data } = await geoClient.geocode({
  params: {
    key: 'AIza...', // use your backend key
    address: 'Coburgbastei Wien',
  },
});
const { results } = data;
const result = results[0];

// extract the fully matched address and the related geometry
const { formatted_address, address_components, geometry } = result;
const { location } = geometry;

console.info({ formatted_address, location });
 
Either of these calls would print:
{
	formatted_address: 'Coburgbastei, 1010 Wien, Austria',
	location: { lat: 48.2055761, lng: 16.3769497 },
  address_components: [{"long_name":"1","short_name":"1","types":["street_number"]},{"long_name":"Coburgbastei","short_name":"Coburgbastei","types":["route"]},{"long_name":"Innere Stadt","short_name":"Innere Stadt","types":["political","sublocality","sublocality_level_1"]},{"long_name":"Wien","short_name":"Wien","types":["locality","political"]},{"long_name":"Wien","short_name":"Wien","types":["administrative_area_level_2","political"]},{"long_name":"Wien","short_name":"Wien","types":["administrative_area_level_1","political"]},{"long_name":"Austria","short_name":"AT","types":["country","political"]},{"long_name":"1010","short_name":"1010","types":["postal_code"]}]
}
 
💡
Tip: import the Google Maps Platform Postman collection to quickly experiment with the available APIs:
Using Postman to request the Geocoding API
Using Postman to request the Geocoding API
 
💡
Pro tip: The Geocoding API accepts an address parameter as well as a latlng. Still, Google’s systems are smart enough to accept lat/lng strings in the address field as well so if your application’s geocoding feature needs to handle both string addresses and/or lat/lng lookups, you can pass the user queries inside the address parameter.
 
Notice the 3rd highlighted query parameter — place_id. What does that have to do with geocoding?

The Places API

🔑
Virtually all results returned by Google’s geocoding service will include a place_id.
Place IDs are available for most locations, including businesses, landmarks, parks, and intersections.
Keep in mind that it is possible for the same place or location to have multiple different place IDs and that they may change over time.
Google employs Place IDs extensively throughout Google Maps — and for a good reason. Oftentimes, multiple real-world points of interest may be located at the very same coordinates (think multi-floor office buildings housing dozens of firms) — so coordinates alone aren’t enough to determine a uniqueness of a place.
Now, the vast collection of Google’s places can searched using the dedicated Places API.
Somewhat counter-intuitively, Google assigns a place_id even to places without a clear business-related aspect. For instance, geocoding this random point in a forest near Vienna, Austria, returns place_id=ChIJYxYVPuKmbUcRtz8-hHCmQpA:
Geocoding a point and obtaining a place_id via maps.google.com
Geocoding a point and obtaining a place_id via maps.google.com
The details behind an arbitrary place_id can be retrieved:
Either using the client-side Javascript PlacesService module:
Import the places library first:
< script async defer
	src=".../maps/api/js?key=YOUR_KEY_STARTING_WITH_AIza&libraries=places&callback=initMap">
Then run .getDetails():
(async () => {
  placesService.getDetails(
    {
      placeId: 'ChIJv3zAHAmobUcRyYvq5F26aU4',
      fields: ['all'], // the `fields` attribute is compulsory; more on this later
    },
    function (results, status) {
      console.info({ results, status });
    }
  );
})();
Or via the server-side Node.js @googlemaps/google-maps-services-js module:
import { Client } from '@googlemaps/google-maps-services-js';

const geoClient = new Client();

const { data } = await geoClient.placeDetails({
  params: {
    key: 'AIza...', // use your backend key
    place_id: 'ChIJYxYVPuKmbUcRtz8-hHCmQpA'
  },
});
Either of these requests returns something along the lines of:
{
  "address_components": [...],
	"formatted_address": "Schönbrunner Schloßstraße 47, 1130 Wien, Austria",
	"geometry": {...},

  "business_status": "OPERATIONAL",
  "current_opening_hours": {...},
  "formatted_phone_number": "01 81113239",
  "international_phone_number": "+43 1 81113239",
  "name": "Schönbrunn Palace",
  "opening_hours": {...},
  "photos": [],
  "place_id": "ChIJv3zAHAmobUcRyYvq5F26aU4",
  "rating": 4.7,
  "reviews": [...],
  "types": [
    "tourist_attraction",
    "museum",
    "point_of_interest",
    "establishment"
  ],
  "user_ratings_total": 126172,
  "website": "https://www.schoenbrunn.at/",
}
Redacted place details of the Schönbrunn Palace, ChIJv3zAHAmobUcRyYvq5F26aU4
In contrast to the Geocoding response, the Place Details response is much richer.
Aside from the familiar address_components and geometry attributes, there are also:
We’ll come back to these extra fields later.
 
Generally speaking, you’d utilize the Places API to respond to user input — and do so as fast as possible:
  • If you’re building a ride-hailing app, your users will likely request rides to points of interest (POIs) like bars and restaurants.
  • If you’re optimizing the checkout experience for your meal delivery app, you’ll want to pre-fill the address form when the buyer selects a suggestion from the autocomplete dropdown.
 
To respond to user input, you’ll need an address autocompletion widget.

Client-side autocomplete widgets

The Autocomplete class

In contrast to the Geocoder class, the client-side Autocomplete class binds directly to an HTML <input> element, reacts to keydown events, and renders PlaceResult suggestions out of the box:
<body>
	<input id="addressAutocomplete" placeholder="Where to go?" />
	<script src="https://maps.googleapis.com/maps/api/js?key=AIza...&callback=initMap&libraries=places"></script>
</body>
index.html
const initMap = () => {
	// instantiate the autocomplete widge
	const autocomplete = new google.maps.places.Autocomplete(
		// bind it to the `input` element
	  document.getElementById('addressAutocomplete') as HTMLInputElement,
		// restrict the returnable fields
	  {
	    fields: ['formatted_address', 'geometry', 'name', 'place_id'],
	  }
	);
	
	// listen to the place selection event
	autocomplete.addListener('place_changed', () => {
		// notice that the callback doesn't include arguments
    // -> you'll need to call `.getPlace()` on the `autocomplete` instance
	  const selectedPlace = autocomplete.getPlace();
	  console.info({ selectedPlace });
	});
}

declare global {
  interface Window {
    initMap: () => void;
  }
}
window.initMap = initMap;
index.ts
You can style the dropdown container and the individual place suggestions according to this guide.
 
👍  The upsides of the Autocomplete widget are as follows:

Already purchased? Sign in here.