How to use browser GeoLocation after user allows access - javascript

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);
}

Related

How to return the coordinates of a getLocation function, return latitude and longitude

I am attempting to build a weather API using the freeCodeCamp API for the weather project.
What I want to do is use the getLocation function to get the latitude and longitude and then return them as a variables that I can use to then concat to a URL.
By using the URL I can obtain the json information I need to output the Fahrenheit and any other info I need.
Since I require an https connection I am using codepen for testing purposes.
additional info:
Codepen:
https://codepen.io/rogercodes/pen/gXvOoO
freeCodeCamp API:
https://fcc-weather-api.glitch.me/
HTML
<html>
<head>
<!-- <link rel="stylesheet" type="text/css"
href="css/quoteStyles.css"> -->
</head>
<body>
<button onclick="getWeather(finalLat,finalLon)">getWeather</button>
<p>What is</p>
<button onclick="getLocation()">Try It</button>
<p id='geoAPI'>Geo API</p>
<p id="lan">Test Lan: </p>
<p id='geo'>geoLocal</p><script>
</script>
</body>
<script src="javascript/weatherTest.js"></script>
<!-- <script src="JSON/weather.json"></script> -->
Javascript
var api= "https://fcc-weather-api.glitch.me/api/current?";
var googleApi="https://www.googleapis.com/geolocation/v1/geolocate?
key=AIzaSyCOMzDSyP4RkXwp7mSiFiuAZroyrazU5eM";
var lat, lon;
var x= document.getElementById("geoLocal");
var tempX= document.getElementById("temp");
var geoLocal=document.getElementById("geo");
var xLat= document.getElementById("lat");
// Following functions will get the latitude and longitude
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(showPosition);
} else {
geoLocal.innerHTML = "Geolocation is not supported by this
browser.";
}
}
var finalLat=finalCoords[0];
var finalLon=finalCoords[1];
function showPosition(position){
geoLocal.innerHTML="Latitude:" + position.coords.latitude +
"<br> Longitude: " + position.coords.longitude;
lat= position.coords.latitude;
lon=position.coords.longitude;
var finalCoords=[lat,lon]
return finalCoords;
}
showPosition(position);
console.log(api,lon);
xLat.innerHTML="testLat: " + finalCoords[0];
finalLat=finalCoords[0];
finalLon=finalCoords[1];
function getWeather(finalLat,finalLon){
var completeApi=api+lon+"&"+lat;
// lon="lon="+ position.coords.longitude;
// lat='lat='+ position.coords.latitude;
xLat.innerHTML="testLatitude: " +completeApi;
return completeApi;
}
getWeather(finalLat,finalLon);
The commented information below is the additional work I am going to be using to complete outputting the weather for any user location.
// var completeApi="getWeather(lat,lon)";
// JSON request for API to get temperature
// var ourRequest= new XMLHttpRequest();
// ourRequest.open('GET',completeApi);
// ourRequest.onload= function() {
// if (ourRequest.status>= 200 && ourRequest.status<400){
// var ourData= JSON.parse(ourRequest.responseText);
// renderHTML(ourData);
// console.log("ourData",ourData);
// } else{
// console.log("Connection Error, Please try again.")
// }
// };
// ourRequest.send();
// console.log(ourRequest)
// var jsonWeather= JSON.stringify(ourData);
// document.body.innerHTML=jsonWeather;
// function renderHTML(data){
// var htmlString="";
// for (i=0;i<data.lenth;i++){
// htmlString=data[i].coord;
// console.log(data[i].coord)
// tempX.textContent= data;
// }
// // htmlString.textContent=data[0];
// tempX.textContent= data;
// // return data;
// }
// console.log(ourRequest)
// var geoLocation= document.getElementById("geoAPI");
// geoLocation.innerHTML=completeApi;
The geolocation API is an asynchronous operation. It would be best to implement this usecase using Promises (if available ) or the callback pattern.
Basically the following asynchronous operations need to happen in order:
Get the current location using geolocation API
Get the weather data using an AJAX request
Make whatever DOM updates necessary to show the results
Sample Implementation using the callback pattern:
// Helper to get the location from the browser
function getLocation(cb) {
cb = (cb && typeof cb === 'function' && cb) || function() {};
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(pos) {
cb(null, [pos.coords.latitude, pos.coords.longitude]);
});
} else {
cb(new Error('Browser does not support geolocation'));
}
}
// Helper to make the AJAX call to the API
function getWeather(coords, cb) {
cb = (cb && typeof cb === 'function' && cb) || function() {};
// Build URL
var url = 'https://fcc-weather-api.glitch.me/api/current?lat=' + coords[0] + '&lon=' + coords[1];
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
// TODO: Handle error cases
if (this.readyState == 4 && this.status == 200) {
// TODO: Handle possible parse exception
cb(null, JSON.parse(xhttp.responseText));
}
};
xhttp.open("GET", url, true);
xhttp.send();
}
// This gets triggered on clicking the button
function handleClick() {
//1. Get the current location using geolocation API
getLocation(function(err, coords) {
if (err) {
// Handle error, return early
return;
}
// 2. Get the weather data using an AJAX request
getWeather(coords, function(err, data) {
if (err) {
// Handle error, return early
return;
}
// Data is available here. Update DOM/process as needed
// 3. Make whatever DOM updates necessary to show the results
console.log(data);
});
});
}
<button onclick="handleClick()">Get Weather Data</button>

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 can I add an alert when Geolocation Services are denied?

It had worked previously in my code, however with the recent changes I made it seems to be broken. I tried looking online for solutions, however this seems specific to me in terms of what I need. All i want is for an "alert();" to be showen if the browser Geolocation Services request is denied. Here is the code without the alert added in. Thank you.
if (navigator.geolocation) {
// Locate position
navigator.geolocation.getCurrentPosition(displayPosition);
} else {
alert('It seems like Geolocation, which is required for this page, is not enabled in your browser. Please use a browser which supports it.');
}
blacklisted_areas = {
'area 51': [1, 2],
'pink unicorn zoo': [1, 2],
};
// Success callback function
function displayPosition(pos) {
var mylat = pos.coords.latitude;
var mylong = pos.coords.longitude;
var thediv = document.getElementById('locationinfo');
thediv.innerHTML = '<p>Your longitude is :' + mylong + ' and your latitide is ' + mylat + '</p>';
var blacklisted = false;
for (let x of Object.values(blacklisted_areas)) {
if (mylat === x[0] && mylong === x[1]) {
blacklisted = true;
}
}
if(!blacklisted){
window.location="insertURLHere";
}
}
Just handle error case:
navigator.geolocation.getCurrentPosition(displayPosition, onError);
function onError (error) {
if (error.code == error.PERMISSION_DENIED)
alert("you denied geolocation");
};
Pass a callback for on error.
if (navigator.geolocation) {
// Get user's geo position.
navigator.geolocation.getCurrentPosition(function success(position) {
// Success
}, function error(err) {
// Geolcoation denied.
if (err.code === err.PERMISSION_DENIED) {
// Determine reason for error.
if (err.message.match(/secure/i)) {
console.alert('INSECURE_ORIGIN');
} else {
console.alert('INSECURE_ORIGIN');
}
}
});
}

How to add a callback to phantomJS onLoadFinished

I'm trying to automate the navigation of some web pages with phantomJS.
What i'm trying to create is a pattern for testing and navigation, so far i got this.
For a moment ignore all the potential null pointers due to empty arrays and such :)
testSuite.js
var webPage = require('webpage');
// Test suite definition
function testSuite(name){
this.name=name;
this.startDate=new Date();
this.tests=[];
this.add=function(test){
this.tests.push(test);
};
this.start=function(){
console.log("Test Suite ["+this.name+"] - Start");
this.next();
},
this.next=function(){
console.log("neeext");
console.log(this.tests.length);
var test=this.tests[0];
this.tests.splice(0,1);
console.log("Test ["+ test.name+"]");
test.execute();
};
}
//Test definition
function test(name,testFunction){
this.name=name;
this.execute=testFunction;
}
module.exports.testSuite=testSuite;
module.exports.test=test;
FirstPageModule.js
var currentPage;
function onPageLoadFinished(status) {
var url = currentPage.url;
var filename='snapshot.png';
console.log("---------------------------------------------------------------");
console.log("Status: " + status);
console.log("Loaded: " + url);
console.log("Render filename:" + filename);
console.log("---------------------------------------------------------------");
if(status == 'success'){
currentPage.render(filename);
}
if(status=='fail'){
console.log("Status: " + status);
}
}
function open(){
currentPage.open("http://localhost:8080");
}
function login(){
var username="topSecretUsername";
var password="topSecretPassord";
currentPage.includeJs("http://ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.min.js");
currentPage.evaluate(function(user,pass) {
$("#user").val(user);
$("#pass").val(pass);
},username,password);
currentPage.render("page.png");
currentPage.evaluate(function(){
$('#loginButton').click();
});
}
function FirstPage(){
var page = webPage.create();
currentPage=page;
this.testSuite = new testSuite("FirstPageModule");
this.testSuite.add(new test("Open First Page",open));
this.testSuite.add(new test("Login",login));
var onLoadFinished=onPageLoadFinished;
var callNextTest=this.testSuite.next;
currentPage.onLoadFinished=function(status){
onLoadFinished.apply(this,arguments);
callNextTest();
};
page.onConsoleMessage = function(msg) {
console.log(msg);
}
}
module.exports=new FirstPage();
PageTests.js
var firstPage=require('./FirstPageModule.js');
firstPage.testSuite.start();
What i want to do is to have a sequential execution of isolated functions, after each function gets executed, i take a screenshot and call the next function.
But, for some reason, the next method on the testSuite isn't getting called, or the method on the second test isn't getting executed.
What am i doing wrong?
Just make available the logName variable in the "global" scope :
var logName;
function onPageLoadComplete(status){
console.log(status);
// Call the logName function
if(typeof(logName) == "function"){
logName();
}
}
function test(){
var page = webPage.create();
this.name="TestName";
// Update logName value as a function.
logName = function(){
console.log(this.name);
}
page.onLoadFinished = onPageLoadComplete;
}
Primary, it doesn't seems to be related to phantomjs but only plain javascript, i hope that's what you need, otherwise please be more specific with your question.
You can create your own page.get implementation with a callback when a page is fully loaded.
ex: create a file module pageSupport.js
// attach listeners
Object.prototype.listeners = {};
// store and fire listeners
Object.prototype.addEventListener = function(event, callback) {
if (!this.listeners[event]) this.listeners[event] = [];
this.listeners[event].push(callback);
this[event] = function(e) {
if (listeners[event]) {
listeners[event].forEach(function(listener) {
listener.call(null, e);
});
}
}
}
// create a new reference to webpage.open method
Object.prototype._open = Object.open;
// receive an url and
// return a function success that will be called when page is loaded.
Object.prototype.get = function(url) {
return {
success : function(callback) {
this.open(url);
this.addEventListener('onLoadFinished', function(status) {
if (status == 'success') {
return callback(status);
}
});
}.bind(this)
}
}
// export as phantomjs module.
exports.Object = Object;
So you can call this module in your script and uses it as follows:
var page = require('webpage').create();
require('./pageSupport');
page.get('http://stackoverflow.com').success(function(status) {
// Now this callback will be called only when the page is fully loaded \o/
console.log(status); // logs success
});

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

Categories