Building Location Aware Sites with Geo-IP

One of the most powerful features of the internet is the power to connect and share from opposite poles of the earth; no longer does your geographic location affect who you talk to or what you read. Companies that were once specific to a certain location now have access to the whole online market; with 1.4 billion people currently connected it opens up whole new revenue streams.

The final product will simply tell you where it thinks you are and allows you to set a new default location.

However with this global access there becomes another problem, not all data is global; the weather in London is most likely different to the current weather in New York. What about shopping? Currencies often change between borders and tax makes things even more complex. There have been countless solutions to this problem; the most glaringly obvious is a splash page or a form asking for the users location.

There’s nothing wrong with these solutions but there’s also nothing special about them, just more distractions and obstacles between the user and their final destination. In this tutorial we’ll be building a simple function and demo which solves this problem and should be easily pluggable into any new or existing project.

Location Uses?

As previously mentioned there are several uses for location based sites; simply looking through the days history in your favourite browser will probably reveal several sites that have asked for your location before, whether it be for simple data collection or put to use to give you more relevant information; local news, weather, messages from people close by or maybe to warn you about bad traffic in your area.

How many times in the last month have you entered your address or country? Whenever you’ve bought anything online or registered to a new site you’ve doubtless had to put in a variety of location based answers.

So what is GeoIP?

GeoIP is the common name given to the technique of determining a visitors Geographical location based on their visiting IP. It requires a database which pairs IP blocks to a country and/or city, these are available from several places online however for this tutorial to work than you will have to use the IPinfoDB SQL, available and updated monthly at http://www.ipinfodb.com/ip_database.php we’ll be specifically using the country only version with only one table, this is for performance and simplicity reasons – once you have the tutorial working with the country only version, modifying the script to work with the other versions should be simple. So go ahead and create a blank MySQL database and import the SQL straight into it.

1. Set up project

Create a blank index.php somewhere on your PHP enabled server and write the following lines into it, replacing all the variables to whatever username, password and database name you have chosen to use.

<?php
mysql_connect('localhost', '%USERNAME%', '%PASSWORD%');
mysql_select_db('%DBNAME%');
?>

2. Building the detection script

The first actual fresh code we need to do write is the script which detects the users location – surprisingly this is actually quite simple and straightforward once you have a simple database to query. All the function is going to do is query the database for the closest result and then return the country name.

function detect_location($ip=false) {
 if(!$ip) $ip = $_SERVER['REMOTE_ADDR'];
 $parts = explode('.', $ip);
 $iph = (($parts[0]*256+$parts[1])*256+$parts[2])*256 + $parts[3];
 $result = mysql_query('SELECT country_name FROM `ip_group_country` where `ip_start` <= '.$iph.'order
by ip_start desc limit 1');
 while ($row = mysql_fetch_assoc($result)) {
 return $row['country_name'];
 }
}
?>

So what does all of this do? Well let’s go through it bit by bit:

if(!$ip) $ip = $_SERVER['REMOTE_ADDR'];

This sets the first $ip to the visitors IP if the function receives no parameter

$parts = explode('.', $ip);
$iph = (($parts[0]*256+$parts[1])*256+$parts[2])*256 + $parts[3];

This bit is the most confusing bit, the first line splits the IP into it’s four sections, the second line put’s it into a format usable for the database.

After that the rest is a simple SQL query to get the first result which matches the IP closest and then returns the country name.

3. Displaying results to the user

This is where we actually start using our function and giving it a purpose, we’ll start simple and just display where we think the user is coming from, underneath the PHP block write the following below:

<?php
$country = detect_location();
?>
<h1>Are you from: <?=$country?></h1>

4. Is it wrong?

Now you can visit your file in a browser and see if it guesses your location is correct; it’s wrong? Well if you run the file locally then it will always be wrong because it detects your local IP (127.0.0.1 or ::1). So what can you do? Short of testing it by placing it on a public machine elsewhere we can modify our code to force an IP:

Change $country = detect_location(); to read $country = detect_location('74.125.45.100');.

Testing it again should return “United States” – this is because the IP is one of Googles US servers IP’s. From here on we could just carry on but from seeing that error it exposes a simple problem; as with most programming things it will not be 100% correct every time. So how can we fix this? Well in the unlikely event it is wrong we can give the user some way of changing it. Modify the current code to match that of below and afterwards it will be explained.

<?php
session_start();
mysql_connect(‘localhost’, ‘%USERNAME%’, ‘%PASSWORD%’);
mysql_select_db(‘%DBNAME%’);
function detect_location($ip=false) {
 if(!$ip) $ip = $_SERVER['REMOTE_ADDR'];
 $parts = explode('.', $ip);
 $iph = (($parts[0]*256+$parts[1])*256+$parts[2])*256 + $parts[3];
 $result = mysql_query('SELECT country_name FROM `ip_group_country` where `ip_start` <= '.$iph.'order
by ip_start desc limit 1');
 while ($row = mysql_fetch_assoc($result)) {
 return $row['country_name'];
 }
}
if($_POST && $_POST[‘country’]) {
 $_SESSION[‘country’] = $_POST[‘country’];
}
$country = (isset($_SESSION[‘country’])) ? $_SESSION[‘country’] :
detect_location($_SERVER['REMOTE_ADDR']);
?>
<h1>Are you from: <?=$country?></h1>
<p>Change Region:
<form action="" method="post">
 <select name="country">
 <option value="United Kingdom">United Kingdom</option>
 <option value="France">France</option>
 </select>
 <input type="submit" value="Go" />
</p>

For now there is only the option to change your country to either the UK or France, but it should be easily changeable to list all countries – either by finding the select HTML from somewhere online or plugging it into a database table of all countries

4a – Making sense of it all

As you can tell the size of the code has suddenly doubled, so it’s probably time to inspect what is actually happening in each section to dispel all claims of magic. Each addition is documented below in enough detail for you to understand it’s purpose.

session_start();

This tells the PHP engine that our script will be using sessions so needs to give the visitor a session to use, or retrieve their current session off the filesystem.

if($_POST && $_POST[‘country’]) {
 $_SESSION[‘country’] = $_POST[‘country’];
}

This detects if a new country name has been submitted to the page via POST, if this occurs than the data is saved into a session – if this wasn’t done than the user would have to tell the site where they are for every page request which would be extremely bad usability.

$country = (isset($_SESSION[‘country’])) ? $_SESSION[‘country’] :
detect_location($_SERVER['REMOTE_ADDR']);

To those that have not seen a ternary if statement in PHP this may seem like a daunting piece of code however once explained it’s actually very simple. In PHP as well as a lot of other C based languages you can write single line if/else statements using something called a ternary statement. The syntax is as follows:

(expression) ? true : false

So applying that to our code above gives the explanation; first it checks to see if $_SESSION[‘country’] is set, if it is than $country is set to contain the sessions value, if not than it is retrieved out of the database.

Under the old <h1> there’s a form in which the important things to note is that the action is set to blank so it submits to whatever URL you are on and that the method is set to post. Inside the form there is only a select box which is where the visitor can choose what country they are in.

5. Test, Play, Experiment

After typing all of that above, fire up the page in your favourite browser and test the new drop down out; then make sure it saves when you refresh the page. It works? Finally. Now that this all works you should hopefully have enough basic knowledge about GeoIP to build it into a new project and even change the script to do some even more amazing things.

At only 28 lines of code you can see how simple it is to build such useful functionality.

Online API’s

If you don’t have access to a SQL database or don’t want to host the database locally, IPinfoDB hosts a simply web API for submitting an IP and getting back it’s information in a variety of formats including JSON and XML. The downside of this is it means you are reliant on their servers being online 100% of the time and also that they keep the service available and free. The upside though is that all maintenance is done by them so you don’t have to update your IP tables every month or so. For documentation on this API documentation is available at: http://www.ipinfodb.com/ip_location_api.php The easiest way to use this is with the simple code below:

<?php
 session_start();
 if(!isset($_SESSION['country'])) {
 $file = file_get_contents('http://ipinfodb.com/ip_query.php?ip='.$_SERVER['REMOTE_ADDR'].
'&format=json');
 $data = json_decode($file);
 $_SESSION['country'] = $data['CountryName'];
 }
 $country = $_SESSION['country'];
 echo 'You live in: '.$country;
?>

This also saves the users location in a session so that you don’t have to keep sending requests to the API’s server – this has a number of advantages; speeding up your page loads, lowering bandwidth usage and decreasing the chance of ever going over any fair usage policy.

HTML5 Geolocation API

One of the recent additions to the HTML5 specification is a new javascript API for retrieving geographical information about the visitor, this includes the ability to gain coordinates for the visitor but also to watch the visitors location for changes. Currently it is already supported in Firefox 3.5 as well as a similar available for the Google Gears API. Being able to retrieve more precise information from the visitor (especially on the client-side) could lead to some interesting uses and web applications. More information and documentation on it’s features can be found at: http://www.w3.org/TR/geolocation-API/

Although there aren’t a lot of public examples of this new HTML5 API, it’s only time before people start creating games played on smartphones which interact with each others position or some other imaginative use.

Sites Using GeoIP

The newly launched irregular choice store uses GeoIP to display localised stock lists and prices at http://shop.irregularchoice.com

Resources

If your using Lighttpd then you can use mod_geoip to handle all the logic for you enabling you to focus on what you do with a visitors location. You can find a tutorial at: http://www.cyberciti.biz/tips/linux-lighttpd-install-mod_geoip-tutorial.html