Targeted Geolocation with Geonames

    Lukas White
    Lukas White

    Location-based applications are all the rage. What used to be prohibitively expensive GPS devices are now sitting in the of pockets of a great many people, embedded within smartphones and other devices on an unprecedented scale, and services making use of location information are springing up all the time. The social web, for example, has become location-aware with existing services such as Facebook following the lead of frontrunners like Foursquare and Gowalla by introducing Places. Twitter has added location information to tweets, more and more services are tailoring themselves to where you are geographically, and of course advertising is getting in on the act with location-based targeting of advertisements a lucrative and expanding area.

    In addition to pinpoint accuracy via GPS on handheld devices, other mechanisms from IP address-based geolocation to Wi-Fi triangulation are becoming more and more sophisticated, and with the W3C solidifying support for geolocation with its Geolocation API Specification the opportunities are vast.

    In this article I’ll share with you one important aspect of geolocation – mapping a physical location to a descriptive place name. I’ll look at how you can use Geonames, an online service offering free access to a large collection of geographic data, to help you develop applications taking advantage of locative information.

    What is Geolocation?

    Location-aware applications rely on being able to locate where you are, and this is what geolocation is all about. After all, once the application knows your location, it can go on to find the nearest store, guide you through the appropriate route to a destination, or target relevant advertisements to you. Geolocation, then, is simply the mechanism for identifying your geographical location.

    There are two challenges with geolocation – finding out where someone is, and then describing that location. There are a number of techniques for determining someone’s location and they vary in sophistication and, most importantly, in accuracy. Latitude and longitude can pinpoint your location to within meters anywhere on Earth, but the values themselves have little meaning to most people. Sometimes fine resolution isn’t really needed, and it’s sufficient enough to identify the name of the nearest city or town.

    The Geonames Service

    Geonames is an online resource offering downloadable databases of a raft of geographical information, including mappings of latitude and longitude information to real-world locations. These files are, as you might imagine, huge as well as volatile. You’re free to download and use these files yourself, but there Geonames also makes web services available one of which is what I’ll use here.

    In order to use the web services, you’ll need to create a free account. Go to and click the Login link at the top right of the page. There you’ll be able to sign in or create a new user account. Once you sign up you’ll be sent an email with a confirmation link; check your mail and confirm your account.

    After you’ve confirmed your account, click your username at the top right of the page to go to the account management page and enable your account to use the web services.

    You now have a username which permits you access to the Geonames’ web services. You may wish to familiarize yourself with the terms and conditions.

    The particular service I’ll demonstrate is findNearbyPlaceName, which does just that – finds the nearest place name to a given latitude and longitude. The documentation states that it’s a RESTlike service with data available in XML and JSON formats (if you’re not clear on what a REST service is, I recommend you read up before continuing). The decision whether to use XML or JSON is often a personal one. I prefer JSON because it’s easier to parse, more lightweight, and works beautifully with JavaScript.

    A RESTlike Location Web Service

    One of the great things about using a REST web service to retrieve information is that, because of its very nature, you can issue a simple GET request in order to test it out – and you can do so using just a web browser! All you need to do is provide a properly formatted URL. In your browser, enter the following URL:

    Let’s break it down:

    • http:// is the transfer protocol. Generally, REST services exploit the ubiquity and ease of HTTP.
    • is the domain name which becomes the initial segment of the service’s endpoint.
    • findNearbyPlaceNameJSON identifies the service. Often the return format you’re requesting is passed as a parameter, file extension, or even using an ACCEPT header, but this particular service incorporates it in the service’s name.
    • lat=53.4774&lng=-2.2381 are the latitude and longitude parameters for which you are requesting the nearest place name.
    • username=yourusername is the username which you enabled to use the service. This service accepts is as a parameter, but other REST services may implement other authorization schemes such as HTTP-Authentication.

    The service responds to the request by sending back the following JSON (I’ve formatted it nicely here for the sake of readability):

    {"geonames":[{"countryName":"United Kingdom",
                  "fclName":"city, village,...",
                  "fcodeName":"seat of a second-order administrative division",

    As you can see, the result is an object with the property geonames which contains an array of one object with a number of properties. The lat and lng properties provide the geographic location of the place the service has identified, distance shows you how far this place is from the position you provided, and most useful is the name property which tells you the location’s name. countryName property tells you the country, in this case the United Kingdom.

    The data also provides information about administrative divisions. The exact nature of such divisions vary from country to country; in this case adminCode1 (the first-level administrative division) identifies “England”; if you were to locate Houston in the United States using this service you’d find that adminCode1 shows “TX” for the state of Texas.

    Additionally, geonameId contains the unique identifier used in the Geonames database which, you’ll remember, is downloadable. You may have a use for this at some point if you go on to use the data for something more substantial.

    Targeted Prompting – A Practical Example

    As an example, let’s assume your web site has feature that prompts the user find cinema listings, and you’d like to personalize the header text with the name of the user’s nearest town – something like, “Find cinema listings near you in Manchester”. Immediately there’s one important limitation you should be aware of: Geolocation is, in the majority of cases, performed on the client. Whether by a web browser or the device itself, this information has to be sent back to the server if you wish to use it there. JavaScript on the client-side would obtain the user’s coordinates and send them to a server-side script via Ajax which then interfaces with the web service and returns a location name.

    Server-side Code

    Let’s start by writing the server-side PHP code. The Geonames website does provide a PEAR package, as well as libraries for frameworks including Zend Framework and Yii, to simplify access to their services – however, I’ll use plain PHP for the purposes of this tutorial.

    This simple class encapsulates the call to the findNearbyPlaceName service:

    class Geonames
        protected $username;
        public function  __construct($username) {
            $this->username =  $username;
        public function  getPlaceName($lat, $lng) {
            $url =  sprintf(
                $lat, $lng, $this->username);
            $response = file_get_contents($url);
            if ($response === false) {
                throw new Exception("Failure to obtain data");
            $places = json_decode($response);
            if (!$places) {
                throw new Exception("Invalid JSON response");
            if (is_array($places->geonames) && count($places->geonames)) {
                return $places->geonames[0]->name;
            else {
                return "Unknown";

    The class is named Geonames, whose constructor takes a single parameter as an argument – the username you registered earlier, which is then stored in a protected member variable.

    The getPlaceName() method calls the findNearbyPlaceName service. Because this is a simple GET request to a RESTlike service, you can simply incorporate the parameters in the URL as you did when testing the web service through your browser earlier. I used sprintf() to build the up URL, an approach I feel is cleaner than concatenation and also permits me to specify the format types for the placeholders; latitude and longitude are floats (%f) and the username is a string (%s). The URL is then passed to file_get_contents() which receives the service’s JSON response. The response is decoded using json_decode() and the first place name is returned from the method.

    The script called by Ajax will receive the latitudinal and longitudinal coordinates, use them to query the Geonames web service through the new class, and return the name of the nearest location.

    require "../include/Geonames.php";
    $lat = floatval($_GET["lat"]);
    $lng = floatval($_GET["lng"]);
    $geo = new Geonames("username");
    $prompt = "Find cinema listings near you";
    try {
        $place = $geo->getPlaceName($lat, $lng);
        if ($place != "Unknown") {
            $prompt .= " in " . $place;
    catch (Exception $e) {
        error_log("Error with web service: " . $e->getMessage());
    header("Content-Type: text/plain");
    echo $prompt;

    The code starts with a default message, “Find cinema listings near you”. If the call to the getPlaceName() method doesn’t raise an exception and the name returned isn’t “Unknown”, the message is further personalized.

    Client-side Code

    Now let’s turn our attention to the HTML page and JavaScript code that will call the PHP.

    <!DOCTYPE html>
    <html lang="en">
      <meta charset="utf-8">
      <script src="jquery.js"></script>
    (function ($) {
        $(document).ready(function() {
            if (navigator.geolocation) {
                    function (position) {
                        var lat = position.coords.latitude;
                        var lng = position.coords.longitude;
                    function (error) {
                        console.log("An error occurred obtaining position.");
            else {
                $("#banner").html("Default content goes here"); 
      <div id="banner"></div>

    If geolocation is supported by the browser then navigator.geolocation will return true, otherwise some default content is inserted into the element with the ID banner. When geolocation is supported, an attempt is made to obtain the user’s location using navigator.geolocation.getCurrentPosition(), which is passed success and error callbacks.

    At this point the browser will ask for your permission to share your location information with the web page. If you grant permission then the success callback is executed and, if the application succeeds in retrieving the location’s name, the banner will contain a personalized invitation to explore the cinema listings. If you deny permission to the browser, the error callback is executed instead.


    In this short article I’ve introduced geolocation and one way in which you can exploit it right now to personalize a visitor’s experience on a website. Although the example I’ve given is somewhat clunky in its use of AJAX to load a block into a page retrospectively, it serves to illustrate one of the technical challenges of geolocation – information is largely obtained client-side and thus there are hurdles you need to overcome, such as asking permission to use the information. Perhaps you may wish to try storing information server-side in a session once you’ve obtained it, look at improving performance using caching, or experiment with more sophisticated targeting. The next logical step is to examine how you would manage relationships between towns – what if my local village doesn’t have a cinema, what’s the nearest town or city that does? That’s for another time, but for now I encourage you to look at API support for geolocation (you could even look at the specification), look at an entirely server-side IP-based solution, or delve into the mathematics behind geolocation. Most of all, I encourage you to have fun!

    Image via Olivier Le Moal / Shutterstock