Getting the Zip Code from an HTML5 GeoLocation API Call June 13th, 2010
2 commentsThe HTML5 Geolocation API is built into newer browsers and returns the approximate latitude and longitude of the device in JSON format.
Latitude and longitude is great for maps, but sometimes you need additional information, like an address or zip code. Unfortunately the native Geolocation API does not provide this functionality, but luckily Google’s Geocoding API does. Google’s Geocoding API has the ability to do what’s called reverse geocoding. You give it latitude and longitude and it gives you nearby addresses in a nice neat JSON package. Here’s an example of the JSON returned by a reverse geocode lookup. As you can see, it contains a lot of information about addresses around that area. However for this particular article we are only interested in the Zip Code of the first result.
One caveat of using Google’s Geocoding API in JavaScript is that there is no way to add a callback to the end of the request. This rules out the cross-domain AJAX method of dynamic script tag embedding as a method of retrieving the data. Currently the easiest way to achieve cross-domain AJAX calls is by using a proxy. A proxy is a server-side script that resides on the same domain as your client script and acts as a middle man between the two domains. Your script makes an ajax call to the proxy, the proxy retrieves the data from the other domain and returns it back to your script.
Heres my proxy.php file
<?php $path = ($_POST['path']) ? $_POST['path'] : $_GET['path']; $url = $path; // Open the Curl session $session = curl_init($url); // If it's a POST, put the POST data in the body if ($_POST['path']) { $postvars = ''; while ($element = current($_POST)) { $postvars .= urlencode(key($_POST)).'='.urlencode($element).'&'; next($_POST); } curl_setopt ($session, CURLOPT_POST, true); curl_setopt ($session, CURLOPT_POSTFIELDS, $postvars); } // Don't return HTTP headers. Do return the contents of the call curl_setopt($session, CURLOPT_HEADER, false); curl_setopt($session, CURLOPT_RETURNTRANSFER, true); // Make the call $xml = curl_exec($session); // does the service return XML or JSON? Set the Content-Type appropriately $headerType = ($_POST['type']) ? $_POST['type'] : $_GET['type']; // 'text/xml' or 'application/json' $headerType = ($headerType) ? $headerType : 'text/xml';//default header("Content-Type: " . $headerType); echo $xml; curl_close($session); ?>
Here’s the JavaScript. Notice I’m using jQuery.
$(function(){ var GETZIP = { getLocation: function(){ $('#status').text('searching...'); if(navigator.geolocation){ navigator.geolocation.getCurrentPosition(GETZIP.getZipCode, GETZIP.error, {timeout: 7000});//cache it for 10 minutes }else{ GETZIP.error('Geo location not supported'); } }, index: 0, error: function(msg) { if(msg.code){ //this is a geolocation error switch(msg.code){ case 1: $("#status").text('Permission Denied').fadeOut().fadeIn(); break; case 2: $("#status").text('Position Unavailable').fadeOut().fadeIn(); break; case 3: GETZIP.index++; $("#status").text('Timeout... Trying again (' + GETZIP.index + ')').fadeOut().fadeIn(); navigator.geolocation.getCurrentPosition(GETZIP.getZipCode, GETZIP.error, {timeout: 7000}); break; default: //nothing } }else{ //this is a text error $('#error').text(msg).addClass('failed'); } }, getZipCode: function(position){ var position = position.coords.latitude + "," + position.coords.longitude; $.getJSON('proxy.php',{ path : "http://maps.google.com/maps/api/geocode/json?latlng="+position+"&sensor=false", type: "application/json" }, function(json){ //Find the zip code of the first result if(!(json.status == "OK")){ GETZIP.error('Zip Code not Found'); return; } var found = false; $(json.results[0].address_components).each(function(i, el){ if($.inArray("postal_code", el.types) > -1){ $("#status").text('Your Zip Code: ' + el.short_name); found = true; return; } }); if(!found){ GETZIP.error('Zip Code not Found'); } }); } } GETZIP.getLocation(); });
A note on Safari 5: It seems that the current implementation of geolocation is a bit buggy. Sometimes it returns results almost immediately, other times it takes more than 10 seconds, but more often than not it runs for eternity without success. Firefox, on the other hand, seems pretty stable.
Soon! Firefox supports addresses as part of the geolocation position object. I think chrome also does the same. Check out the demo here:
http://people.mozilla.org/~dougt/demos/geo.html
Interesting.
Firebug is having a really hard time outputting the position object so i was unable to see that information being brought back. Console.log() outputs it as an XPconnect wrapped object and won’t let me inspect it.