Targeted Geolocation with Geonames

Share this article

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 www.geonames.org 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:
http://api.geonames.org/findNearbyPlaceNameJSON?lat=53.4774&lng=-2.2381&username=yourusername
Let’s break it down:
  • http:// is the transfer protocol. Generally, REST services exploit the ubiquity and ease of HTTP.
  • api.geonames.org 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",
              "adminCode1":"ENG",
              "fclName":"city, village,...",
              "countryCode":"GB",
              "lng":-2.23743438720703,
              "fcodeName":"seat of a second-order administrative division",
              "distance":"0.39678",
              "toponymName":"Manchester",
              "fcl":"P",
              "name":"Manchester",
              "fcode":"PPLA2",
              "geonameId":2643123,
              "lat":53.4809464283615,
              "adminName1":"England",
              "population":395515}]}
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:
<?php
class Geonames
{
    protected $username;

    public function  __construct($username) {
        $this->username =  $username;
    }

    public function  getPlaceName($lat, $lng) {
        $url =  sprintf(
            "http://api.geonames.org/findNearbyPlaceNameJSON?lat=%f&lng=%f&username=%s",
            $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.
<?php
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">
 <head>
  <meta charset="utf-8">
  <title>Hello!</title>
  <script src="jquery.js"></script>
  <script>
(function ($) {
    $(document).ready(function() {
        if (navigator.geolocation) {
            navigator.geolocation.getCurrentPosition(
                function (position) {
                    var lat = position.coords.latitude;
                    var lng = position.coords.longitude;
                    $("#banner").load("/prompt.php?lat="+lat+"&lng="+lng);
                },
                function (error) {
                    console.log("An error occurred obtaining position.");
                });
        }
        else {
            $("#banner").html("Default content goes here"); 
        }
    });
})(jQuery);
  </script>
 </head>
 <body>
  <div id="banner"></div>
 </body>
</html>
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.

Summary

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

Frequently Asked Questions about Targeted Geolocation with GeoNames

How does GeoNames work for targeted geolocation?

GeoNames is a geographical database that contains over 11 million geographical names. It works by providing various web services that allow users to extract useful information about different locations. These services include finding places by name, finding places near a certain location, and getting information about a specific geographical location. The data provided by GeoNames can be used for a variety of purposes, such as creating location-based applications, performing geographical analysis, and more.

What kind of data can I get from GeoNames?

GeoNames provides a wealth of data about geographical locations. This includes basic information like the name of the place, its latitude and longitude, and its population. It also includes more detailed information like the country the place is in, its timezone, and even its elevation. Additionally, GeoNames provides various other data like postal codes, administrative divisions, and more.

How can I use GeoNames in my application?

GeoNames provides various web services that can be used in your application. These services return data in a variety of formats, including XML, JSON, and CSV, which can be easily integrated into most applications. To use these services, you need to make HTTP requests to the GeoNames server with the appropriate parameters.

Is GeoNames free to use?

Yes, GeoNames is free to use. However, it does offer a premium service for users who need higher reliability and availability. The premium service offers additional features like faster response times, priority support, and more.

How accurate is the data provided by GeoNames?

The data provided by GeoNames is generally very accurate. It is sourced from various authoritative sources and is regularly updated. However, like any data source, it may not be 100% accurate and should be used in conjunction with other data sources for critical applications.

Can I contribute to the GeoNames database?

Yes, GeoNames is a collaborative project and welcomes contributions from users. You can contribute by adding new geographical names, updating existing ones, or even helping to translate the interface into different languages.

What is the coverage of GeoNames?

GeoNames covers all countries and contains over 11 million geographical names. It includes all types of geographical features, from countries and cities to mountains and lakes.

How can I search for a place using GeoNames?

You can search for a place using the GeoNames search web service. This service allows you to search for places by name, and returns a list of places that match the search criteria, along with their geographical information.

Can I use GeoNames for commercial purposes?

Yes, GeoNames can be used for commercial purposes. However, if you are using the free service, you must credit GeoNames as the data source. If you need higher reliability and availability, you can opt for the premium service.

How can I get support for GeoNames?

GeoNames has a community forum where users can ask questions and get help from the community. For premium users, GeoNames offers priority support.

Lukas WhiteLukas White
View Author

Lukas is a freelance web and mobile developer based in Manchester in the North of England. He's been developing in PHP since moving away from those early days in web development of using all manner of tools such as Java Server Pages, classic ASP and XML data islands, along with JavaScript - back when it really was JavaScript and Netscape ruled the roost. When he's not developing websites and mobile applications and complaining that this was all fields, Lukas likes to cook all manner of World foods.

Intermediate
Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week