Javascript - returning a variable is failing - javascript

I'm trying to do something that should be pretty simple - but it's doing my head in.
I can't seem to get a variable to return from a function
var where;
if (navigator.geolocation) {
where = navigator.geolocation.getCurrentPosition(function (position) {
// okay we have a placement - BUT only show it if the accuracy is lower than 500 mtrs
//if (position.coords.accuracy <= 500 ) {
// put their lat lng into google
var meLatLng = new google.maps.LatLng(position.coords.latitude, position.coords.longitude);
var image = "images/map_blue.png";
var myMarker = new google.maps.Marker({
position: meLatLng,
map: map,
title: "Your location, as provided by your browser",
icon: image,
zIndex: 50
});
//} // end 500 mtrs test
return meLatLng;
});
alert("A: "+where);
}
I have a global variable called where, I'm trying to fill it with LatLng information received from the browser using navigator.geolocation - it plots the user on the map - but alert(where) always returns undefined
What am I doing wrong?

Short answer, nothing,
Bit longer answer, well... you see, the problem is that it would appear that your anonymous function returns its value to the getCurrentPosition function, which it seems doesnt return anything at all.
In order to use your returned value you should instead use a callBack or handle the data where it is...
Simply try replacing return meLatLng with customCallBack(meLatLng) and then defined customCallBack somewhere along the line.
Or, since you have already defined where in the global scope you could try and replace return meLatLng with where = meLatLng and then remove where = from where = navigatior.geoloc....
If what I suspect is right, the last method wont work, since I believe getCurrentPosition is asynchronous, meaning that you leave the standard flow of the application as soon as you call it. Which is why you supply a function to it in the first place.

Related

Recenter Mapbox map using user coords

Been trying for 1.5 hours. Can someone please help me out? "your post appears to contain code that is not properly formatted as code" issue.
Please can someone help me format this code?
It works now in this post, but not when making a new one...
Now I cannot make a new post for a day... What the...
FIXED: It appears that the browser output had to be in code layout.
This was my original question:
TITLE: Working with geolocation API - scope question
Hi all
I am working with Mapbox and am trying to update the vars lngreal and latreal (from default Greenwhich coordinates). The function initializeMap() is called during setup, and the idea is that it updates it with user's longlat coordinates if possible. When I run the code below...
function initializeMap() {
mapboxgl.accessToken = 'HIDDEN FOR THIS POST';
var lngreal = 0.0098;
var latreal = 51.4934;
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(displayLocationInfo);
}
function displayLocationInfo(position) {
//Why does this not work to update the map zoom?
lngreal = position.coords.longitude;
latreal = position.coords.latitude;
console.log(lngreal);
console.log(latreal);
}
console.log(lngreal);
console.log(latreal);
// This adds the map to your page
var map = new mapboxgl.Map({
// container id specified in the HTML
container: 'map',
// style URL
style: 'mapbox://styles/mapbox/light-v10',
// initial position in [lon, lat] format
center: [lngreal, latreal],
// initial zoom
zoom: 14
});
// More code after this but not related
}
It shows the following in html inspector console:
play-mapboxscripts.js:21 0.0098
play-mapboxscripts.js:22 51.4934
play-mapboxscripts.js:17 4.9212796
play-mapboxscripts.js:18 52.333704999999995
Why does it first show the last console.log lines, instead of the console.log lines in the displayLocationInfo function? Isn't that function called in the navigator.geolocation function, which comes before that?
I cannot get the lngreal and latreal variables updated correctly to match the user's location. They will default back to 0.0098 and 51.4934, and the loaded map (var map) will show the default Greenwhich location. Does this have to do with variable scope of the displayLocationInfo function?
It shows the last two console logs first because displayLocationInfo is the callback to the async method getCurrentPosition and so has to wait until that process has resolved before it can log the new coords.
To recenter the map with the new coords you need to do something like this:
function displayLocationInfo(position) {
const { coords: { latitude, longitude } } = position;
// Get a new lat/lng object
// https://docs.mapbox.com/mapbox-gl-js/api/#lnglatlike
const center = new mapboxgl.LngLat(longitude, latitude);
// Center the map
// https://docs.mapbox.com/mapbox-gl-js/api/#map#setcenter
map.setCenter(center);
}
Thanks Andy and #Chris G. I wasn't aware of asynchronous functions and am happy to have read about them now. Very helpful.
I have a question about callbacks, which relates to this topic. How does a function know that something is a callback; something that needs to be executed once certain I/O has completed. How does it know that it shouldn't execute right away? Is it defined in a function in a (standardized) way?
As far as I know the 'callback' keyword that is often used in an argument is just common practise, but does not automatically let the function interpret the argument as something that should start once certain I/O has completed.
Taking the below example, I have three questions (taken from https://medium.com/codebuddies/getting-to-know-asynchronous-javascript-callbacks-promises-and-async-await-17e0673281ee):
const request = require(‘request’);
function handleResponse(error, response, body){
if(error){
// Handle error.
}
else {
// Successful, do something with the result.
}
}
request('https://www.somepage.com', handleResponse);
What does the structure of the 'require' function look like so that it knows that argument 2 (handleResponse in this case) should be executed once the request has completed? I guess this gets down to the same question that I asked above.
Can functions be asynchronous even without the async keyword in the function? If yes, how does the browser know it's an asynchronous function?
In my code, is it possible to have the browser/script wait with execution of remaining code until the navigator.geolocation.getCurrentPosition has finished executing and therefore updated the lngreal latreal coords?

Here API clustering error: "addLayer is not defined"

I am try to use clastering in my aplication but I am get a error addLayer is not defined".
I have no Idea how to resolve this issue.
I just copy and paste this sample function from Here API samples api clusters
And I'm passing lat and lng, to the function but addLayer is undefined.
I have the map object, but the addLayer is undefined.
function startClustering(map, data) {
// First we need to create an array of DataPoint objects,
// for the ClusterProvider
var dataPoints = data.map(function(item) {
return new H.clustering.DataPoint(item.latitude, item.longitude);
});
// Create a clustering provider with custom options for clusterizing the input
var clusteredDataProvider = new H.clustering.Provider(dataPoints, {
clusteringOptions: {
// Maximum radius of the neighbourhood
eps: 32,
// minimum weight of points required to form a cluster
minWeight: 2
}
});
// Create a layer tha will consume objects from our clustering provider
var clusteringLayer = new H.map.layer.ObjectLayer(clusteredDataProvider);
// To make objects from clustering provder visible,
// we need to add our layer to the map
map.addLayer(clusteringLayer);
}
And I'm passing lat and lng, to the function but addLayer is undefined.
I have the map object, bust does not exist addLayer.
I'm embed the script
<script type="text/javascript" src="https://js.api.here.com/v3/3.0/mapsjs-clustering.js"></script>
on my index.
I don't know how to resolve this issue.
If someone knows how to resolve I'm glad to listen
EDIT: Additional code per comment request:
function addMarkerToCameraGroup(map, coordinate, html) { // add and remove markers
document.querySelector(".camera-box").addEventListener("click", function() {
if (document.getElementsByClassName("camera-marker-position")[0] === undefined) {
map.addObject(cameraMarker);
self.startClustering(map, coordinate);
} else {
map.removeAll();
}
});
}
You asked this question a second time as: "Try to create a cluster unsing a sample but addLayer got undefinied" which doesn't include some of the details in this question. Given the additional edit / context you made on this question however, it would seem you are adding an event listener in which map is probably out of scope or not yet defined for visibility inside the anonymous function.
document.querySelector(".camera-box").addEventListener("click", function() {
if (document.getElementsByClassName("camera-marker-position")[0] === undefined) {
map.addObject(cameraMarker);
self.startClustering(map, coordinate);
} else {
map.removeAll();
}
});
You could do a typing check to see if map has been defined when the click event occurs and/or confirm whether it is an H.map. Without seeing the full listing, it may also be the case that you have not made map a global variable but instead declared it somewhere else in the page initialization so is out of scope when the click event is fired.
You can check JavaScript callback scope for details on closures where you could provide the map to the event when declaring the function. For more general information: JavaScript HTML DOM EventListener

Altering external variable from Google Maps' StreetViewService.getPanorama({},callback) method

I am running into trouble with this piece of code since I'm not sure how to fix it. I already asked this in the chats but couldn't figure out myself after some answers.
I want to get Panorama from StreetViewService using the method from the Google Maps javascript API from a StreetViewService getPanorama() method.
The method receives a literal with the coordinates and a radius, and a callback function that receives 2 parameters: data and status.
in that callback you check wether the service returns some images for street view or not, in which case you do one thing or another.
It seems the callback is executed asynchronously or the getPanorama method, executing some kind of ajax behind the scenes.
I'm pasting the code below, but first I explain my intentions. I need to return from one method I made inside a literal that is inside a self made library wether the the request has valid images for that requested coordinates or not by setting a variable to true or false and then at the end returning that value. However, even if I set the variable to true inside that callback anonymous function, when the variable returns, it always has it's initial value without it not being changed.
Here the code. Not everything, just the essential code.
Then my intention is to used the boolean returned to know if I have to switch one button active for some kind of job or not or do some things or not depending of if it returned true or false, Change some style etc as well.
I'd appreciate if you could change my code in a way it could be done. I was told about a callback solution or wrapping it into a promise. However I don't know how to do it. I used promisses in jquery but not in vanilla javascript. I'd like to see how the callback solution could be made as well with this code.
//Library not show for shortenning the example.
streetView: { //This is inside a library
valid_request: false,
event_key: null,
panorama: null,
setStreetView: function(coords, element) {
libMapa.streetView.valid_request = false; // Initialize the value again.
let sv_service = new google.maps.StreetViewService();
var latlng = coords;
sv_service.getPanorama({ // This is a method from Google Map Javascript API.
location: latlng,
radius: 50
}, function(data, status) {
if (status === google.maps.StreetViewStatus.OK) {
if (!libMapa.streetView.panorama) {
libMapa.streetView.panorama = new google.maps.StreetViewPanorama(element);
libMapa.streetView.panorama.setEnableCloseButton(true);
}
libMapa.streetView.panorama.setPano(null);
libMapa.streetView.panorama.setPano(data.location.pano);
libMapa.streetView.panorama.setVisible(true);
libMapa.streetView.valid_request = true;
} else {
alert("No images for this place");
// Otherwise variable stays as FALSE.
}
});
return libMapa.streetView.valid_request;
}
}
/****************************************/
//OUTSIDE THE LIBRARY IN INDEX.HTML
var sv_valid = libMapa.streetView.setStreetView(coords, div_mapa);
//sv_valid still shows false even if it should return true.
if (sv_valid) {
// "pressed" is to control a button as if it was a switch by activatinc and deactivating it.
pressed = false; // It always ends up being false even when it should return true.
element.style.cursor = "default";
libMapa.mapa.unByKey(libMapa.streetView.event_key);
}
Use a callback to do some action or modify some variable after the AJAX call to getPanorama() (and by proxy, setStreetView()) is complete.
Modify setStreetView() to accept a callback parameter and pass it along to getPanorama().
streetView {
// streetView variables etc.
setStreetView: function(coords, element, callback) {
// get ready for the AJAX call etc.
sv_service.getPanorama({
// parameter object
}, function(data, status){
// The getPanorama() callback.
// Do stuff with the data/status here,
// then call the callback function that you passed in to setStreetView.
// You can send data along (e.g. the result of the AJAX call) to the callback.
callback(someResult);
});
}
}
// somewhere else, where you're calling streetView.setStreetView()
var someVar = "foo";
streetView.setStreetView(someCoords, someElement, function(eventualResult){
someVar = eventualResult;
});
Here's a small example: https://jsfiddle.net/_jered/pgftxgf3/
I also highly suggest you do some research on AJAX, asynchronous JavaScript, and callbacks.

Showing Google Maps Marker infoname on Mouseover of a Dynamically Generated DIV

I want to introduce a functionality which allows a marker's infoname to appear or disappear upon mouseover or mouseout of a corresponding DIV element generated from jQuery. However, I am getting a "a is undefined" error on line 19 of main.js. After extensive testing on my script, I realise that this has something to do with the marker in the newly added lines as commented below:
function addMarker(A) {
var point = new google.maps.LatLng(A.lat, A.lng);
var image = new google.maps.MarkerImage('images/r.png',
new google.maps.Size(30, 30),
new google.maps.Point(0, 0),
new google.maps.Point(0, 30));
marker = new google.maps.Marker({
map: map,
position: point,
icon: image,
});
}
function addInfoName(A) {
var infoname = new infoName; // custom object
google.maps.event.addListener(marker, 'mouseover', function(event) {infoname.show();});
google.maps.event.addListener(marker, 'mouseout', function(event) {infoname.hide();});
infoname.open(map, marker);
}
function showResult(A) {
$('#results').append('<DIV id=' + A.pid + '>{Blah Blah Blah}</DIV>');
return document.getElementById(A.pid);
}
function process(json) {
$('#results').empty();
total = json.details.length;
for(i=0; i<total; i++) {
var detail = json.details[i];
var marker;
addMarker(detail);
addInfoName(detail);
// these new lines are added
var listDisplay = showResult(detail);
listDisplay.onmouseover = function(){google.maps.event.trigger(marker, 'mouseover');};
listDisplay.onmouseout = function(){google.maps.event.trigger(marker, 'mouseout');};
}
}
google.maps.event.addListener(map, 'idle', function () {$.getJSON(query, process);});
The error disappears if I merge the function addInfoName into process. However, all the DIVs will point to the last marker if I do so. My question is, how do I modify my script to achieve the functionality mentioned above?
Currently you've got a variable marker declared local to the process function, but you're trying to read and write to it from other functions. In particular, addMarker writes to marker without var, which causes an accidental global variable to be created. Meanwhile process is not actually writing to the marker local it has declared, so it contains undefined, which will trip the Google Maps code up when you pass that in.
(Tools like jslint, or ECMAScript 5 Strict Mode can catch accidental globals for you. Note total and i are also accidental globals.)
It looks like addMarker and addInfoname have been hacked out of the body of process without tying up the variables from process that both of them used. If they were included in the body of process it would work, but you'd get the described behaviour where the same marker value was used for every div because of the Closure Loop Problem.
This problem occurs in languages with closures and function-level scope, which includes JavaScript, Python and others. In these languages any variables defined by or inside a for loop are local to the containing function, not reallocated every time you go around the loop. So if you make a closure referring to i in the first iteration of the loop, it's the same variable i as you refer to in the second iteration of the loop; every instance of the function has a closure over the same variable i so every function will see the same value. The same would be true of marker.
The Closure Loop Problem can be avoided through use of a second closure that keeps the loop variable in an argument, or, more cleanly, using a closure-based loop mechanism instead of the C-like for loop. ECMAScript 5 offers array.forEach() for this purpose and jQuery offers $.each():
function process(json) {
$('#results').empty();
var gev= google.maps.event;
$.each(json.details, function(detaili, detail) {
var marker= addMarker(detail.lat, detail.lng);
$('#results').append($('<div>', {
text: detail.name,
mouseover: function() { gev.trigger(marker, 'mouseover'); },
mouseout: function() { gev.trigger(marker, 'mouseout'); }
}));
var infoname= new InfoName();
gev.addListener(marker, 'mouseover', function() { infoname.show(); });
gev.addListener(marker, 'mouseout', function() { infoname.hide(); });
infoname.open(map, marker);
});
}
function addMarker(lat, lng) {
return new google.maps.Marker({
map: map,
position: new google.maps.LatLng(lat, lng),
icon: new google.maps.MarkerImage(
'images/r.png',
new google.maps.Size(30, 30),
new google.maps.Point(0, 0),
new google.maps.Point(0, 30)
)
});
}
The "a is undefined" error is probably because you are trying to create the map before the dom is ready. At least, that's the only time I have seen it. I can't tell from your code where you're creating it, but make sure that the map div is ready. You either have to place the call to your initialize function at the bottom of the page or in an page load listener. Here's one way you can do that (this can go anywhere in the page):
function initialize() {
var map = new google.maps.Map(document.getElementById("map_canvas"), {
zoom: 6,
mapTypeId: google.maps.MapTypeId.ROADMAP
});
google.maps.event.addListener(map, 'idle', function () {
$.getJSON(query, process);
});
}
google.maps.event.addDomListener(window, 'load', initialize);
Also notice that your idle listener goes in that init function, too, since it won't be able to run until the map has been created.
If that's not causing the "a is undefined" error, then I can't see it in the code you've posted. I do, however, see some other problems with your code. So maybe that is actually what's causing it. First, the var marker; definition in process doesn't do anything. Here you are creating a local variable, but that local variable never gets defined. Then addMarker you are creating a global variable by defining marker without the var. So the marker in addInfoname always refers to the global marker, which will always be the last marker defined. So that's why the divs are always showing up with the last marker. I would put a return before marker = ... in addMarker, and use it to set the marker variable like this:
var marker = addMarker(detail);
In process, of course. Then you also have to send this to addInfoname as a parameter so it gets the correct one.

Set data inside getCurrentPosition function

I'm doing next:
First thing - I'm checking if I already have loaded latitude and longitude data. If I do have, I'm saving that into array named "location".
Second thing - if I don't have any loaded data, I'm trying to get current position. After that, I'm trying to save latitude and longitude for current position.
And, at the end, I'm calling setMap(); function where I check location array and where I'm generating a map.
Problem:
Well, as I said it...inside "getCurrentPosition", I'm trying to set current position into "location" array, and after that I'm trying to take those values inside setMap(); function. Here's the problem, but only when i set "location" values inside "getCurrentPosition". If I set "location" values manually before (from "data" array), everything works fine.
Also, when I call a setMap() function inside "getCurrentPosition", everything works fine...but when I call it from outside, won't work.
Can someone explain me what's going on and what can I do?
Here's my code:
location = new Array();
if (data.lat) {
location['lat'] = data.lat;
location['lng'] = data.lng;
} else {
if (!navigator.geolocation) {
//
} else {
navigator.geolocation.getCurrentPosition(function(position) {
location['lat'] = position.coords.latitude;
location['lng'] = position.coords.longitude;
});
}
}
setMap();
Thank you.
You are setting a different variable inside of getCurrentPosition.
I would suggest researching variable scope.
Check out this question.
--edit--
I did not realize that the function you were using was a part of Javascript, I thought you had written/gotten it from somewhere.
Also, I stand corrected about the scope. Since you are using an array, the scope is fine, you should be able to set it inside the callback function just fine.
I ran your code and got some strange printouts when I did a console.log on location.
I took a guess, and it appears that location is a reserved word. I bet that if you change the name of location to something else, your code should work fine.
--edit--
I should have realized this sooner actually, since location is the same as window.location, which is the browser bar location!

Categories

Resources