At lunch today I took a quick look at how Safari on the iPhone handles Geolocation. If you have an iPhone (you know, that device that doesn't play Flash?) then you've probably seen applications that ask permission to locate your position. The hardware supports it both for applications but web pages as well. I was curious to see a) how difficult it would be to use and b) if it could degrade nicely for other clients.

So to begin, I found an excellent blog post that demonstrated a simple example of the code: How to Use Geolocation in Mobile Safari.

His code sample was simple and to the point:

navigator.geolocation.getCurrentPosition(foundLocation, noLocation);

function foundLocation(position) { var lat = position.coords.latitude; var long = position.coords.longitude; alert('Found location: ' + lat + ', ' + long); } function noLocation() { alert('Could not find location'); }

Basically - run a getCurrentPosition and pass it a pass/fail operator. I tested this just to ensure it worked and it did perfectly. Using that as a base I began to work on a simple application.

My idea was simple - I'd tie onto Yahoo's Local Search API. I'd have a form where you could enter "pizza", and because we knew your longitude/latitude, we could pass that to Yahoo. To support browsers/clients that couldn't use this, I'd use jQuery to simply ask the user for their location. This works because Yahoo's APIs supports both a precise location, or a precise street/city/state, as well as a loose "location" argument. Kudos to Yahoo for being so flexible.

Before I go further - one thing I don't like about JavaScript based blog entries is that it's sometimes difficult to explain the process. The code I'm going to show was done over multiple iterations, but since I didn't save each and every edit, your only going to see the final result. Do know that I certainly didn't write it out like this - it took more than one try to get it right.

That being said - let's begin with the view area:

<form id="searchForm"> Search for: <input type="text" id="term" value="pizza"> </form> <input type="button" id="searchBtn" value="Search" disabled="true">

<div id="results"></div>

Nothing too fancy here. You can see a form with a search field, a button (I'll explain why I put it outside the form in a PS), and a div for my results. Now let's go up to my JavaScript.

<script> var lat = ""; var long = "";

function foundLocation(position) { lat = position.coords.latitude long = position.coords.longitude $("#searchBtn").attr("disabled", false) $("#results").html("") }

function noLocation() { $("#results").html("") $("#searchBtn").attr("disabled", false) $("#searchForm").append(" Location: <input type='text' id='location'>") }

$(document).ready(function() { if(navigator.geolocation == null) noLocation() else { $("#results").html("<i>Locating you...</i>") navigator.geolocation.getCurrentPosition(foundLocation, noLocation) }

$("#searchBtn").click(function() { $("#results").html("<i>Searching...</i>") var theURL = "http://local.yahooapis.com/LocalSearchService/V3/localSearch?callback=?" var data = { appid:"5rJLaITV34GaU8t05tz.MIYQfRol9PBL51PyK3KAhmRiFa0FEAtvFyD36r4xTns_VRP1", output:"json", query:$("#term").val() } if(lat != "" && long != "") { data.latitude = lat data.longitude = long } else { data.location = $("#location").val() }

$.getJSON(theURL,data, function(d,s) { var result = "" for(var i=0; i<d.ResultSet.Result.length;i++) { var node = d.ResultSet.Result[i] result += "<p><b>" + node.Title + "</b><br/>" result += "<a href='" + node.ClickUrl + "'>"+node.Url+"</a><br/>" result += node.Address + ", "+node.City+", "+node.State+"</p>" } $("#results").html(result) }) }) })

</script>

Ok, there's a lot going on here. Let's focus first on the document.ready block. I begin by checking to see if the navigator.geolocation object exists. If it doesn't, it means we are on a browser where we can't get the user's location. That runs the noLocation function. It enables the search button for my form (which I disabled while I was working) and adds a field for the user's location.

If the geolocation object exists, we fire off the request. Now notice - if for some reason the lookup fails, we still run the noLocation function. So we've got support for both non-supported clients and the user saying no to the location request. (Or some other error happening.) If the location works right, we have access to the user's longitude and latitude. We store those values and enable the button. (Notice I made use of the results area to tell the user I was looking up his location.)

Ok, so the final bit of code handles the Yahoo API stuff. I've got a click event listener for the search button. It can tell if it currently knows the long/lat value and will use it if so. Otherwise it uses the freeform location field. I construct my data and fire it off to Yahoo. The result handler simply outputs the result.

You can demo this here: http://www.coldfusionjedi.com/demos/feb1210/test4b.cfm

I tested it in Chrome, my iPhone Simulator (which uses a California location) and my actual iPhone, and it seems to work well.

p.s. So why did I put the button outside of the form? Probably a dumb reason, but I had trouble getting jQuery to add my location field before the search button. When I did a prepend, it just didn't show up. When I append to the search term field, it didn't work. When I appended to the form tag - it worked perfectly - but added it after the search button. Probably something silly I did wrong there - but I went with a quick fix.