Our address-generator CLI app is now capable of grabbing a random US address. We just need to geocode that address. In this post, we’ll leverage the Google Map Geocoding API to do exactly that!
If you’re just jumping on, you can check out the previous parts here: Part 1, Part 2, and Part 3.
Prerequisites
We’ve already got an address, thanks to fakena.me. We’ll now augment that with the latitude and longitude using the Google Maps Geocoding API.
You will need your own Google API key before you can begin, and you will need to enable the Google Maps Geocoding API for your account. This guide will walk you through getting all that setup and configured.
In this app, we will be calling the Geocoding API directly. We could use Google’s JavaScript library if we wanted, but that really isn’t necessary for this simple use case.
Making the Request
We’ll start by making changes to the AddressCreator
class.
The first thing we need to do is add a const
for our Google API key:
const rp = require('request-promise');
const cheerio = require('cheerio');
const googleApiKey = '---YOUR KEY HERE---';
//snip
Obviously, if you are following along, you’ll want to use your own API key.
Now we can take advantage of promise chaining, and call the Geocoding API after we’ve extracted an address from fakena.me:
//We need to declare our result so we can update it from two promises...
let address;
return rp(requestOptions).then($ => {
//You've seen this before... snip!
address = {
street: match[1],
city: match[2],
state: match[3],
zip: match[4]
};
return rp(`https://maps.googleapis.com/maps/api/geocode/json?address=${rawAddress.replace('<br>', ' ')}&key=${googleApiKey}`);
}).then(body => {
//TODO: We need to do something with the response body!
return address;
});
If you look closely, you’ll see geocode/json
in the URL that we’re requesting. That tells Google Maps to return JSON data to us. All we need to do now is parse it!
Parsing the Result
The response we’ll get from Google should look like this:
{
"results" : [
{
"address_components" : [
{
"long_name" : "2385",
"short_name" : "2385",
"types" : [ "street_number" ]
},
{
"long_name" : "North Chmelka Road",
"short_name" : "N Chmelka Rd",
"types" : [ "route" ]
},
{
"long_name" : "Garden City",
"short_name" : "Garden City",
"types" : [ "locality", "political" ]
},
{
"long_name" : "Sherlock",
"short_name" : "Sherlock",
"types" : [ "administrative_area_level_3", "political" ]
},
{
"long_name" : "Finney County",
"short_name" : "Finney County",
"types" : [ "administrative_area_level_2", "political" ]
},
{
"long_name" : "Kansas",
"short_name" : "KS",
"types" : [ "administrative_area_level_1", "political" ]
},
{
"long_name" : "United States",
"short_name" : "US",
"types" : [ "country", "political" ]
},
{
"long_name" : "67846",
"short_name" : "67846",
"types" : [ "postal_code" ]
}
],
"formatted_address" : "2385 N Chmelka Rd, Garden City, KS 67846, USA",
"geometry" : {
"location" : {
"lat" : 37.9866424,
"lng" : -100.956987
},
"location_type" : "RANGE_INTERPOLATED",
"viewport" : {
"northeast" : {
"lat" : 37.9879913802915,
"lng" : -100.9556380197085
},
"southwest" : {
"lat" : 37.9852934197085,
"lng" : -100.9583359802915
}
}
},
"partial_match" : true,
"place_id" : "Ei0yMzg1IE4gQ2htZWxrYSBSZCwgR2FyZGVuIENpdHksIEtTIDY3ODQ2LCBVU0E",
"types" : [ "street_address" ]
}
],
"status" : "OK"
}
There’s a lot of useful information in there, but all we really care about is the lat/long.
We can parse the result into a JSON object, then we can easily access the lat/lng props that are buried in there:
const response = JSON.parse(body);
address.latitude = response.results[0].geometry.location.lat;
address.longitude = response.results[0].geometry.location.lng;
Our AddressCreator
now looks like this:
const rp = require('request-promise-native');
const cheerio = require('cheerio');
const googleApiKey = 'AIzaSyDghZSs85he6zk9Qhm868m3S6aUFj-TozQ';
class AddressCreator {
static getRandomAddress() {
const requestOptions = {
uri: 'https://fakena.me/random-real-address/',
transform: html => cheerio.load(html)
}
let address;
return rp(requestOptions).then($ => {
const rawAddress = $('strong').html();
const regex = /(\d+ [\w ]+)\<br\>([\w ]+), ([A-Za-z]{2}) (\d{5}-\d{4})/g;
const match = regex.exec(rawAddress);
if (match === null || match.length !== 5) {
console.error('Unable to parse the result!');
return;
}
address = {
street: match[1],
city: match[2],
state: match[3],
zip: match[4]
};
return rp(`https://maps.googleapis.com/maps/api/geocode/json?address=${rawAddress.replace('<br>', ' ')}&key=${googleApiKey}`);
}).then(body => {
const response = JSON.parse(body);
address.latitude = response.results[0].geometry.location.lat;
address.longitude = response.results[0].geometry.location.lng;
return address;
});
}
}
module.exports = AddressCreator;
Geocoding Successful!
If we run our app now, we’ll see the street, city, state, zip, and the lat/long!
node .\index.js
3210 Wimberly Rd
Amarillo TX 79109-3433
(35.180013, -101.8646735)
That’s cool and all, but when I wrote this app, what I really needed was this data in JSON format.
What if we added a parameter to our CLI app to control the output?
Another thing that would be useful: to automatically copy the output to the clipboard! What if we added a flag to do that, too?
Up Next
In the next (and maybe final?) post in this series, we’ll add support for parameters to our CLI app to cover these new features. Check back soon!