Skip to: Site menu | Main content

Custom GMap Solution for Automatically Centering on User Location (Part 3)

In my previous two posts, I discussed how I implemented dynamic markers and the PdMarker custom marker type into the home page for OffRoadAtlas.com. The final major GMap customization I added was the ability of the map to automatically center and zoom on the user's location based on their IP address. The solution isn't always perfect (depending on the user's internet access provider), but more often than not, it gets pretty close.

The tricky part of implementing this feature was being able provide it for anonymous users while Drupal caching mechanism was activated. Later in this post, I'll show you how I did it using a combination of hook_init(), cookies, and Javascript.

I started off with the GeoUser module - this had all of the required operability with MaxMind's GeoLite City database. This 25MB free version of their IP-to-location database is updated monthly and provides "good enough" accuracy for this type of application. The GeoUser module does a great job of providing an interface to the GeoLite City API. Kudos to zidong.c, the developer of the GeoUser module.

The GeoUser module has the ability to look up the user's location whenever they login, then display the location on the user's profile page. This was not exactly the output I was looking for. I was able to thin out much of the GeoUser module and leave just the one bit of functionality I was looking for: given an IP address, return the latitude and longitude. In the GeoUser module, that is accomplished via this single function call:

$thelocation = GeoUser::getGeoCity(ip_address());
$lat = $thelocation->latitude;
$lon = $thelocation->longitude;

The ip_address() function is actually a Drupal 6 function, but I was able to backport it to Drupal 5 and include it as part of my modified GeoUser module. The function simply provides the IP address of the client machine, taking reverse proxy (I'd pretend to say that I knew what "reverse proxy" is, but I'm afraid I wouldn't be able to pull it off) into account.

Now that I had the latitude and longitude, I now had to somehow get it to the home page map without it getting cached. To do this, I took advantage of hook_init() and cookies. Knowing that hook_init() gets called even when caching is turned on ("aggresive" caching skips hook_init()), I created the following function:

function geouser_init() {
if (isset($_COOKIE['geouser_latlon'])) {
return;
}
else {
require_once('GeoIP.php');
$geo = new Net_GeoIP(variable_get('geouser_db_path','sites/all/modules/geouser/IPDatabase/GeoLiteCity.dat'));

$thelocation = GeoUser::getGeoCity(ip_address());
setcookie('geouser_latlon',
$thelocation->latitude.':'.$thelocation->longitude,
time()+60*60*1, // set the cookie to expire in one hour
'/'
);
}
}

This function looks to see if the user had a "geouser_latlon" cookie already set, if it does, it exits. If not, it calls the necessary functions to grab the lat/lon based on IP address and stuffs them into a cookie.

Then, when the map is generated, the following Javascript code grabs the cookie, then sets the map object to the latitude and longitude contained in the cookie (the lat/lon is in a single colon-delimited variable, hence the call to the split() function):

var geouser_latlon = readCookie('geouser_latlon');
if (geouser_latlon.length > 4) {
var aLatLon = geouser_latlon.split('%3A');
obj.vars.latitude = aLatLon[0];
obj.vars.longitude = aLatLon[1];
obj.vars.zoom = 8;
}

In the preceeding code, the "obj" is the GMap. If you'd like to implement this type of functionality, you'll most likely have to modify the Javascript code to account for how and when the cookie gets read and the map gets re-centered. I was able to add this bit of code to the "gmap_dynamic" Javascript code (as part of the gmap handler) that I discussed in my previous two posts. If you do implement it, let me know, I'd love to check it out.

Submitted by michael on Sun, 08/31/2008 - 10:33am
Filed under: