Getting the Zip Code from an HTML5 GeoLocation API Call Travis June 13th, 2010

2 comments
Filed under: HTML5,javascript |

The HTML5 Geolocation API is built into newer browsers and returns the approximate latitude and longitude of the device in JSON format.

View Demo

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.

2 comments Add Yours »

  1. Doug Turner June 13th, 2010 | 11:23 pm

    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

  2. Travis June 13th, 2010 | 11:51 pm

    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.

RSS feed for comments on this post. TrackBack URL

Leave a comment