├── geo.js └── README.md /geo.js: -------------------------------------------------------------------------------- 1 | navigator.geolocation.getAccurateCurrentPosition = function (geolocationSuccess, geolocationError, options) { 2 | 3 | var lastCheckedPosition; 4 | 5 | var locationEventCount = 0; 6 | 7 | var checkLocation = function (position) { 8 | options.debug && console.log('checkLocation',position,locationEventCount); 9 | lastCheckedPosition = position; 10 | ++locationEventCount; 11 | // We ignore the first event unless it's the only one received because some devices seem to send a cached 12 | // location even when maxaimumAge is set to zero 13 | if ((position.coords.accuracy <= options.desiredAccuracy) && (locationEventCount > 0)) { 14 | clearTimeout(timerID); 15 | navigator.geolocation.clearWatch(watchID); 16 | foundPosition(position); 17 | } else { 18 | options.geoProgress(position); 19 | } 20 | } 21 | 22 | var stopTrying = function () { 23 | navigator.geolocation.clearWatch(watchID); 24 | foundPosition(lastCheckedPosition); 25 | } 26 | 27 | var onError = function (error) { 28 | clearTimeout(timerID); 29 | navigator.geolocation.clearWatch(watchID); 30 | geolocationError(error); 31 | } 32 | 33 | var foundPosition = function (position) { 34 | if ( typeof position == 'undefined') { 35 | geolocationError({code: 0}); 36 | } else { 37 | geolocationSuccess(position); 38 | } 39 | } 40 | 41 | if (!options.maxWait) options.maxWait = 10000; // Default 10 seconds 42 | if (!options.desiredAccuracy) options.desiredAccuracy = 20; // Default 20 meters 43 | if (!options.timeout) options.timeout = options.maxWait; // Default to maxWait 44 | if (!options.geoProgress) options.geoProgress = function(position){}; // do nothing 45 | if (!options.debug) options.debug = false; 46 | 47 | options.maximumAge = 0; // Force current locations only 48 | options.enableHighAccuracy = true; // Force high accuracy (otherwise, why are you using this function?) 49 | 50 | var watchID = navigator.geolocation.watchPosition(checkLocation, onError, options); 51 | var timerID = setTimeout(stopTrying, options.maxWait); // Set a timeout that will abandon the location loop 52 | } 53 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # getAccurateCurrentPosition 2 | 3 | __getAccurateCurrentPosition__ is a simple enhancement to 4 | [navigator.geolocation](http://dev.w3.org/geo/api/spec-source.html) 5 | that provides a more accurate and predictable result. It is intended for any 6 | geolocation-enabled web browser. This is also usable in PhoneGap applications since 7 | PhoneGap uses the underlying HTML geolocation APIs already. I have tested this on desktop 8 | Chrome, Safari, Firefox and on iOS and Android devices. I have not tested on IE9+ or 9 | Opera or Windows devices. 10 | 11 | 12 | ## Background: 13 | 14 | `navigator.geolocation` provides the method `geolocation.getCurrentPosition()` 15 | that will return the current location of the device. This seems easy enough so most 16 | developers simply call this method when they need the location. One of the options for 17 | this method is "enableHighAccuracy", which obviously implies that you need an accurate 18 | location. However, I soon discovered that if the device's GPS has not been used recently 19 | in the current location, it will take a while for it to acquire a decent location. The 20 | `getCurrentPosition()` success callback will trigger before the phone's GPS hardware can 21 | provide anything accurate. In other words, you get a quick response, but not necessarily 22 | an accurate response. 23 | 24 | In my own testing with an iPhone 4s and an HTC Inspire, when I would check 25 | `getCurrentPosition()` on the device, I would sometimes get an accuracy of over 1000 26 | meters. Basically, the first location to be acquired is what is passed to the callback. 27 | What if you need more accuracy? You can re-call `getCurrentPosition()` and likely better 28 | accuracy because the GPS has had more time to acquire satellites, but how many times will 29 | you need to call it? 30 | 31 | A better way to do this is to use `navigator.geolocation.watchPosition()`. This method 32 | will do a callback every time the location changes or every time the device improves 33 | the accuracy (based on my observations). In my own testing with a freshly booted device, 34 | it will take between 2 and 6 callbacks to get to something highly accurate. This led me 35 | to write this very simple JavaScript function that uses `watchPosition()` in combination 36 | with a simple timer. 37 | 38 | The option parameters are identical to `getCurrentPosition()` with the following additions: 39 | 40 | * __desiredAccuracy__: The accuracy in meters that you consider "good enough". Once a 41 | location is found that meets this criterion, your callback will be called. 42 | 43 | * __maxWait__: How long you are willing to wait (in milliseconds) for your desired 44 | accuracy. Once the function runs for maxWait milliseconds, it will stop trying and 45 | return the last location it was able to acquire. NOTE: If the desired accuracy is not 46 | achieved before the timeout, the onSuccess is still called. You will need to check the 47 | accuracy to confirm that you got what you expected. I did this because it's a "desired" 48 | accuracy, not a "required" accuracy. You can of course change this easily. 49 | 50 | * __geoProgress__: A function that expects a _position_ argument, which is executed 51 | during location acquisition. It will fire each time a new or more accurate position is 52 | determined, until the _desiredAccuracy_ is reached, or _maxWait_ has elapsed. 53 | It can be used to merely provide the user with feedback or to trigger other 54 | functions of your web/app. 55 | 56 | * __debug__: Boolean. If set to `true` then various information will be logged to the 57 | console. Set `false` by default. 58 | 59 | The following params also exist for `getCurrentPosition()` but are set for you in 60 | `getAccurateCurrentPosition()`: 61 | 62 | * __timeout__: If no timeout is specified, it will be set to the __maxWait__ value. 63 | * __enableHighAccuracy__: This is forced to true (otherwise, why are you using this 64 | function?!). 65 | * __maximumAge__: This is forced to zero since we only want current location information. 66 | 67 | 68 | ## Sample usage: 69 | 70 | navigator.geolocation.getAccurateCurrentPosition( 71 | mySuccessFunction, 72 | myErrorFunction, 73 | { 74 | desiredAccuracy: 20, 75 | maxWait: 15000, 76 | geoProgress: myProgressFunction 77 | } 78 | ); 79 | 80 | function mySuccessFunction( position ) { ... } 81 | 82 | function myErrorFunction( error ) { ... } 83 | 84 | function myProgessFunction( position ) { ... } 85 | 86 | Translating the above options into english -- This will attempt to find the device 87 | location with an accuracy of at least 20 meters and attempt to achieve this accuracy 88 | for 15 seconds. As it tries to obtain this accuracy, it will fire `myProgressFunction()`. 89 | 90 | Blogged at 91 | --------------------------------------------------------------------------------