Accessing the GPS in iPhone Safari
Finally with the release of the 3.0 firmware update to the iPhone we now have access to the GPS coordinates in Safari. Using the W3C Geolocation API we can access the users position much the same way a native app would. The user can either allow or disallow the current websites’ access to your location.
This demo will need the iPhone 3G with the 3.0 firmware update or Firefox 3.5 in order to work. The iPhone will give the better results as it has access to the GPS where as Firefox depends on the hardware available.
Some hurdles
You may have noticed that I’m not using Google or Yahoo Maps APIs to plot my location, this is due to both having restrictions on using it in conjunction with real time locations. In Google Maps Terms of Use.
use the Service or Content with any products, systems, or applications for or in connection with (i) real time navigation or route guidance, including but not limited to turn-by-turn route guidance that is synchronized to the position of a user’s sensor-enabled device; or (ii) any systems or functions for automatic or autonomous control of vehicle behaviour.
Yahoo’s terms are a bit different they still restrict use of real time location abilities to be no younger than 6 hours, essentially making the safari’s location awareness a no go zone for these 2 services.
…use the Yahoo! Maps APIs with location information that is less than 6 hours old and derived from a GPS device or any other location sensing device;
CloudMade to the rescue
Thanks to CloudMade who is putting a rock solid set of tools and API’s in various languages so we can access the OpenStreetMap map data. Only difference is that these maps are created by the community and are licensed under the Creative Commons Attribution/ShareAlike licence, version 2 which means there are no such restrictions that we would of had with Google or Yahoo maps. I highly recommend you read where they are heading in terms of their licensing and help in giving back to their community by creating maps.
How it works
Now we have a maps API where we won’t be abusing the terms of use by using it in conjunction with real time locations. We’ll get stuck into how to use the W3C Geolocation API.
Request the users location
navigator.geolocation.getCurrentPosition(success, fail);
The above line will try to obtain the users current location using the getCurrentPosition method this can be passed up to 3 arguments; the success callback function, the error callback function and the position options: enableHighAcurracy, timeout & maximumAge.
Upon successfully obtaining the users location the success function will get passed the position object which will contain the coords and timestamp attributes. The coords attribute is the one we need to extract information about the user location and has the following properties available:
- latitude
- longitude
- altitude
- accuracy
- altitudeAccuracy
- heading
- speed
navigator.geolocation.getCurrentPosition(success, fail); // success callback, gets passed position object success(position) // 'position' can be named anything { alert("Your latitude: " + position.coords.latitude + "longitude: " + position.coords.longitude); }
Not all of the geolocation information in the coords attribute is available to the iPhone, heading and speed currently return null but I suspect that both will be functional in the iPhone 3G S thanks to the compass and coreLocation updates. Both these attributes still return null on the iPhone 3GS which is surprising. Speed can alternatively be calculated by using the Haversine formula to obtain the distance travelled between two points which can then be used to compare time and distance to make a pretty accurate speed approximation.
The options available as the 3rd argument that can be passed to the getCurrentPosition method are:
- enableHighAccuracy
- timeout
- maximumAge
These options can help in forcing the error callback and having a fallback option if the request cannot be obtained before the timeout ends or the location wasn’t obtained within the maximumAge set. enableHighAccuracy will force the GPS to get the most accurate results possible this will take longer and chew through the battery.
navigator.geolocation.getCurrentPosition(success, fail,{maximumAge: 300000, timeout:0});
Both maximumAge and timeout are expressed in milliseconds. This is basically saying get the last known location that is no older than 5 minutes, if no position is found timeout immediately and fire the error callback function.
Updating location
Getting the users initial location is great and all but what we really need is a way to get constant updates of any changes in the users’ location. Thankfully the geolocation API also has the watchPosition method which will fire upon a change in the users’ location.
var updateLocation = navigator.geolocation.watchPosition(success, fail);
This, like the getCurrentPosition method, takes the same arguments the only difference above is that I have stored the watchPosition method in a variable, this is so we can cancel it at anytime if we no longer need to watch for any changes in position.
navigator.geolocation.clearWatch(updateLocation);
This will cancel the watchPosition and stop it from firing the success or error callback. It works in much the same way the clearTimeout method works for the setTimeout method.
Handling errors
If something goes wrong with obtaining the users location e.g. the user decides not to share their location to your web app. We have the error callback so we can handle the situations that might occur and present useful actions or information back to the user.
The PositionError interface has 4 possible scenarios:
- UNKNOWN_ERROR (code 0)
- PERMISSION_DENIED (code 1)
- POSITION_UNAVAILABLE (code 2)
- TIMEOUT (code 3)
We have 2 attributes on the error callback available which are code & message these will return either the code number 0-3 or the message.
navigator.geolocation.getCurrentPosition(success, fail); // fail callback, gets passed error object fail(error) // 'error' can be named anything { switch(error.code) // Returns 0-3 { case 0: // Unknown error alert error message alert(error.message); break; case 1: // Permission denied alert error message alert(error.message); break; ... etc } }
Just the beginning
The iPhone 3.0 release isn’t the only browser to support Geolocation API, currently Firefox 3.5, Opera 10 beta and Opera Mobile 9.7 beta though this is speculation. I’m sure the other browser vendors will follow in their new releases.
Post filed under: javascript.
Comments
Trackbacks
-
[...] Finally with the release of the 3.0 firmware update to the iPhone we now have access to the GPS coordinates in Safari. Using the W3C Geolocation API we can access the users position much the same way a native app would. The user can either allow or disallow the current websites’ access to your location. See more here: Accessing the GPS in iPhone Safari [...]
-
[...] [...]
-
[...] dreckige Reichtum Kanadas (hitec, 03/08/09) • Die Sendung « Xenius Blog • Accessing the GPS in iPhone Safari | The CSS Ninja – All things CSS, Javascript & xhtml • iPhone and iPod Touch development – WebApp.Net • halbluchs: Auf dem [...]
-
[...] Accessing the GPS in iPhone Safari | The CSS Ninja – All things CSS, Javascript & xhtml (tags: location howto firefox geolocation mobile maps iphone) [...]
I’m glad you’re enthusiastic about web development for mobile safari. We’re also really excited about the potential for safari and other mobile browsers. CloudMade’s Web Maps Lite library has some basic support for iPhone safari – we’re going to expand support in the near future. Please drop me an email if you’d like more info, or sign up to our developer list at lists.cloudmade.com
Nick, July 12th, 2009Thanks for the sample code. We are planning on using this on one of our sites (ataxi.ro to show more relevant directory information to our customers.
-Daniel
Daniel Chvatik, July 19th, 2009Awesome, glad you found it useful.
The Css Ninja, July 20th, 2009Thanks for the code! I’m using it now to develop a web app and it worked on the first go.
After adding some other code to it, though, something went wrong and now when I try to access geolocation I get the code 1 error (User disallowed GeoLocation). Since then, I fixed my code and it works on other iphone’s and on my ipod touch…but still errors on my own iphone.
I’ve reset everything on my iphone, did a complete restore, etc. and it still blocks me. Looks like it just blacklisted the domain my web app is on or something like that.
Any suggestions how to fix this? Not finding much about it online.
Thanks!
katrina, July 25th, 2009You’re welcome.
If you go into settings > general > reset there will be an option in there to “Reset Location Warnings”. I haven’t yet tried it myself but from what people have said that fixes the problem you are having.
The Css Ninja, July 25th, 2009Thanks, this helps alot! Im having one issue though!
I deliver pizzas for dominos, and wrote an app that let you record tips for the iphone. i now have it so it records the lat and lon when you add the tip, and at the end of the day it exports the data to google maps for a tip map sorta deal. every time you add a tip, it calls
The app adds the data to the database very quickly, and seems to be only using the cell tower location, and not be waiting for the actual gps results. What can i do to force it to wait for more accurate results?
Andrew Carter, August 8th, 2009oh, the actual call I’m using is:
Great idea. As for getting the most accurate results, I would check the accuracy value in the coords attribute that will return how accurate it is in metres you could also check the timestamp attribute to see how old the position is in unix time. Although your example is looking for a position that is no older than 30 seconds so the timestamp should be in between that.
Hope that helps. Let me know how you go.
The Css Ninja, August 9th, 2009Great stuff. One question, how do you force the iPhone/pod to update the location info? Every time I launch my app I get my last location, not the current one. The only way I found to refresh the data is to load the Google Maps (or Google Earth) app and have it get my location, then the location updates. In my code I have maximumage set to zero, I would think that would force a location update, it doesn’t. I have tried my app on two different iPhones and a Touch, same results. It seems to always return the cached position info. What am I missing??
Doug R., August 13th, 2009I’m not entirely sure without seeing the JavaScript you’re doing, if you set the timeout option to 0 and in your error callback see what error message is getting displayed that should be able to help you. If you attach a watchPosition it should only fire on getting a fresh position, just add an alert in there so you know if your GPS is working or not.
Also if you load up the demo and go for a walk or drive the watchPosition will update the map marker when the GPS gets fresh coordinates.
The Css Ninja, August 13th, 2009hey! Thanks for this page
I have a weird problem tho:
I tried to deny the access to my iphone’s GPS for my own website… well, now I cant get it back!
I don’t have this problem with your live demo. If I deny it once, I just have to reload the page, and it works.
Actually, I tried your code on my server… and it doesn’t work neither! Looks like my safari banned my IP for ever
I reset my phone settings, etc… nothing works
help me! ^_^’
yvan, August 17th, 2009Have you reset your location warnings as mentioned above? I tested that on my own demo. Denied the location request and then reset my location warnings. When I reloaded the page it asked me again to allow my location but it kept failing and falling back to the error handler (showing Apple HQ on the map). For some reason resetting my location took quite a while for it to flow back to safari and start working again. But it does work.
The Css Ninja, August 17th, 2009I’ve reset everything, again and again… I also did a complete restore..
I tried all kind of voodoo stuff but nothing works. Seems that Safari put my server on a GPS black list for ever
yvan, August 17th, 2009Hmm that’s kind of odd, so it only happens on your server? If you visit my demo and deny access then reset your location warnings it starts working again?
The Css Ninja, August 17th, 2009Yes, for some reason, safari blacklisted my server only…
I could eventually “solve” this problem : i had to restore the phone without using a saved configuration ( I lost all my contacts, pictures, and so on).
So, be careful when you deny access to a website!
yvan, August 17th, 2009I posted a comment here back in July reporting the same problem with my iPhone blocking my server/domain after I denied it geolocation. Even after resetting location warnings, it wouldn’t work.
The way I eventually got it to work was by restoring my phone back to a time before I initially denied location access for my site. This way I didn’t have to lose all my contacts, etc. After it was restored, it still blocked me. But I went in and reset location warnings again and it worked.
I have no clue why I’d have to reset location warnings after doing a restore. Seems strange to me. But that worked.
Hope this helps others so they don’t risk doing a complete restore and losing all their data!
katrina, August 18th, 2009The licensing agreement from Google does not preclude the use of GPS, no? It just mentions the use of such technology in a turn-by-turn scenario.
Olivier, September 15th, 2009Thoughts?
It mentions turn by turn as an example but from their restrictions section:
It’s not limited to turn by turn. I think it is better to be aware that these restrictions are in place before you were to use Google maps in such a way that it would violate their terms & conditions.
The Css Ninja, September 15th, 2009This works great. I was curious if you knew if there was a way to return the zip code closest to the GPS location?
This would help me greatly hwoever, I have yet to figure out how to do it.
Brian, September 17th, 2009It’s currently not available in the geolocation API although Mozilla has added the address attribute to the position object in FF3.7 (minefield) for friendly name results from lat/lng.
Cloudmade still haven’t released their reverse geocoding webservice so until then you could do a call to Google’s reverse geocoder that’ll return the closest address to the lat/lng provided and from there you could extract the zip/postal code from the address object.
The Css Ninja, September 17th, 2009Thanks for the info. Very helpful. I am now hitting an issue where I am trying to return only the zip code but it seems to keep pulling the full address up in a Google Map. I’m trying to figure out what I might be missing. Is there a special way to pass the lat & lon to get a Zip code returned?
Brian, September 17th, 2009You’re welcome. Yeah the address object will always return the full address so you’ll have to extract out the zip/postal code with a regexp, something like this should work:
Basically it will match any number that is 5 digits long. Hope that helps.
The Css Ninja, September 18th, 2009I have also used the geolocation api on the iphone with success. One thing I was wondering though.
I use the watchPosition and have that callback to a function that inserts the location in a database. Later when I call clearWatch() I want to show the route on GMaps. This works just fine. But if I am tracking my route and the phone automatically locks my screen (which I have set it to do after 3 minutes), the GPS stops making the callbacks until I unlock the screen on the iPhone again.
Is there a way in the Iphone Geolocation API to force it to continue monitoring the GPS even while the screen is locked? If that is not possible is it then possible to prevent the iphone from locking the screen through javascript when my app is running?
Flemming, September 22nd, 2009@Flemming – Not that I am aware of, at least not with JavaScript. The Phone has a timer that will send the phone to sleep mode if it doesn’t detect any touches after a certain amount of time. I do have a crazy theory that hasn’t been tested nor do I guarantee it will work at all but it’s worth a try.
If you setup a setInterval timer to set focus to a hidden checkbox every 10 seconds that could fool it into thinking it is being “touched”, it’s worth a try. Let me know how you go.
The Css Ninja, September 23rd, 2009The geolocation api works fine for me, but there is a big problem.
The API does NOT use GPS to get my position. The accuracy is never below 50m. There also is no altitude available. This accuracy can be calculated using WLAN Stations or Cell Phone Transceivers.
If I start the “build in map” an locate my position, GPS is working. The accuracy is < 20m.
If I switch to my webapp very quickly after using the built in map the accuracy is ok and I also get the altitude. But after 10 seconds I have the bad accuracy again.
At my iPhone the enableHighaccuracy option is definitly ignored.
No GPS from javascript!!!
bernhard, September 26th, 2009I have tested the demo while on the move and it updates my location pretty accurately although I am not ouputing the accuracy attribute so I don’t know the exact accuracy figure.
I did create a bookmarklet that will alert you current lat/lng/accuracy. Find my location.
The geolocation spec is not purely about using a gps to get your location it uses the best available information it has to work with, whether that be gps, cell towers or ip address.
The Css Ninja, September 27th, 2009I use the geolocation API on my iphone (3G). If I’m inside a building I won’t be getting locations more accurate than about 2-300m – and most times even much worse that – usually when I test my app indoors I get accuracy of about 1800m :-/
However, as soon as I’m outside and have clear line of sight to the sky I don’t have problems getting accuracy of about 14m and sometimes as low as 7m.
I too have set the highAccuracy option to true, but I’m not sure it does much more than poll more often. What is important though is setting the maximumAge property to a very low value – otherwise the phones api may return cached coordinates rather than actually polling the gps in the phone. This will probably also increase battery consumption.
Flemming, September 28th, 2009Seems to work pretty well. This is a great intro to the geolocation API, thanks Ninja!
I’m working with a 3GS, and I’m getting values for heading and speed, but only while actually moving. Anyone know what the units are for those? While traveling on the freeway around 65mph, speed returned 29.220444444444446 and heading returned 358 (I’m assuming that’s degrees, so I was heading almost due north).
I’ve linked to a screenshot.
Jeff, September 30th, 2009Oh! I’ve answered my own question. Speed returns meters per second.
Jeff, September 30th, 2009@Jeff – Thanks for your comment Jeff.
Interesting all my testing I did I always got null for speed and heading must only register those values once you are at a certain speed. Do you get heading values when you’re stationary?
The Css Ninja, September 30th, 2009No I haven’t gotten heading values while stationary, but I have gotten 0 for speed while sitting in the office.
Altitude also seems to like movement. I’m close to sea level, and while the actual altitude value comes back at what I’m assuming is pretty close, I don’t think I’ve seen altitudeAccuracy get below 70 meters. That’s a big range.
…and @Flemming: you can set your iPhone to never auto lock in Settings>General>Auto Lock, however, if you are synching with an Exchange server your sysadmin can disable the never option. If you turn off your Exchange account, you’ll see the option for never appear.
Jeff, October 1st, 2009@Jeff – When I wrote this article I was using the 3.0 beta firmware perhaps they have since updated it to return values for speed and heading? I would imagine you would need the 3GS for heading to work because of the internal compass. Will have to try speed and heading next time I’m in a car.
The Css Ninja, October 1st, 2009Quite possible, I’m running 3.1 (the current firmware).
Jeff, October 2nd, 2009A buddy of mine just tested out my code for me on his 3G and it was returning the heading, so it’s NOT 3GS specific. He said that it wouldn’t really give him values unless he was over about 30mph (48kph). He was also using the latest firmware (3.1).
Jeff, October 2nd, 2009@Jeff – Just did some testing, a quick data dump with the coords attribute. Ran it while I was walking down the street and was getting speed and heading results with the speed reading as low as 2km/h (meters * 3.6 to get a rough km/h). So looks like it’s getting pretty good results even at low speeds all it needs is a clear view of the sky to get these results.
The Css Ninja, October 7th, 2009This is incredible.
I have a list of Zip codes.
Can I display these zip codes on the map if they are within x miles (within view)?
Do you know a way to:
- converting the zips to longitude and latitude
- displaying them on the map
- plotting my location
Thoughts?
Rvhoss, December 8th, 2009You would have to look up the documentation for the mapping API you’re using. For cloudmade maps you can use their Geocoding method. Google Maps has similar methods for getting locations nearby etc.
The Css Ninja, December 8th, 2009Thanks for the quick response. I looked and wanted to get your thoughts on if I am approaching it right.
I have a data base of addresses
I should
1) plot the user first and then plot nearby locations or
2) plot my database of locations and then show the user where they are
any advice you can give would be great.
Rvhoss, December 9th, 2009If you’re only interested in plotting nearby locations from your db of address’ then option 1 would be the better approach. You’ll need the users location initially anyway to work out which of the address’ are nearby.
The Css Ninja, December 9th, 2009did you notice that very large marker images are not displayed centered, but with the center being the lower left corner.
Jonathan, December 16th, 2009Are you referring to the cloudmade marker not centering properly?
The Css Ninja, December 17th, 2009I am using the PhoneGap API system with my iPhone web app but am running into a problem. At the moment, I have the iPhone getting the GPS co-ordinates which I am then using to open Google Maps and plot the location on the screen.
What I seem to be encountering is that the GPS seems to need a good 30 seconds to get a good lock on. I believe the API can not only ‘navigator.geolocation.getCurrentPosition’ but use a function similar to Google Maps when it can follow the user. This would obviously solve my problem as it keeps refreshing the location, hence me being able to get a better GPS location lock.
Can anyone advise on how to edit my code (see below) to get this running. You help is appreciated.
I’m pretty sure the PhoneGap API emulates that of the Geolocation API so the syntax and methods available should match. If you want to be constantly updated with new lat/lng points you can use the watchPosition method which will fire everytime the location changes. Position acquiring can take some time if you’re not standing somewhere with a clear line of sight to any of the satellites also.
The Css Ninja, January 27th, 2010Hi!Excuse me …I have a question
My iPhone 3G is 3.0(7A341),but why it couldn’t display the location information?
thank you!
Eric, February 19th, 2010Not sure why it’s not working for you, you should probably update your firmware anyway.
The Css Ninja, February 20th, 2010