crt_featured-01

Geeking Out: Alternatives for Reverse Geocoding with node.js

NOTE: CRT is excited to have Mark Lesswing, Chief Technology Officer and SVP of ITS at NAR, write a series of posts on his experiences with node.js. In the article below, Mark geocoding and libraries and services that prove useful in the node.js environment. Please note that this will be a pretty technical series and speaks to the diversity of topics CRT will cover. We will indicate when we have articles that are more technical as we post them. Thank you. – Chad

There is a really good extension available for node.js that performs Reverse Geocoding called “geocoded” (go figure). It was well written by Stephen Wyatt.  Installing it is quite simple:

>npm install geocoder

The simple scenario I will address in this post is using a mobile device (tablet or handset) to gather latitude and longitude information, passing it to a node.js server via a WebSocket and returning the address to the user’s device.

Looking at Mr. Wyatt’s module, you will notice that it utilizes the Google REST service accessed over node.js http client.  There are other services that can be used to perform the same process.  Here are a couple:

  • Bing
  • Factual
  • GeoNames

Each of the can be accessed in a RESTful manner (GET) and each have their advantages and disadvantages.  The advantages are GET requests with JSON responses and the disadvantages typically focus on accuracy.  Please read and respect the associated Terms and Conditions with all of these service because I will only addressing using them with node.js. I have provided a “signature” for each REST call and you must substitute appropriate values for :

  • {YOUR_LAT}
  • {YOUR_LONG}
  • {YOUR_API_KEY}

I have also provided some information about where to find the address information (that I want to display back through a WebSocket to the user’s device) in the response.  Of course, you would have already run the response through JSON.parse() before looking at the response structure.  In these examples, the “geoResponse” object results from a call to JSON.Parse().

Bing

This service requires an API key that easy to obtain from Microsoft. Generally this is an accurate service; urban and rural locations translate pretty closely. This service provides JSON as a default, but can also provide XML responses.  Also, the address comes back in a form that is ready to use.  If you do not like the format, you can also gather the pieces together in another form.

http://dev.virtualearth.net/REST/v1/Locations/{YOUR_LAT},{YOUR_LONG}?&key={YOUR_API_KEY}

var resolvedAddress = geoResponse.resourceSets[0].resources[0].address.formattedAddress;

Factual

This service requires an API key that is easy to obtain from Factual. I found that in urban areas, this service is highly accurate and outside of the city (I live in a very rural area), less so.

http://api.v3.factual.com/places/geocode?geo={“$point”:[ {YOUR_LAT}, {YOUR_LONG} ]}&KEY={YOUR_API_KEY]

Hint: The KEY argument must be in all upper case.

var geoObject = geoResponse.response.data[0];
var resolvedAddress = geoObject.address + ” ” + geoObject.locality + “, ” + geoObject.region + ”  ” + geoObject.postcode;

Hint: The address and postcode attributes are not always in the return structure.  If you are rural, they may not be there.

GeoNames

This service requires an API key that is easy to obtain from GeoNames. I included this service because the content is covered by a Creative Commons license. It is a collection of interesting data sources accessed with a REST interface.

api.geoNames.org/findNearestAddressJSON?lat={YOUR_LAT}&lng={YOUR_LONG}&username{YOUR_API_KEY}

Hint: The API key is your login after you register.

var geoObject = geoResponse.address;
resolvedAddress = geoObject.streetNumber + ” ” + geoObject.street + ” ” + geoObject.placename + “, ” + geoObject.adminCode1 + ”  ” + geoObject.postalcode;

Google

There is no registration for this service (requires no key) but there are limits to usage. This is an accurate service that includes a flag to tell the service is the device you gathered the latitude and longitude from had a special sensor to perform that task.  Also, the address comes back in a form that is ready to use, a trait shared with Bing.

http://maps.googleapis.com/maps/api/geocode/json?latlng={YOUR_LAT},{YOUR_LNG}&sensor=false

var resolvedAddress = geoResponse.results[0].formatted_address;

Code Template

The following piece of code will help you put together a node.js server that is configurable for different GeoCoding services:

var options = null
switch (REVERSE_GEOCODE_VENDOR) {
case "Bing":
var geoKey = "{YOUR_API_KEY}";
var addressArgs = aMessage.currentLatitude + "," + aMessage.currentLongitude + "?key=" + geoKey;
options = { hostname: "dev.virtualearth.net", port: 80, path: "/REST/V1/Locations/" + addressArgs };
break;

case "Factual":
var addressArgs = 'geo={"$point":[' + aMessage.currentLatitude + "," + aMessage.currentLongitude + "]}&KEY="{YOUR_API_KEY}";
options = { hostname: "api.v3.factual.com", port: 80, path: "/places/geocode?" + addressArgs, method: "GET" };
break;

case "GeoNames":
var addressArgs = "lat=" + aMessage.currentLatitude + "&lng=" + aMessage.currentLongitude + "&username={YOUR_API_KEY}";
options = { hostname: "api.geoNames.org", port: 80, path: "/findNearestAddressJSON?" + addressArgs, method: "GET" };
break;

case "Google":
var addressArgs = "latlng=" + aMessage.currentLatitude + "," + aMessage.currentLongitude + "&sensor=false";
options = { hostname: "maps.googleapis.com", port: 80, path: "/maps/api/geocode/json?" + addressArgs, method: "GET" };
break;
}

var geoRequest = http.get(options, function(res) {
var fullOutput = "";
res.setEncoding('utf8');

res.on('data', function (chunk) {
fullOutput = fullOutput + chunk;
});

res.on('end', function () {
var geoResponse = JSON.parse(fullOutput);
//
// Hint: put all of your code in here to make sure the entire result has been read from the service. Hints on working through the different formats were provided above.
//
switch (REVERSE_GEOCODE_VENDOR) {
case "Bing":
//--- your code
break;

case "Factual":
//--- your code
break;

case "GeoNames":
//--- your code
break;

case "Google":
//--- your code
break;

}
}); // on end
}); // http

If you start from these basics, you will quickly realize the potential behind node.js WebSockets to perform GeoLocation processing.

ADD YOUR COMMENT