Geolocation: Easier Than It Looks
Have you ever wanted to add location-aware content to your web applications? Would you believe me if I told you it was dead easy, and you could be up and running in about 10 minutes?
The first thing you want to do is use someone else’s work. Geolocation is a solved problem; there’s no need to roll your own. I went searching for free Geolocation APIs and found two I wanted to try: MaxMind’s GeoLite API and Quova.
GeoLite
GeoLite is somewhat unique in that their service is free, it doesn’t require registration, and they provide their database for download. That means you host the data on your own site and use their PHP library to make calls to the database.
Here’s how you get GeoLite up and running in your web application.
- Download the GeoLiteCity database
- Uncompress the database to a folder that’s readable by your web application
- Install the PEAR GeoLite library (
pear install Net_GeoIP
) - Code!
The code you’ll need to write is ridiculously minimal. Here’s how I tested my installation.
$geoip = Net_GeoIP::getInstance(dirname(__FILE__) . '/data/GeoLiteCity.dat'); $ipaddress = '72.30.2.43'; // Yahoo! $location = $geoip->lookupLocation($ipaddress); var_dump($location);
And here’s the output.
object(Net_GeoIP_Location)[2] protected 'aData' => array 'countryCode' => string 'US' (length=2) 'countryCode3' => string 'USA' (length=3) 'countryName' => string 'United States' (length=13) 'region' => string 'CA' (length=2) 'city' => string 'Sunnyvale' (length=9) 'postalCode' => string '94089' (length=5) 'latitude' => float 37.4249 'longitude' => float -122.0074 'areaCode' => int 408 'dmaCode' => float 807
The Net_GeoIP_Location
object makes use of the __get()
and __set()
magic methods, so retrieving the data from the object is as simple as writing $location->city;
.
Quova
Since using GeoLite was so darned easy, I decided to try my hand at another geolocation API. I decided to check out Quova’s offering to see how it compared.
After opening a free developer account and getting my API key, I went to work. Quova’s documentation is excellent, and I was up and running in short order. The PHP example on their site is kind of ugly, in my opinion, so I spent 5 or 10 minutes putting a class together to make the code a little prettier.
Here’s what I came up with.
/** * Quova ipinfo API class * * @category Example * @package Example_Quova * @subpackage GeoIP * @version $Id$ */ /** * Uses Quova's GeoIP API to get geographical location by IP address * * To obtain your Quova API key (apikey) and the shared secret * that you need to build a digital signature, register your * application at http://developer.quova.com/. * * @category Example * @package Example_Quova * @subpackage GeoIP */ class Example_Quova_GeoIP { /** * Quova API key * * @var string */ private $_apiKey; /** * Quova shared secret * * @var string */ private $_secret; /** * Default URL for Quova's GeoIP service * * @var string */ private $_defaultService = 'http://api.quova.com/v1/ipinfo/'; /** * Public constructor * * @param string $apiKey Quova API Key * @param string $secret Quova shared secret */ public function __construct($apiKey, $secret) { $this->_apiKey = $apiKey; $this->_secret = $secret; } /** * Get geographical location of IP address from Quova's API * * @param string $ipaddress * @param string $format json or XML * @return string XML or json, depending on the value of $format */ public function getLocation($ipaddress, $format = 'json') { $ch = curl_init(); $parameters = array( 'apikey' => $this->_apiKey, 'sig' => $this->generateSig(), 'format' => $format ); $url = $this->_defaultService . $ipaddress . '?' . http_build_query($parameters); curl_setopt($ch, CURLOPT_URL, $url); curl_setopt($ch, CURLOPT_HTTPAUTH, CURLAUTH_ANY); curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false); curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1); $data = curl_exec($ch); $headers = curl_getinfo($ch); // Check headers here curl_close($ch); return $data; } /** * Checks headers for response code and issues error message if necessary * * @param mixed $headers */ public function checkHeaders($headers) { // this is where I'd test HTTP response codes } /** * Generates sig, an MD5 hash of the API key, the shared secret, and Unix timestamp * * @return string MD5 hash */ public function generateSig() { return md5($this->_apiKey . $this->_secret . gmdate('U')); } }
Querying the Quova API is then as simple as this:
$apikey = 'dummyApiKey'; $secret = 'dummySecret'; $ipaddress = '72.30.2.43'; // Yahoo! $quova = new Example_Quova_GeoIp($apikey, $secret); $location = json_decode($quova->getLocation($ipaddress)); var_dump($location);
And here’s the output:
object(stdClass)[4] public 'ipinfo' => object(stdClass)[5] public 'ip_address' => string '72.30.2.43' (length=10) public 'ip_type' => string 'Mapped' (length=6) public 'Network' => object(stdClass)[6] public 'organization' => string 'inktomi corporation' (length=19) public 'OrganizationData' => object(stdClass)[7] public 'organization_type' => string 'Business Conglomerate' (length=21) public 'carrier' => string 'inktomi corporation' (length=19) public 'asn' => int 14777 public 'connection_type' => string 'tx' (length=2) public 'line_speed' => string 'high' (length=4) public 'ip_routing_type' => string 'fixed' (length=5) public 'Domain' => object(stdClass)[8] public 'tld' => string 'com' (length=3) public 'sld' => string 'yahoo' (length=5) public 'Location' => object(stdClass)[9] public 'continent' => string 'north america' (length=13) public 'latitude' => float 37.33053 public 'longitude' => float -121.83823 public 'CountryData' => object(stdClass)[10] public 'country' => string 'united states' (length=13) public 'country_code' => string 'us' (length=2) public 'country_cf' => int 99 public 'region' => string 'southwest' (length=9) public 'StateData' => object(stdClass)[11] public 'state' => string 'california' (length=10) public 'state_code' => string 'ca' (length=2) public 'state_cf' => int 94 public 'dma' => int 807 public 'msa' => int 41940 public 'CityData' => object(stdClass)[12] public 'city' => string 'san jose' (length=8) public 'postal_code' => string '95122' (length=5) public 'time_zone' => int -8 public 'area_code' => string '408' (length=3) public 'city_cf' => int 90
Calling $location->ipinfo->Location->StateData->state_code;
gets you the two letter state code. Nice!
Gotchas
As far as gotchas go, there aren’t too many. The big thing to look out for is accuracy. Free geolocation datasets generally promise accuracy withing a range of about 25 miles, and not all datasets match. You’ll notice above that GeoCityLite returns a different city and postal code than Quova for the same IP address. When I tested my own IP address at work (Southaven, MS), GeoCityLite told me the IP address was in Collierville, TN, while Quova said it was in Memphis, TN. Both cities are well within the 25 mile accuracy radius, but neither are in the state of Mississippi. Go figure.
Wrapping Up
Want to add geolocation features to your web application? If you have about 10 minutes to spare, you can. Follow the examples above, and you’ll be up and running in no time. The code you’ll have to write will be minimal, and the return on invested time will be well worth it.
Leave a comment
Use the form below to leave a comment:
Responses and Pingbacks
November 7th, 2011 at 8:45 pm
You forgot about Yahoo! Placefinder. I just used it in a project and was up and running in a few minutes. From the TOS it’s free for any project up to 50,000 requests a day.
November 7th, 2011 at 8:49 pm
Yahoo! Placefinder looks awesome, but it’s a tool for a different purpose. Where Yahoo! Placefinder does geocoding (addresses to coordinates and back), I was looking specifically at geolocation (IP address to geographical address).
Unless I’m missing something . . .
November 8th, 2011 at 1:20 am
Yup, you’re right. I skimmed through the article the when I made my post (I know, shame on me).
November 8th, 2011 at 6:06 pm
You can also use YQL (Yahoo Query Language)
Example :
$yql_query = “use ‘http://www.datatables.org/misc/geoip/pidgets.geoip.xml’ as location
select * from location where ip=\”” . $ip . “\””;
$results = $this->query($yql_query);
if (isset($results->Result))
{
$arLocation = array(‘country_code’ => $results->Result->country_code,
‘country_name’ => $results->Result->country_name,
‘region’ => $results->Result->region,
‘city’ => $results->Result->city,
‘zip’ => $results->Result->postal_code,
‘latitude’ => $results->Result->latitude,
‘longitude’ => $results->Result->longitude
);
}
return $arLocation;
November 8th, 2011 at 8:42 pm
[…] Szerző: admin – 2011. november 8. kedd Geolocation: Easier Than It Looks Kategória: php | A közvetlen link. ← […]
November 28th, 2011 at 3:47 pm
Also now there is : http://geocoder-php.org/
February 24th, 2013 at 6:53 am
Do the geolocation apis work also for applocation running on a machine behind the proxy?