Make geolocation call synchronously in javascript - javascript

Im developing an app based on geolocation, so its mandatory to get the position in the first place, even before the execution of the rest of the app. So, how can I convert this module in a SYNCHRONOUS way????
var geolocation = (function() {
'use strict';
var geoposition;
var options = {
maximumAge: 1000,
timeout: 15000,
enableHighAccuracy: false
};
function _onSuccess (position) {
console.log('DEVICE POSITION');
console.log('LAT: ' + position.coords.latitude + ' - LON: ' + position.coords.longitude);
geoposition = position
};
function _onError (error) {
console.log(error)
};
function _getLocation () {
navigator.geolocation.getCurrentPosition(
_onSuccess,
_onError,
options
);
}
return {
location: _getLocation
}
}());
Thank you very much!

Geolocation has to remain asynchronous, but you can achieve what you want by passing in a callback to your module's main function and calling it each in the success and error functions, after they have completed their processing:
var geolocation = (function() {
'use strict';
var geoposition;
var options = {
maximumAge: 1000,
timeout: 15000,
enableHighAccuracy: false
};
function _onSuccess (callback, position) {
console.log('DEVICE POSITION');
console.log('LAT: ' + position.coords.latitude + ' - LON: ' + position.coords.longitude);
geoposition = position
callback();
};
function _onError (callback, error) {
console.log(error)
callback();
};
function _getLocation (callback) {
navigator.geolocation.getCurrentPosition(
_onSuccess.bind(this, callback),
_onError.bind(this, callback),
options
);
}
return {
location: _getLocation
}
}());
geolocation.location(function () {
console.log('finished, loading app.');
});
Fiddle

I think you should not try to make it synchronously. Just implement some kind of event system for geolocation getter. This way it work asynchronously but geolocation initializes your app or components which requires geolocation.
Here's a dead simple example of how this could work:
var callbacks = [];
var onGeolocationReady = function (callback) {
callbacks.push(callback);
}
function _onSuccess (position) {
// iterare through each callback and invoce them
}
// and making component
onGeolocationReady(function () {
// some code here which requires geolocation
});
I hope this helps.

Related

navigator.geolocation returns error from second execution on - IE

When executing navigator.geolocation.getCurrentPosition(success, error, options); for the first time, I'm able to get the user's location. however from the second execution on, the function returns the error:
The current position could not be determined.
I have followed the advice given in this question's answers with no success, how can i get this to work?
Here you can find a working fiddle to quickly see the error.
//Pass this options to the getCurrentPosition
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
//function to execute if the current position was succesfully retrieved
function success(pos) {
console.log(pos);
var crd = {lat: pos.coords.latitude, lng : pos.coords.longitude };
var myPre = document.querySelector('pre');
myPre.textContent = JSON.stringify(crd);
myPre.style.color = someColor(); // use a diferent color just to see it's a new execution of the code
};
//execute this on error
function error(err) {
var myPre = document.querySelector('pre');
myPre.textContent = err;
myPre.style.color = someColor(); // use a diferent color
};
//attach function to button
var myButton = document.querySelector('button');
myButton.addEventListener('click', function(){
navigator.geolocation.getCurrentPosition(success, error, options);
});
My idea is the following:
The IE user only allows the website (script) (by default settings) to run getCurrentLocation once. The user has to grant an exception for it to run multiple times.
However I don't know (and can't really find any documentation) if this behaviour is by design or a bug. The solution below is a work-around.
Use watchposition instead after the initial succes circumnavigates this bug. See updated fiddle: https://jsfiddle.net/b2rnr7tw/6/
In this fiddle I set up a watchPosition and as soon as it updates it shows the new location. After that it is cancelled (else it keeps updating).
//Pass this options to the getCurrentPosition
var options = {
enableHighAccuracy: true,
timeout: 5000,
maximumAge: 0
};
var watch = null;
var watchId = null;
//function to execute if the current position was succesfully retrieved
function success(pos) {
var crd = {lat: pos.coords.latitude, lng : pos.coords.longitude };
var myPre = document.querySelector('pre');
myPre.textContent = JSON.stringify(crd);
myPre.style.color = someColor(); // use a diferent color
watch.clearWatch(watchId); //after success clear the watchId.
};
//execute this on error
function error(err) {
var myPre = document.querySelector('pre');
myPre.textContent = err;
myPre.style.color = someColor(); // use a diferent color
//keep running the watchPosition if on error, however you can use a counter to only try it a few times (recommended)
};
//attach function to button
var myButton = document.querySelector('button');
myButton.addEventListener('click', function(){
if (!watch)
{
watch = navigator.geolocation;
watch.getCurrentPosition(success, error, options);
}
else
{
watchId = watch.watchPosition(success, error, options);
}
});
Mouser's solution worked for me in IE11 however breaks Edge, so we need browser detection. Here is my solution tested in IE11, Edge 14, FFx and Chrome (latest versions of FFx and Chrome at time of writing)
var currentPositionHasBeenDisplayed = false;
if (navigator.geolocation) {
var options = {};
var isIE = document.documentMode; //IE 8+
// IE only allows one call per script to navigator.geolocation.getCurrentPosition, so we need a workaround
if (currentPositionHasBeenDisplayed == true && isIE) {
navigator.geolocation.watchPosition(
function (pos) {
var myLatLng = new google.maps.LatLng(parseFloat(pos.coords.latitude), parseFloat(pos.coords.longitude));
map.setCenter(myLatLng);
},
function (error) { },
options);
}
else {
navigator.geolocation.getCurrentPosition(
function (pos) {
var myLatLng = new google.maps.LatLng(parseFloat(pos.coords.latitude), parseFloat(pos.coords.longitude));
map.setCenter(myLatLng);
currentPositionHasBeenDisplayed = true;
},
function (error) { return false; },
options);
}
}

How to use browser GeoLocation after user allows access

I'm having trouble with a weather API call which returns JSON based on geoLocation. The browser asks the question allow/deny access to location and then I want the API call to happen, preferably on page load. I have put the call behind a button click instead but it still doesn't update the page with the local weather. However, if I step through the code using the debugger it works properly.
Javascript:
$(document).ready(function () {
var curLatitude = 'x';
var curLongditude = 'y';
var weatherApi = 'http://api.openweathermap.org/data/2.5/weather?q=London,GB&APPID=[insert-registered-api-key]';
// check for Geolocation support
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(position) {
curLatitude = position.coords.latitude;
curLongditude = position.coords.longitude;
if (curLatitude !== 'x' && curLongditude !== 'y') {
weatherApi = 'http://api.openweathermap.org/data/2.5/weather?lat=' + curLatitude +
'&lon=' + curLongditude + '&APPID=[insert-registered-api-key]';
};
loadWeather(weatherApi);
}, function() {
console.log('FAILED');
} );
} else {
console.log('No geoLoc');
// no geo location support - just load London weather
loadWeather(weatherApi);
};
$('#btnLocal').click(function () {
$(".message").html('clicked');
loadWeather(weatherApi);
});
function loadWeather(weatherApiUri) {
var current = "";
var ok;
var ret = $.getJSON(weatherApiUri, function(jsonres) {
console.log( "JSON Data: loaded" );
current = 'called api';
console.log(current);
$(".message").html(current);
}).done(function() {
console.log( "done" );
}).fail(function() {
console.log( "error" );
}).always(function() {
console.log( "complete" );
});
var weatherReturned = {id:0 , main:"weather", description:"weather detail", icon:"code" };
weatherReturned = ret.responseJSON.weather;
$(".message").html(ret.responseJSON.name + ', ' + weatherReturned[0].description);
};
});
JSON response:
{"coord":{"lon":-0.13,"lat":51.51},"weather":[{"id":500,"main":"Rain","description":"light rain","icon"
:"10d"}],"base":"cmc stations","main":{"temp":290.673,"pressure":1009.48,"humidity":83,"temp_min":290
.673,"temp_max":290.673,"sea_level":1019.21,"grnd_level":1009.48},"wind":{"speed":4.3,"deg":93.0027}
,"rain":{"3h":0.1375},"clouds":{"all":92},"dt":1462884891,"sys":{"message":0.0043,"country":"GB","sunrise"
:1462853704,"sunset":1462909185},"id":2643743,"name":"London","cod":200}
What you can do is to set a timer with call setInterval function.
In timer function you can check a flag you will set after navigator.geolocation check pass.
Only in interval function you will put the logic.
Just don't forget to clear timer to avoid unwanted calls.
Code can be like this:
var timer = setInterval(function(){
if(geoReady){
// clear interval
window.clearInterval(timer);
// do your logic
}, 300
}
if (navigator.geolocation) {
geoReady = true;
}
else {
// clear interval
window.clearInterval(timer);
}

Trying to get local weather

I am trying to get local weather by getting currentposition and passing it to url for getting results. I can't seem to be able to pass the coordinates outside the getCurrentPosition.
My codepen is: http://codepen.io/rush86999/pen/MKMywE
if (navigator.geolocation) {
//position.coords.longitude
var app = {
getGeoLoc: function(id) {
var self = this;
navigator.geolocation.getCurrentPosition(function(position) {
var myVar1, myVar2, myVar3; // Define has many variables as you want here
// From here you can pass the position, as well as any other arguments
// you might need.
self.foundLoc(position, self, myVar1, myVar2, myVar3);
}, this.noloc, {
timeout: 3
});
},
foundLoc: function(position, self, myVar1, myVar2, myVar3) {
this.latituide = position.coords.latituide;
this.longitude = position.coords.longitude;
console.log('#4 position coords work in foundLoc: ', this.latitude, this.longitude);
},
latitude: '',
longitude: ''
};
console.log('#5 found loc in app, ', app.foundLoc);
var url = 'api.openweathermap.org/data/2.5/weather?lat=' + app.latitude + '&lon=' + app.longitude + '&APPID=7bda183adf213c8cfa2ef68635588ef3';
//lets look inside url
console.log('#1 url has coordinates: ', url);
Theres a few issues here.
Firstly, you don't seem to be calling the getGeoLoc method, so that would be the first fix.
You have included an error callback of this.noloc but it isn't included in your object.
There are a few typo's for your co-ordinates
You are making your API request before the geolocation has resolved so app.latitude and app.longitude will be undefined. This should ideally be wrapped in a method that gets called upon a successful geolocation request.
var app = {
getGeoLoc : function (id) {
//Removed timeout option due to error
var options = {}; //{ timeout: 3 };
navigator.geolocation.getCurrentPosition(this.foundLoc.bind(this), this.noloc, options);
},
foundLoc : function(position) {
this.latitude = position.coords.latitude;
this.longitude = position.coords.longitude;
console.log('coords ', this.latitude, this.longitude);
// Call your get weather function
// Using call to bind the context of `this`
this.getWather.call(this);
},
// Error method
noloc: function(err) {
console.log(err.message);
},
// Method to get your weather after location is found
getWather: function() {
var url = 'http://api.openweathermap.org/data/2.5/weather?lat=' + this.latitude + '&lon=' + this.longitude +'&APPID=7bda183adf213c8cfa2ef68635588ef3';
console.log('URL is: '+url);
$.getJSON(url, function(data) {
console.log('Your weather data', data);
// Do your dom stuff here
});
},
latitude: '',
longitude: ''
};
// Make sure to call your initialising function
app.getGeoLoc();
NOTE: I have removed the HTML stuff for the demo and have removed the timeout option as it caused an error.
Link to forked codepen

ReferenceError: function parseXml undefined in Firefox

I am getting this strange error even when parseXml is defined. The piece of code works fine in Chrome but NOT in Firefox.
$(document).on("pageinit", "#map-page", function () {
var defaultLatLng = new google.maps.LatLng(56.8517843, 14.828458); // Default somewhere to Växjö when no geolocation support
if (navigator.geolocation) {
var stations = [];
$.ajax({
type: "GET",
url: "busstations.xml",
dataType: "xml",
success: parseXml
});
function parseXml(xml) {
$(xml).find('station').each(function () {
var name = $(this).find("name").text();
var localurl = $(this).find("localurl").text();
var latitude = $(this).find("latitude").text();
var longitude = $(this).find("longitude").text();
navigator.geolocation.getCurrentPosition(success, fail, {
maximumAge: 500000,
enableHighAccuracy: true,
timeout: 6000
});
function success(pos) {
currentLatitude = pos.coords.latitude;
currentLongitude = pos.coords.longitude;
console.log(pos.coords.latitude + " " + pos.coords.longitude);
}
function fail(error) {
alert("No GL support!");
}
stations.push({
"name": name,
"localurl": localurl
});
console.log(JSON.stringify(stations));
});
}
}
});
However if I remove the if(navigator.geolocation) check condition on the 3rd line, then it also works fine in Firefox and there is also no such undefined ReferenceError.
Also if I bring this if(navigator.geolocation) check condition inside the parseXml function, the code works fine. Wonder what is causing the problem in Firefox.
Is this acceptable and working?
$(document).on("pageinit", "#map-page", function () {
var defaultLatLng = new google.maps.LatLng(56.8517843, 14.828458); // Default somewhere to Växjö when no geolocation support
if (navigator.geolocation) {
$.ajax({
type: "GET",
url: "busstations.xml",
dataType: "xml",
success: parseXml
});
}
});
function parseXml(xml) {
var stations = [];
$(xml).find('station').each(function () {
var name = $(this).find("name").text();
var localurl = $(this).find("localurl").text();
var latitude = $(this).find("latitude").text();
var longitude = $(this).find("longitude").text();
navigator.geolocation.getCurrentPosition(
function(pos) {
currentLatitude = pos.coords.latitude;
currentLongitude = pos.coords.longitude;
console.log(pos.coords.latitude + " " + pos.coords.longitude);
},
function(error) {
alert("No GL support!");
},
{
maximumAge: 500000,
enableHighAccuracy: true,
timeout: 6000
}
);
stations.push({
"name": name,
"localurl": localurl
});
console.log(JSON.stringify(stations));
});
}
The problem might be that Firefox deals a little different with function declarations inside conditionals statments. The documentation says:
Note: Although this kind of function looks like a function
declaration, it is actually an expression (or statement), since it is
nested within another statement. See differences between function
declarations and function expressions.
So if it is an expression then when the ajax call try to use it the function is not defined yet.
To fix it change the order of the declaration or declare the function outside.
This is covered also in this question.

Geolocation can't return confirmation?

I'm looking for a way to trigger user geolocation navigator function from another function mapInit(). It nearly works, but I can't have a proper callback of getCurrentPosition() to confirm it went well.. it return undefined each times.
My geolocation object will have to achieve other tasks so I don't want it to trigger mapInit(). It should have to get user location, record it and return trueor false.. Any guess?
Thanks :)
// Get user current position, return true or false
//
var geolocation = {
get: function() {
if (alert(navigator.geolocation.getCurrentPosition(this.success, this.error, {
enableHighAccuracy: true,
maximumAge: 5000
})) {
return true;
} else {
return false;
}
},
success: function(position) {
this.last = position; // record last position
return true;
},
error: function() {
alert('code: ' + error.code + 'n' + 'message: ' + error.message + 'n')
return false;
},
last: undefined,
}
// Initialize leaflet map and get user location if coords are undefined
//
var mapInit = function(latitude, longitude) {
if (!latitude && !longitude) { // if no latlng is specified, try to get user coords
if (geolocation.get()) {
latitude = geolocation.last.coords.latitude;
longitude = geolocation.last.coords.longitude;
} else {
alert('oups!');
}
}
var map = L.map('map').setView([latitude, longitude], 15);
L.tileLayer('http://{s}.tile.cloudmade.com/#APIKEY#/68183/256/{z}/{x}/{y}.png', {
minZoom: 13,
maxZoom: 16,
}).addTo(map);
var marker = L.marker([latitude, longitude]).addTo(map);
}
Not sure I understand what you're trying to do but when you call "getCurrentPosition" the first argument you pass is a method that will be called with the Position once it is retrieved. As you said in your comment getCurrentPosition will always return immediately but the callback method will be called if the user position can be retrieved (it may never be called):
navigator.geolocation.getCurrentPosition( function(position) {
var lat = position.coords.latitude;
var lon = position.coords.longitude;
//do something like recent the Map
});
You will need to create the Leaflet Map first with some default coordinates and then recenter the map with the coordinates provided to the callback method.

Categories