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: enableHighAccurracy, 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.
@Koyetsu – Google has a reverse geocoding service just pass your latlng to that and it will return JSON
I do not have an iPhone, so I am going on faith here. I assume that the latitude and longitude are returned in decimal format?
As you know, there are three different GPS formats: decimal (45.758759), degrees-minutes (N 19° 38.416), and degrees-minutes-seconds (N 19° 38′ 13”).
If the return values of the coordinates are not in decimal format, please let us know. Thanks!
@Idiot –
Any browser that says they support the geolocation API will abide by the specification which states:
So it should always supply the coordinates in decimal degrees. Trying to work with any other format would be a nightmare in javascript.
Would it be possible to write a small app that gets gps periodically. Lets say every 5 minutes and then writes a file to the iphone with the coordinates. Could this easily be done in javascript or jquery?
@cam – You could call the
getCurrentPositionusing thesetIntervalmethod and set it to fire every 5mins(300000ms) and then save the position using localStorage e.g.thanks to this (and some other) great tutorials i’ve got my web app running – however i’m curious about how to control the ‘time out’ value of the browser. Once the iPhone goes to sleep it stops sending position and location. Can this be controlled?
-bill
@Bill P – If you’re wanting the
watchPositionfunction to keep updating the users location even when the phone is asleep I think you’re out of luck. Not only would that be a serious privacy issue but also a serious battery drain.@Bill P – I agree with CSS Ninja – unfortunately this cannot be done through the Javascript API. The only way to keep getting position updates in Safari via the JS API is to manually disable the sleep setting in the iphone.
If you are writing a native app, you can however work around this “problem” by doing something like this:
- (void)applicationDidFinishLaunching:(UIApplication *)application {
application.idleTimerDisabled = YES;
[window addSubview:[rootViewController view]];
[window makeKeyAndVisible];
}
But this API is not exposed via Javascript, so that can only be done in native apps. I wrote a hybdid app that almost had no native code other than the code above to disable the sleep timer – the rest of my application was written using the javascript API.
As CSS Ninja says when you disable the idleTimer it will lead to serious battery drain, because not only will the GPS keep updating, the screen will also be on maximum brightness, the accelerometer will keep updating etc. etc. It would be nice if I could just turn on GPS updates and let the rest of the hardware go into sleep as usual.
Thanks for the reply on timeout. I’m resigned to requiring my users to set auto lock to never and to reduce screen brightness.
I’ve got a live demo that stores path info running at logsitall.com/geo/geo-second.asp – feedback would be welcome.
Oh wise Ninja Sir
I’m working on this iphone webapp to help educate our fire departments who set up Night time landing zones for us when we need to land we have to have them at night to land. So we wanted to create a nifty tool for them and to help us get out education easier. Anyway I’m by no means a JS savy person even though I’m learning. All I want to do in this app is show the coordinates of their current position and I”m having trouble making it work. Any chance you can post just simple script to call the location and then to how to document write them into a div.
Thanks ahead of time
@Lenny C – Sounds like a cool idea, take a look at this web app which outputs all the available information that can be read from the GPS.
Is there an easy way to call the coords as deg min sec or do we have to do the math. I’ve been looking at examples but any changes i make to the current script causes it to fail (not sure why).. example if i add the enableHighAccuracy: it stops working… but my main issue is I must have them as deg min and sec.
thanks again “Wise Ninja”
@Lenny – The coordinates are only specified in decimal degrees you’ll need to do the maths to convert them to degrees, minutes, seconds notation.
You don’t have to have the newest mobile phone to use GPS and Geolocation API. Almost every mobile browser (without proxy server) can be used to read position from buidin GPS. If You have Java and GPS in Your phone – You can use mobile-gps-web-gate – see at http://code.google.com/p/mobile-gps-web-gate/
Hey All and especially Ninja,
navigator.geolocation.getCurrentPosition(success, fail);
This call is very slow and may sometimes fail on Safari. Works pretty well on Chrome, Firefox and Opera.
Do you know anything about this?
Lakeside
There can be many factors why it could be slow. Set a timeout value so say after 3 seconds it will trigger the error callback and in the callback you can trigger the getPosition call again e.g.
When I use the “View a live demo” button from my iPhone 3Gs, I get the Apple Inc.’s headquarters shown, not my current location. Any suggestions?
@Emmanuel G
Does the demo prompt you for GPS access? If not you may either have location services turned off for Safari. Settings > General > Location Services, make sure that toggle switch is on. Another reason why it might not be working is if you have denied access to the request before. You’ll possibly need to reset location warnings to get prompted again, Settings > General > Reset.
Hi!
I am facing a problem with the geolocation API which I hope you might be able to help me with.
Some of people here commented that when denying the access to the position on the prompt, the geolocation got “blocked” and it became necessary to reset the location warnings on the iphone in order to make it to work again.
Well, in my case the thing is that even if I restart the location warning, when I call the getCurrentLocation I always get the error 1 (User denied location) even though I haven´t said anything… Has anyone faced the same problem and still continued after restarted the location warnings?
Thanks so much for your help in advance.
Cheers, Victor
@Victor –
If you navigate to settings > general > reset and tap Reset Location Warnings that should fix your problem. Make sure to kill safari too just to make sure it boots up a new instance.
Same problem here as Victor. Choosing Deny on the “…would like to use your location…” popup pretty much permanently disables geoLocation. Even if I use Settings -> General -> Reset Location Warnings, yes the popup asking if I want to allow Safari to use my location does in fact popup, but afterwards, the getCurrentPostion call ALWAYS fails with code: 1 message “User denied GeoLocation”, even if I select Allow. This has most certainly got to be a bug in the geo location service of the phone. The same app and code based pushed to a staging site works perfectly fine on chrome and safari browsers, as well as the iPhone Emulator from XCode. These all allow you to simply clear the cache and reload the site and have the geoLocation work fine.
Hi. Is there a way to suppress the dialog that asks if my page would like to use your current location? I’m using jQueryMobile and would like to show my own dialog.
Thanks!
@andy –
You can’t suppress the OS dialog.
Hey,
I’m having the same problem as some of you guys. Even though I approve the popup asking if my website is allowed to use my position date I always get a “User denied Geolocation” error. I tried resetting location warnings, cache, cookies etc. but nothing helps.
Has anybody found a solution for this without having to reset the whole phone?
// Daniel
Awesome, very useful article and one of the few places that draws attention to the advanced options for the geolocation API.