I'm very new to Javascript and ran into a problem I can't resolve/understand myself.
Let's say that I made "UserMap" class with a "userPosition" property and want to get coordinates using Geolocation API.
navigator.geolocation.getCurrentPosition(this.getCoordinates);
Then in the callback function I get latitude and longitude:
var coords = { "lat" : position.coords.latitude, "lng" : position.coords.longitude };
My question is: how can I make this callback function report back to the UserMap instance, so the userPosition property can be updated? Obviously this.userPosition = coords; won't work here. Now I feel pretty much helpless dealing with callbacks. I hope I don't have to make a new object every time I get the updated coords from a user.
Here is the wrong code:
function UserMap() {
this.map = L.map('map', {"zoomControl" : false});
this.userPosition = {};
this.boundary = {};
if (navigator.geolocation){
navigator.geolocation.getCurrentPosition(this.getCoordinates);
}
else{ alert("Geolocation is not supported by your browser.");
return false;
}
this.map.on("click", function(e) {
alert("Lat: " + e.latlng.lat + " Lng: " + e.latlng.lng);
});
this.display();
}
UserMap.prototype.getCoordinates = function(position) {
var coords = { "lat" : position.coords.latitude, "lng" : position.coords.longitude};
this.userPosition = coords; // I understand why this line won't work, but can't find a workaround solution
}
UserMap.prototype.display = function() {
var lat = this.userPosition.lat;
var lng = this.userPosition.lng;
this.map.setView([lat, lng], 18);
var tile = L.tileLayer('http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© OpenStreetMap contributors',
minZoom: '16',
}).addTo(this.map);
L.marker([lat, lng]).addTo(this.map)
.bindPopup('You are here!')
.openPopup();
}
In the callback you can use an ajax call, wich will interact with the server in a nonblocking manner.
If you use jquery you can do something like this:
$.ajax({
url: "test.html",
data: coords
})
.done(function() {
alert("done")
});
You could use the map.locate function and the locationfound event to accomplish this:
Check this fiddle: http://jsfiddle.net/tuchi35/3fTL7/1/ and also check the locate doc: http://leafletjs.com/reference.html#map-locate
Basically, I removed your getCoordinates function and replace it with a call to the locate function and a event handler for the "locationfound" event:
map.locate({setView:true});
map.on('locationfound', function (data){
L.marker(map.getCenter).addTo(map)
.bindPopup('You are here!')
.openPopup();
});
Hope it helps
Related
First time trying to hack together some Javascript here so any resources that will help me understand my problem case is appreciated.
I'm trying to extract the lat and long from the following request to use in another request:
var placeSearch, autocomplete;
var x = document.getElementById("location");
function initAutocomplete() {
// Create the autocomplete object, restricting the search predictions to
// geographical location types.
autocomplete = new google.maps.places.Autocomplete(
document.getElementById('autocomplete'), { types: ['geocode'] });
// Avoid paying for data that you don't need by restricting the set of
// place fields that are returned to just the address components.
autocomplete.setFields(['geometry']);
}
function showPosition() {
x.innerHTML = "Latitude: " + autocomplete.result.geometry.lat +
"<br>Longitude: " + autocomplete.result.geometry.lng;
}
/*
"result" : {
"geometry" : {
"location" : {
"lat" : 51.46588129999999,
"lng" : -0.1413263
}
}
*/
// Bias the autocomplete object to the user's geographical location,
// as supplied by the browser's 'navigator.geolocation' object.
function geolocate() {
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(function (position) {
var geolocation = {
lat: position.coords.latitude,
lng: position.coords.longitude
};
var circle = new google.maps.Circle(
{ center: geolocation, radius: position.coords.accuracy });
autocomplete.setBounds(circle.getBounds());
});
}
}
When a user selects the autocompleted location the google api makes a request to:
https://maps.googleapis.com/maps/api/place/js/PlaceService.GetPlaceDetails on the selected location. I can see this returns my desired data here:
Obviously autocomplete.result.geometry.lat returns a location_search.js:18 Uncaught TypeError: Cannot read property 'geometry' of undefined error so I'm missing some knowledge here.
Thank you for your help.
I've implemented something very similar to your needs in my project recently. It's quite easy but it took me a while to realise how to do it.
Basically, you can simply use the .getPlace() method on the Autocomplete object and go from there. Here's how I got the latitude and longitude:
let locationInfo = autocomplete.getPlace().geometry.location;
let latitude = locationInfo.lat();
let longitude = locationInfo.lng();
In your specific case you should change your showPositions function to
function showPosition() {
x.innerHTML = "Latitude: " + autocomplete.getPlace().geometry.location.lat +
"<br>Longitude: " + autocomplete.getPlace().geometry.location.lng;
}
Does this do the trick?
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
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.
I have a serious problem that i have been trying to debug for a few days already.
I have a script that gets users current latitude and longitude, and then stores them in variables. however, when i try to use these variables outside this function and in the //init map zone, the map is just not showing up. by alerting out the variables i can see that outside the position function variables are set to "Undefined". here is my code:
//main function here
function initialize() {
var lat;
var lon;
//check if user has geo feature
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(
//get position
function(position){
lat = position.coords.latitude;
lon = position.coords.longitude;
},
// if there was an error
function(error){
alert('ouch');
});
}
//case the users browser doesn't support geolocations
else {
alert("Your browser doesn't support geolocations, please consider downloading Google Chrome");
}
//init map
var myOptions = {
center: new google.maps.LatLng(lat, lon),
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
}
Thanks for any help, Ariel
That is because you declare your variables within the function. The variables gets private to the initialize function and can only be accessed from within it. If you need to be able to access your variables outside of the initialize function, then move the variable declaration out of the function.
var lat;
var lon;
function initialize() {
...
Have a look at this MDN article about variable scope in JavaScript.
UPDATE
Looking through the code again I realize that the problem isn't variable scope, got confused by the indentation. I'm not familiar with the Geolocation API but I believe that the problem might be that the navigator.geolocation.getCurrentPosition() is asynchronous, as it will have to wait for the user to allow the website to get the position of the device. Therefor myOptions will have been assigned before the actual position have been retrieved - thus lat & lng are still undefined when myOptions is assigned.
Try this instead:
//main function here
function initialize() {
var lat, lon, map, myOptions;
//check if user has geo feature
if(navigator.geolocation){
navigator.geolocation.getCurrentPosition(
//get position
function(position){
lat = position.coords.latitude;
lon = position.coords.longitude;
//init map
myOptions = {
center: new google.maps.LatLng(lat, lon),
zoom: 16,
mapTypeId: google.maps.MapTypeId.ROADMAP
};
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
},
// if there was an error
function(error){
alert('ouch');
});
}
//case the users browser doesn't support geolocations
else {
alert("Your browser doesn't support geolocations, please consider downloading Google Chrome");
}
}
More of a correction on the other answer. The issue of scope is not relevant. As defined in the original code lat and lon are in scope in the context to which the question author is using alert.
Here's a runnable example that proves it.
function getPos( f ) {
var position = new Object();
position.coords = new Object();
position.coords.latitude = 5;
position.coords.longitude = 10;
f( position );
}
function initialize() {
var lat;
var lon;
getPos(
function(position){
lat = position.coords.latitude;
lon = position.coords.longitude;
}
);
alert( lat + " " + lon );
}
initialize(); //expected 5 & 10
Anyways, this doesn't seem to be a pure JS issue. This seems to be an issue with whatever google api you are using. This question should have been tagged with such, as I have no knowledge of this API or whether you're calling it wrong or not.
I am having issues passing two coordinates from one function to another. I don't really know JavaScript, but it seems to be somewhat correct. Could you please let me know where is my error?
<script type="text/javascript">
function initialize() {
var address = "Chicago IL";
locs= getLatLong(address);
alert(locs.lat()); //does not work
var myOptions = {
zoom: 4,
center: new google.maps.LatLng(41, -87),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map_canvas'),
myOptions);
}
function getLatLong(address){
var geo = new google.maps.Geocoder;
geo.geocode({'address':address},function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
locations[0] = results[0].geometry.location.lat();
locations[1] = results[0].geometry.location.lng();
//alert(locations[0]); //test is good- works! Need to pass it back to function
//alert(locations[1]); //test is good- works! Need to pass it back to function
locs =results[0].geometry.location;
return locs;
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
</script>
It is not possible to return a value from an asynchronous function. Do not try it.
Use a callback instead.
function setLocationOnMap(locs) {
alert(locs.lat()); // works now!
var myOptions = {
zoom: 4,
center: new google.maps.LatLng(41, -87),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById('map_canvas'), myOptions);
}
function initialize() {
var address = "Chicago IL";
getLatLong(address, setLocationOnMap);
}
function getLatLong(address, callback){
var geo = new google.maps.Geocoder;
geo.geocode({'address':address},function(results, status){
if (status == google.maps.GeocoderStatus.OK) {
// processing...
locs = results[0].geometry.location;
callback(locs);
} else {
alert("Geocode was not successful for the following reason: " + status);
}
});
}
You must understand that asynchronous function calls like geo.geocode() return immediately, i.e. before any result is ready. That's why you can't use return to return a value from them - they don't have it yet.
Once the result (most often, an HTTP request) is ready, the asynchronous function relies on a callback function to handle it. You must do all your further processing in that callback function, either directly or by making another function call.