I'm trying to make a function that takes in a users location and then loops through a JSON file of station locations to determine which is the closest station. The issue I am having is with how to include both the location object and the JSON file as arguments in the function.
I am getting the location by using:
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(findNearestStation);
} else {
return "James Street";
}
}
I then want to use the function findNearestStation to take in a JSON as an argument and use the location passed by getLocation to find the nearest station. Something like this:
function findNearestStation(position, json) {
var UserLat = position.coords.latitude;
var UserLong = position.coords.longitude;
for (var i = 0; i < json.stations.length; i++) {
compare and find the min distance...
}
}
Any help would be hugely appreciated. Thanks.
Try an anonymous function:
function getLocation() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function(pos) {
findNearestStation(pos, json);
});
} else {
return "James Street";
}
}
Related
I was testing geolocation API and found if I refresh my page, the page keeps on asking for permission, so I saved my coordinate data to local storage but it doesn't works! Is there any way to give permission only once???
const COORDINATION = "coords";
function saveCords(coordsOBJ){
localStorage.setItem(COORDINATION,JSON.stringify(coordsOBJ));
}
function handleGeoError(position){
console.log("Cant find position");
}
function handleGeoSuccess(position){
// console.log(position);
const latitude = position.coords.latitude;
console.log(latitude);
const longitude = position.coords.longitude;
const coordsOBJ = {
latitude,//latitude = latitude,
longitude//longitude = longitude
}
saveCords(coordsOBJ);
}
function askForCoords(){
navigator.geolocation.getCurrentPosition(handleGeoSuccess,handleGeoError);
}
function loadCoordinate(){
const loadedCords = localStorage.getItem("COORDINATION");
if(loadedCords === null)
{
askForCoords();
}
}
function init(){
loadCoordinate();
}
It looks like there is a typo in your code, whereby you've added quotes to COORDINATION but it's a varible not a string.
Try changing:
const loadedCords = localStorage.getItem("COORDINATION");
To:
const loadedCords = localStorage.getItem(COORDINATION);
I am trying to get position coordinate variables using the standard Navigator.geolocation property with jquery, so i can use the value later in my code:
$(document).ready(function(){
$.getlocation = function(){
navigator.geolocation.getCurrentPosition($.getPosition,$.error);
}
$.getVariables = function(lat,lon){
this.lat = lat; // i want these to be visible
this.lon = lon;
}
$.getPosition= function(position){
console.log("latitude:" +position.coords.latitude+ " longitude: "+position.coords.longitude);
//this function will be executed once position is determined.
$.getVariables(position.coords.latitude,position.coords.longitude);
}
$.error = function(){alert("error");}
$.getlocation(); // outputs correctly
setTimeout(()=>{console.log(this.lat)},5000); // undefined
});
I expect to get location output but instead i get undefined from console.log(this.lat), i did try this in vanilla javascript and it works fine, here is the javascript code:
function locateMe() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(getPosition, error);
} else {
alert("connection problem");
}
}
let vars = function(lat, lon) {
this.lat = lat;
this.lon = lon;
}
let getPosition = function(position) {
vars(position.coords.latitude, position.coords.loongitude);
}
let error = function(msg) {
console.log("problem");
}
locateMe();
setTimeout(() => { console.log(this.lat); }, 5000); //correct output
I could get this.lat working if I changed getVariables to:
$.getVariables = function(lat,lon){
document.lat = lat;
document.lon = lon;
}
It appears that the two this objects may refer to different things in the two methods.
FYI Trial 1 does not work, but Trial 2 works.
I understand that getJSON is executes asynchronously, but I don't actually understand how it applies to the code I've written.
What can I learn about asynchronous execution from this?
Why do I have to separate the getJSON call into a function separate from ready()?
FOR TRIAL 2:
How can I write this code so I don't have to initialize functions inside of getJSON? If there isn't a way, how can I write this code to be more robust?
/*
//TRIAL 1
$(document).ready(function(){
console.log("loaded");
$.getJSON(url, function(json){
var fahrenheit = true;
getLocation(json);
getTemperature(fahrenheit, json);
$("#unit").on("click", function(){
fahrenheit = !fahrenheit;
getTemperature(fahrenheit, json);
});
getWeather(json);
});
});
//Gets current weather conditions from current_observation
function getWeather(json){
var currWeather = "";
var iconURL = "";
currWeather=json.current_observation.weather;
iconURL=json.current_observation.icon_url;
$("#icon").attr("src", iconURL);
$("#weather").html(currWeather);
};
//Gets current temperature from current_observation
function getTemperature(fahrenheit, json){
var currTemp = 0;
if(fahrenheit){
currTemp+=json.current_observation.temp_f;
currTemp+="℉";
} else{
currTemp+=json.current_observation.temp_c;
currTemp+="℃";
}
$("#temperature").html(currTemp);
};
//Gets city, state, country, zip, latitude, and longitude from location
function getLocation(json){
var currLocation=["city", "state", "country", "zip", "lat", "lon"];
var locationHTML = "";
currLocation[0] = json.location.city;
currLocation[1] = json.location.state;
currLocation[2] = json.location.country_name;
currLocation[3] = json.location.zip;
currLocation[4] = json.location.lat;
currLocation[5] = json.location.lon;
locationHTML += currLocation[0]+", "+currLocation[1]+", "+currLocation[2]+" " +currLocation[3]+"<br>";
locationHTML += "Latitude: "+currLocation[4]+"<br>Longitude: "+currLocation[5];
$("#location").html(locationHTML);
};
*/
//TRIAL 2
$(document).ready(function(){
console.log("loaded");
dispWeather();
});
function dispWeather(){
console.log("inside dispWeather");
//Retrieve json from weather underground
var url = "https://api.wunderground.com/api/19c5c96f0b140c0f/geolookup/conditions/q/autoip.json";
$.getJSON(url, function(json){
console.log("Got JSON");
console.log(json);
var fahrenheit = true;
getLocation(json);
getTemperature(fahrenheit, json);
$("#unit").on("click", function(){
fahrenheit = !fahrenheit;
getTemperature(fahrenheit, json);
});
getWeather(json);
//Gets current weather conditions from current_observation
function getWeather(json){
var currWeather = "";
var iconURL = "";
currWeather=json.current_observation.weather;
iconURL=json.current_observation.icon_url;
$("#icon").attr("src", iconURL);
$("#weather").html(currWeather);
};
//Gets current temperature from current_observation
function getTemperature(fahrenheit, json){
var currTemp = 0;
if(fahrenheit){
currTemp+=json.current_observation.temp_f;
currTemp+="℉";
} else{
currTemp+=json.current_observation.temp_c;
currTemp+="℃";
}
$("#temperature").html(currTemp);
};
//Gets city, state, country, zip, latitude, and longitude from location
function getLocation(json){
var currLocation=["city", "state", "country", "zip", "lat", "lon"];
var locationHTML = "";
currLocation[0] = json.location.city;
currLocation[1] = json.location.state;
currLocation[2] = json.location.country_name;
currLocation[3] = json.location.zip;
currLocation[4] = json.location.lat;
currLocation[5] = json.location.lon;
locationHTML += currLocation[0]+", "+currLocation[1]+", "+currLocation[2]+" " +currLocation[3]+"<br>";
locationHTML += "Latitude: "+currLocation[4]+"<br>Longitude: "+currLocation[5];
$("#location").html(locationHTML);
};
})
};
.ready() jQuery Documentation
Specify a function to execute when the DOM is fully loaded.
What can I learn about asynchronous execution from this?
Your learning that you don't know when the document is going to be ready() so we wait until the event completes before beginning execution on our application. You also learned that you have to wait for $.getJSON to fetch json then you process the data.
Why do I have to separate the getJSON call into a function separate from ready()?
As specified above .ready() is waiting for the DOM to be fully loaded, then we start the application. So when we are "ready" lets fetch the weather data. The document is only ready one time when the DOM is fully loaded.
How can I write this code so I don't have to initialize functions inside of getJSON?
Without you being specific, I'm assuming your problem here was with toggling the degrees between celsius and fahrenheit. After we load the weather you can store the data in a variable outside of the scope of the function, this way when you click to change the degrees you can pass in the same data without having to call the api again (although at this point the weather could have changed)
how can I write this code to be more robust?
I've included a JS Bin that alters your code. The biggest problem was bad naming conventions and not keeping things simple. Example getWeather() was not "getting weather" it was setting html from data we got from $.getJSON which was invoked in your ready() instead of breaking it out into another function we could call later on.
For the most part this is how the code reads now, clear function names help quickly see what this code is supposed to do.
$(document).ready(function() {
renderWeather();
});
var state = {
fahrenheit: true,
data: {}
};
function renderWeather() {
getWeatherJSON()
.then(data => {
state.data = data;
setWeatherHTML(data);
setTemperatureHTML(data, state.fahrenheit);
setLocationHTML(data);
})
.catch(err => console.log(err));
}
http://jsbin.com/cekite/edit?js,output
If we wanted to take this a step further we could create a WeatherAPI prototype concealing our html render functions and extend it with a WeatherUndergroudAPI prototype, this way if we ever change our weather service we should only have to implement a format function to marshall the data the way the WeatherAPI expects it to be in.
class WeatherAPI {
constructor(opt) {
...
}
init() {
...
}
get() {
... feteches json from endpoint provided
}
renderWeather() {
this.get()
.then(this.formatter.bind(this))
.then(this.setWeatherData.bind(this))
.then(this.renderWeatherHTML.bind(this))
.then(this.renderTemperatureHTML.bind(this))
.then(this.renderLocationHTML.bind(this))
.catch(err => console.log(err));
}
formatter(data) {
...
}
setWeatherData(data) {
...
}
renderWeatherHTML() {
...
}
renderTemperatureHTML() {
...
}
renderLocationHTML() {
...
}
}
Extending the WeatherAPI is then a matter of passing in a new endpoint to get data from. Or in this case overriding the WeatherAPI get method and returning static data.
class FakeWeatherAPI extends WeatherAPI {
constructor(opt = {}) {
super(opt);
}
get() {
return new Promise((resolve) => {
const data = {
someReallyWeirdKeyForACity: 'San Francisco',
state: 'CA',
country: 'US',
lat: '37.77999878',
lon: '122.41999817',
f: '1000',
c: '25',
icon: 'http://www.nyan.cat/cats/zombie.gif',
weather: 'Acid Rain'
};
resolve(data);
});
}
formatter(data) {
const formattedData = {
city: data.someReallyWeirdKeyForACity,
state: data.state,
country: data.country,
lat: data.lat,
lon: data.lon,
temperature: {
fahrenheit: data.f,
celsius: data.c
},
icon: data.icon,
weather: data.weather
};
return formattedData;
}
}
Our application init code then becomes.
$(document).ready(init);
function init(){
const weatherAwesomeService = new FakeWeatherAPI();
weatherAwesomeService.init();
};
Here is a working jsbin for they above
http://jsbin.com/sicofe/edit?js,output
In my app I am trying to get a Region (city) to store in my Location model along with lngLat and address.
The thing is that for new locations it would be easy as when I would create I would do that. For old locations I wrote this bit of code
function geocodeLatLng(geocoder, latlngStr, callback) {
var latlng = { lat: parseFloat(latlngStr.split(',')[0]), lng: parseFloat(latlngStr.split(',')[1]) };
var city;
geocoder.geocode({ 'location': latlng }, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
var address = results[0].address_components;
for (var p = address.length - 1; p >= 0; p--) {
if (address[p].types.indexOf("locality") != -1) {
console.log(address[p].long_name);
city = address[p].long_name;
callback(city);
}
}
}
});
}
And I am calling it like this
self.getRegion = function () {
for (var i = 0; i < self.rawLocations().length; i++) {
var location = self.rawLocations()[i];
setTimeout(
geocodeLatLng(geocoder, location.systemRepresentation(), function (res) {
}), 250);// End of setTimeOut Function - 250 being a quarter of a second.
}
}
The issue is that I get over_query_limit after 5 calls. I will store the Location it self in the database for now I would have to do this to fix the old locations.
Any headers?
Google maps javascript library has a maximum calls per second as well as an hourly rate, are you trying to geocode at a rate faster than their per second rate possibly?
5 does seem low as their own documents inform users that it is 50 per second (https://developers.google.com/maps/documentation/geocoding/usage-limits)
Also have you signed up for a key and are using it? This could make a difference (if the google account is old as signed up for maps API sometime you can use the system without a key)
I'm working through the process of modulization on an app that I have written. This works with spatial location
I'm using an event to query for the user's lat / lon position for use inside the application. My calling snippet is below (button click starts it up)
<script>
require([
'dojo/dom',
'dojo/_base/array',
'demo/testModule',
'esri/SpatialReference',
'esri/geometry/Point'
], function (
dom,
arrayUtils,
testModule,
SpatialReference,
Point
) {
//Here is the button click listener
$('#whereAmIButton').click(function () {
var spatialRef = new esri.SpatialReference({ 'wkid': 4326 });
//variable I want to set to a returned geometry.
var myGeom;
//This runs but I'm missing the boat on the return of a value
testModule.findUserLocPT(spatialRef);
//var myModule = new testModule(); //not a constructor
});
});
</script>
Here is the custom module. It logs the information to the console for the user's location. But I want to return the value for setting the 'myGeom' variable.
define(['dojo/_base/declare','dojo/_base/lang','dojo/dom',
'esri/geometry/Point','esri/SpatialReference'], function (
declare, lang, dom, Point, SpatialReference) {
return {
findUserLocPT: function (spatialRef) {
var geom;
var location_timeout = setTimeout("geolocFail()", 5000);
navigator.geolocation.getCurrentPosition(function (position) {
clearTimeout(location_timeout);
var lat = position.coords.latitude;
var lon = position.coords.longitude;
setTimeout(function () {
geom = new Point(lon, lat, spatialRef);
//console.log writes out the geom but that isnt what I am after
console.log(geom);
//I want to return this value
return geom;
}, 500);
});
function geolocFail() {
console.log("GeoLocation Failure");
}
}
}//end of the return
});
Any help would be welcome. I can by reference back change textual/html values on the document but am not getting things back as a variable.
Andy
Ok, I don't know if this is the 'best' answer but I have one now.
I added a global variable inside the 'test.html' page
<script>
var theGeom; //This is the variable
require([
'dojo/dom',
here is where I am setting the value of this variable for use in the original dojo 'require' code block. This is coming from the 'testModule.js'
setTimeout(function () {
geom = new Point(lon, lat, spatialRef);
theGeom = geom; //Here is the feedback of the value to the global variable.
return myGeom;
}, 500);
$('#whereAmIButton').click(function () {
var spatialRef = new esri.SpatialReference({'wkid':4326});
testModule.findUserLocPT(spatialRef);
setTimeout(function () {
console.log(theGeom); //here is the value set and ready to use
},2000);
});
I'm not sure if this is the best way. If you have something better please let me know.
Andy