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.

Skip to comment form.

  1. @Koyetsu – Google has a reverse geocoding service just pass your latlng to that and it will return JSON

  2. Idiot says:

    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!

  3. @Idiot –

    Any browser that says they support the geolocation API will abide by the specification which states:

    The latitude and longitude attributes are geographic coordinates specified in decimal degrees.

    So it should always supply the coordinates in decimal degrees. Trying to work with any other format would be a nightmare in javascript.

  4. cam says:

    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?

  5. @cam – You could call the getCurrentPosition using the setInterval method and set it to fire every 5mins(300000ms) and then save the position using localStorage e.g.

    navigator.geolocation.getCurrentPosition(success, fail);
     
    function success(pos) {
        // Do something with position
     
        // Store position in localStrorage
        window.localStorage.setItem("coords", 
        [pos.coords.latitude,pos.coords.longitude]);
     
        var getPositionInterval = window.setInterval(
            "navigator.geolocation.getCurrentPosition(success, fail)",
             300000);
    }
  6. bill patton says:

    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

  7. @Bill P – If you’re wanting the watchPosition function 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.

  8. Flemming says:

    @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.

  9. Billp says:

    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.

  10. 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

  11. @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.

  12. 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.

    navigator.geolocation.watchPosition(
        function (position) {
            var lat=position.coords.latitude;
            var long=position.coords.longitude;
            var accur=position.coords.accuracy;
            document.getElementById('location').innerHTML=
                'Your current location is:' + '' + 'Lat: ' +lat + '' + 
                'Long: ' +long + '' + 'Accuracy: ' +accur + 
                ' meters' + '';
        },
        function(error) {
            alert('Sorry, could not find you... error code: '+error.code);
        });

    thanks again “Wise Ninja”

  13. @Lenny – The coordinates are only specified in decimal degrees you’ll need to do the maths to convert them to degrees, minutes, seconds notation.

  14. krycek says:

    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/

  15. 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

  16. 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.

    navigator.geolocation.getCurrentPosition(success, fail,
        {timeout: 3000});
     
    function fail(e) {
        // Try again
        if(e.TIMEOUT) {
           navigator.geolocation.getCurrentPosition(success, fail,
              {timeout: 3000});
        } else {
            // Failed for some other reason
        }
    }
  17. Emmanuel Garcia says:

    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?

  18. @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.

  19. Victor says:

    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

  20. Ryan Seddon says:

    @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.

  21. @RB says:

    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.

  22. andy leontovich says:

    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!

  23. Ryan Seddon says:

    @andy

    You can’t suppress the OS dialog.

  24. Daniel says:

    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

  25. Deepwinter says:

    Awesome, very useful article and one of the few places that draws attention to the advanced options for the geolocation API.

  26. Mik Muller says:

    Awesome. Looking for help getting the GPS info into two hidden form fields so the x y info will be posted with the form. JS function should continually update every few seconds to get the most accurate info. Easy?

  27. Mik Muller says:

    Got that part to work, however the GPS locations are literally all over the map. I just tried mapping a river and five out of seven location captures put me outside the river, with a couple of those being a mile away! Ugh. Any way to get more accurate GPS info?