- 4 minutes read

Modern smartphones tell you where you've taken a photo. Unfortunately, as soon as you copy the image to your PC, the information seems to be lost. Well, it isn't, but it hides in the EXIF metadata of the image. Plus, it's not the name of the city, but the precise GPS coordinate. Luckily, it's surprisingly simple to convert that to a real address.

There's a library for that?

The task was part a of small Node.js project of mine, so my first instinct was to use a library. The keywords are "geolocation" and "reverse geolocation". There are several Node.js libraries. Some of them look feature-rich and offer a simple API, so there's nothing wrong with that approach. For instance, node-geocoder looks promising. According to the documentation, it allows you to chose from no less than 19 "Geocoder providers", among them Google Maps and OpenStreetMap.

However, when I tried the library, I needed an API key of the geocoder provider. When trying to get such a key, I noticed that there's a more simple way.

Using the web API directly

OpenStreetMap has a web API. It's not exactly a REST API, but it's almost as simple to use. You can even use it in the browser. If you're only interested in a few GPS coordinates, just enter the URL:

https://nominatim.openstreetmap.org/reverse?format=xml&lat=50&lon=8.22&zoom=18&addressdetails=1

The response is an XML file containing the address. Depending on the parameter zoom, the address contains more details. Zoom level 0 only contains the continent and the country, zoom level 9 contains the city and zoom level 18 even includes the house number. In my project, I went with level 18, but ignored everything except the town.

The other two parameters are the latitude (lat) and the longitude (long) of the GPS coordinate. If you have a traditional GPS coordinate with degrees, minutes, and seconds, you have to convert it to the decimal format.

Surprises

OpenStreetMap differentiates between cities, towns, villages, and hamlets. I suppose that's a feature - but it shows you have to brace yourself for some surprises. In most cases, only one of those fields is populated.

Programmatic access

You can use OpenStreetMap for free, with a few limitations. One of the limitations is that you are only allowed to use it once per second (unless you pay for it). In my case, that wasn't much of a problem, so I added a sleep() function to my code and waited a bit longer for the result.

The other limitation is that you have a add a referer or an user-agent to the code. The resulting node.js code looks like so:

const https = require('https'); const convert = require('xml-js'); function getTown(latitude, longitude, callback = logTown) { var options = { host: 'nominatim.openstreetmap.org', path: '/reverse?format=xml&lat=' + latitude + '&lon=' + longitude + '&zoom=18&addressdetails=1', method: 'GET', headers: { referer: '(add a referer here)', 'user-agent': '(add a user agent here)' } }; var g = https.get( options, response => { var body = ''; response.on('data', function(d) { body += d; }); response.on('end', function() { // Data reception is done, do whatever with it! const result1 = convert.xml2json(body, { compact: true, spaces: 4 }); const geo = JSON.parse(result1); if (geo.reversegeocode.addressparts.town) { callback(geo.reversegeocode.addressparts.town._text); } else if (geo.reversegeocode.addressparts.city) { callback(geo.reversegeocode.addressparts.city._text); } else if (geo.reversegeocode.addressparts.village) { callback(geo.reversegeocode.addressparts.village._text); } else if (geo.reversegeocode.addressparts.hamlet) { callback(geo.reversegeocode.addressparts.hamlet._text); } else { console.log("An error occurred - couldn't find the address of the GPS coordinates"); console.log(geo.reversegeocode.addressparts); } }); } ); g.end(); }

Wrapping it up

There are powerful libraries for gecoding out there. They have their uses, but if your use-case is simple, using the web API of OpenStreetMap directly does the trick as well. The code above has a lot of lines, but that's mostly due to the asynchronous approach of Node.js and the fact that OpenStreetMap distinguishes between towns, cities, villages, and hamlets.


Comments