Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 23 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,39 @@ In my own testing with an iPhone 4s and an HTC Inspire, when I would check getCu

A better way to do this is to use <strong>navigator.geolocation.watchPosition()</strong>. This method will do a callback every time the location changes or every time the device improves the accuracy (based on my observations). In my own testing with a freshly booted device, it will take between 2 and 6 callbacks to get to something highly accurate.  This led me to write this very simple JavaScript function that uses watchPosition() in combination with a simple timer.

<h3>Options:</h3>
The option parameters are identical to getCurrentPosition() with the following additions:
<ul>
<li><strong>desiredAccuracy</strong>: The accuracy in meters that you consider "good enough". Once a location is found that meets this criterion, your callback will be called.</li>
<li><strong>maxWait</strong>: How long you are willing to wait (in milliseconds) for your desired accuracy. Once the function runs for maxWait milliseconds, it will stop trying and return the last location it was able to acquire. NOTE: If the desired accuracy is not achieved before the timeout, the onSuccess is still called. You will need to check the accuracy to confirm that you got what you expected. I did this because it's a "desired" accuracy, not a "required" accuracy. You can of course change this easily.</li>
<li><strong>desiredAccuracy=20</strong>: The accuracy in meters that you consider "good enough". Once a location is found that meets this criterion, your callback will be called.</li>
<li><strong>maxWait=10000</strong>: How long you are willing to wait (in milliseconds) for your desired accuracy. Once the function runs for maxWait milliseconds, it will stop trying and return the best location it was able to acquire. NOTE: If the desired accuracy is not achieved before the timeout, the onSuccess is still called. You will need to check the accuracy to confirm that you got what you expected. I did this because it's a "desired" accuracy, not a "required" accuracy. You can of course change this easily.</li>
<li><strong>countMin=2</strong>: First event may be cached (even on maximumAge=0).
<li><strong>desiredAccuracyCountMin=1</strong>: You may wait and allow for more (accurate) positions. MaxWait is unaffected by this.
<li><strong>enableLowAccuracy=false</strong>: Simultaneously a low accuracy result is searched (seems to be ignored on Chrome 43).
</ul>
The following params also exist for getCurrentPosition() but are set for you in getAccurateCurrentPosition():
<ul>
<li><strong>timeout</strong>: If no timeout is specified, it will be set to the maxWait value</li>
<li><strong>enableHighAccuracy</strong>: This is forced to true (otherwise, why are you using this function?!)</li>
<li><strong>maximumAge</strong>: This is forced to zero since we only want current location information</li>
<li><strong>timeout</strong>: Is set to maxWait value. It is not recommended to change this value.</li>
<li><strong>enableHighAccuracy=true</strong>: This is forced to true (otherwise, why are you using this function?!)</li>
<li><strong>maximumAge=0</strong>: You may allow a cached position (as a starter).</li>
</ul>

<h3>Callbacks:</h3>
<ul>
<li><strong>onProgress(Position)</strong>: Standard geolocation <a href="https://developer.mozilla.org/en-US/docs/Web/API/Position">Position</a>
<li><strong>onError(PositionError)</strong>: Standard geolocation <a href="https://developer.mozilla.org/en-US/docs/Web/API/PositionError">PositionError</a>
<li><strong>onSuccess(Position, resultString)</strong>: resultString is 'success' if desired accuracy is met or 'timeout'
</ul>


<h3>Sample usage:</h3>
<code>navigator.geolocation.getAccurateCurrentPosition(onSuccess, onError, onProgress,
{desiredAccuracy:20, maxWait:15000});</code>

Translating the above options into english -- This will attempt to find the device location with an accuracy of at least 20 meters and attempt to achieve this accuracy for 15 seconds
Translating the above options into english -- This will attempt for 15 seconds to find the device location and will return as soon the accuracy is at least 20 meters. Otherwise the best result found is returned.


<h3>Recommendation:</h3>
You should call this function inititally with <code>{maxWait:120000}</code> in the background, so a fix can be aquired.
Thus, subsequent calls are much quicker. For example <code>{desiredAccuracy:20, desiredAccuracyCountMin:5, maxWait:20000, enableLowAccuracyOnTimeout:true}</code> to allow for the best location out of 5 with an accuracy of at least 20m, if this is possible in the 20s given. If there is no gps position, it will return the best it has.

Blogged at <a target="_blank" href="http://gregsramblings.com/2012/06/30/improving-geolocation-getcurrentposition-with-getaccuratecurrentposition/">http://gregsramblings.com/2012/06/30/improving-geolocation-getcurrentposition-with-getaccuratecurrentposition/</a>
20 changes: 20 additions & 0 deletions bower.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
{
"name": "getAccurateCurrentPosition",
"version": "0.1.0",
"homepage": "https://github.com/zevero/getAccurateCurrentPosition",
"authors": [
"gwilsen and zevero"
],
"description": "Simple function to complement navigator.geolocation - fine tuning the location before replying",
"main": "geo.js",
"moduleType": [],
"keywords": [
"geolocation",
"enableHighAccuracy",
"location",
"gps",
"html5",
"navigator"
],
"license": "MIT"
}
63 changes: 43 additions & 20 deletions geo.js
Original file line number Diff line number Diff line change
@@ -1,28 +1,45 @@
/*navigator.geolocation.test = 100;
var global_geo_int = setInterval(function(){
if (!navigator.geolocation || !navigator.geolocation.test){
clearInterval(global_geo_int);
return console.log('geo_stopped');
}
console.log(navigator.geolocation.test--);
},10);
console.log(navigator.geolocation.test);
*/
setTimeout(function(){ //navigator.geolocation is OVERWRITTEN on startup. So we load and use it with timeout
console.log('extending navigator.geolocation with getAccurateCurrentPosition');
navigator.geolocation.getAccurateCurrentPosition = function (geolocationSuccess, geolocationError, geoprogress, options) {
var lastCheckedPosition,
var bestCheckedPosition,
locationEventCount = 0,
watchID,
desiredAccuracyCount = 0,
watchID,watchID_low,
timerID;

options = options || {};

var checkLocation = function (position) {
lastCheckedPosition = position;
geoprogress(position);
if (!bestCheckedPosition || position.coords.accuracy <= bestCheckedPosition.coords.accuracy) {
bestCheckedPosition = position;
}
locationEventCount = locationEventCount + 1;
// We ignore the first event unless it's the only one received because some devices seem to send a cached
// location even when maxaimumAge is set to zero
if ((position.coords.accuracy <= options.desiredAccuracy) && (locationEventCount > 1)) {

if ((position.coords.accuracy <= options.desiredAccuracy) &&
(++desiredAccuracyCount >= options.desiredAccuracyCountMin) &&
(locationEventCount >= options.countMin)) {
clearTimeout(timerID);
navigator.geolocation.clearWatch(watchID);
foundPosition(position);
} else {
geoprogress(position);
geolocationSuccess(bestCheckedPosition, 'success');
}
};

var stopTrying = function () {
navigator.geolocation.clearWatch(watchID);
foundPosition(lastCheckedPosition);
navigator.geolocation.clearWatch(watchID_low);
if (bestCheckedPosition) geolocationSuccess(bestCheckedPosition, 'timeout');
else geolocationError({code:3, message:'Timeout after trying for '+options.maxWait+' ms!'}); //sniff
};

var onError = function (error) {
Expand All @@ -31,17 +48,23 @@ navigator.geolocation.getAccurateCurrentPosition = function (geolocationSuccess,
geolocationError(error);
};

var foundPosition = function (position) {
geolocationSuccess(position);
};

if (!options.maxWait) options.maxWait = 10000; // Default 10 seconds
if (!options.desiredAccuracy) options.desiredAccuracy = 20; // Default 20 meters
if (!options.timeout) options.timeout = options.maxWait; // Default to maxWait

options.maximumAge = 0; // Force current locations only

if (isNaN(options.maxWait)) options.maxWait = 10000; // Default 10 seconds
if (isNaN(options.desiredAccuracy)) options.desiredAccuracy = 20; // Default 20 meters
if (isNaN(options.desiredAccuracyCountMin)) options.desiredAccuracyCountMin = 1; // Default get first position of desiredAccuracy

if (isNaN(options.timeout)) options.timeout = options.maxWait; // Default to maxWait
if (isNaN(options.maximumAge)) options.maximumAge = 0; // Default current locations only
if (isNaN(options.countMin)) options.countMin = 2; // Default ignore first event because some devices send a cached
// location even when maxaimumAge is set to zero

if (options.enableLowAccuracy) watchID_low = navigator.geolocation.getCurrentPosition(checkLocation, function(e){console.log('getAccuratePostition LowAccuracy Error',e.code);}, options);
//Optionally start a low Accuracy reading. I expected to find an early result as fallback, if no better results are found
//however this the enableHighAccuracy flag seems to be bluntly ignored on Chrome 43 and others
// see http://stackoverflow.com/questions/17804469/html5-geolocation-ignores-enablehighaccuracy-option/32521789

options.enableHighAccuracy = true; // Force high accuracy (otherwise, why are you using this function?)

watchID = navigator.geolocation.watchPosition(checkLocation, onError, options);
timerID = setTimeout(stopTrying, options.maxWait); // Set a timeout that will abandon the location loop
};
},200); //it is overwritten around 10-20ms after load. So 200 ms should be enough?