I'm trying to build an array of latitudes and longitudes for start/end points for routing.
function parkRoute(){
var start = $('#start').val();
var end = $('#end').val();
var points = [];
function printPoints(points){
console.log(points)
}
function getXY(add){
geocoder.geocode( {'address': add + ", glendale, ca"}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0].geometry.location) {
var lon = results[0].geometry.location.kb;
var lat = results[0].geometry.location.jb;
points.push(lat,lon);
console.log(points)
}
}
})
}
getXY(start);
getXY(end);
printPoints(points);
}
It prints out an empty array first even though I'm calling another function to print them after the function to create the array.
[] parks1.js:192
[34.1480811, -118.24759369999998] parks1.js:201
[34.1480811, -118.24759369999998, 34.1851925, -118.27651679999997] parks1.js:201
What am I doing wrong?
You are printing the array contents before pushing. The geocoding operation is asynchronous, so you have to print from the async callback.
The problem is that you're not waiting for all the callbacks, which are asynchronous, to be executed.
You could do this :
var inProcess = 0; // number of tasks we're waiting for
function getXY(add){
inProcess++; // increment the number of tasks
geocoder.geocode( {'address': add + ", glendale, ca"}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0].geometry.location) {
var lon = results[0].geometry.location.kb;
var lat = results[0].geometry.location.jb;
points.push(lat,lon);
console.log(points)
}
}
oneProcessIsDone();
})
}
function oneProcessIsDone() {
if (--inProcess==0) { // decrement the number of tasks, and print the points if all are done
printPoints(points);
}
}
getXY(start);
getXY(end);
Related
Using Google Maps and the Geocoder, I'm trying to loop over an object of addresses, return LatLng addresses for them, and create markers using both the original details and the latlng address in the setMarker function below.
The problem is, the response[a] is being overwritten by the last address in the object, because the for loop is running ahead of the AJAX results being returned.
How can I save the data in the current response[a] being looped over, so that when setMarker() is later called, it contains the right information?
Thanks
var limit = 0;
for (a in response){
if(limit<5){ // limit API calls
var addr = [response[a].Addr1, response[a].City, response[a].Zip];
geo = new google.maps.Geocoder();
geo.geocode({
address: addr.join(", "),
componentRestrictions: {
// country: 'UK'
}
}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK && results) {
var latitude = results[0].geometry.location.lat();
var longitude = results[0].geometry.location.lng();
var latlng = new google.maps.LatLng(latitude, longitude);
if(latitude!="" && longitude!=""){
bounds.extend(latlng);
map.fitBounds(bounds);
_this.setMarker(map, limit, latlng, response[a]);
}
} // if geo results
});
}
limit++;
}
The problem you are facing is a classic one that can be solved using closure function.
Current code looks something like :
var a[20];
for(i=0;i<20;i++) {
some_async_method() {
//code that uses 'a[i]'
}
}
Using closure to preserve the scope of var a inside a async function :
var a[20];
for(i=0;i<20;i++) {
(function(_a){
some_async_method() {
//code that uses 'a[i]' as '_a'
}
})(a[i]);// self calling function that preserves the scope of a[i]
}
So your code will look like :
var limit = 0;
for (a in response){
if(limit<5){ // limit API calls
var addr = [response[a].Addr1, response[a].City, response[a].Zip];
geo = new google.maps.Geocoder();
(function(response_a){ // closure function to preserve scope of 'response[a]'
geo.geocode({
address: addr.join(", "),
componentRestrictions: {
// country: 'UK'
}
}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK && results) {
var latitude = results[0].geometry.location.lat();
var longitude = results[0].geometry.location.lng();
var latlng = new google.maps.LatLng(latitude, longitude);
if(latitude!="" && longitude!=""){
bounds.extend(latlng);
map.fitBounds(bounds);
_this.setMarker(map, limit, latlng, response_a);
}
} // if geo results
});
})(response[a]);
}
limit++;
}
Based on the user location and a store location I try to figure out what the distance is between this two. This is working, but what I want is an array with all the values and sort on the distance between the two points.
I have my add_stores_to_array function, wich add all stores to the array stores when it is looping through the JSON file.
add_stores_to_array = function(position) {
var user_latitude = position.coords.latitude;
var user_longitude = position.coords.longitude;
$.getJSON('/stores').done(function(data) {
$.each(data.features, function(i, item) {
var store_latitude = item.geometry.coordinates[1];
var store_longitude = item.geometry.coordinates[0];
var user = new google.maps.LatLng(user_latitude, user_longitude);
var store = new google.maps.LatLng(store_latitude, store_longitude);
var directionsService = new google.maps.DirectionsService();
var request = {
origin:user,
destination:store,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
// add distance and store id to the array stores
stores.push({distance: response, id: item.properties.Nid});
}
});
});
// call the sort function
sort_stores(stores);
console.log(stores);
});
};
After the $.each I call the sort function. But after logging it to the console, it is still not sorted.
My sort_stores function:
sort_stores = function(stores){
stores.sort(function(a, b){
return a.distance - b.distance;
});
};
First I thought it wasn't working because the $.each was still running, but after adding this code, it still doesn't working:
if (i == Object.keys(data.features).pop()) {
sort_stores(stores);
}
So, I tried something different. I call the sort_stores(stores) function in the $.each.
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
stores.push({distance: response, id: item.properties.Nid});
sort_stores(stores);
}
});
And it works.. the array is sorted based on the value distance in the array. But now he sorts the array after each added store.. not really effective.
Is there a proper way to call the sort_stores(stores) function one time, and sort it when all stores are added to the array?
EDIT:
If I place an alert() before the sort_stores(stores) it is working..
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
stores.push({distance: response, id: item.properties.Nid});
}
});
});
alert('Call the sort_stores(stores) function after the $.each, with an alert.. it is working?');
sort_stores(stores);
});
};
Edit 2:
Normally I call the function add_stores_to_array from here?
get_user_location = function(){
if (navigator.geolocation) {
navigator.geolocation.getCurrentPosition(add_stores_to_array);
}
};
There's nothing wrong with your sort function. The problem is that directionsService.route is asynchronous call and the rest of the code will run even when all the calls are not completed yet.
You can use jQuery.when(). Here is the new add_stores_to_array() function
add_stores_to_array = function(position) {
var promises = []; //ADDED promise array
var user_latitude = position.coords.latitude;
var user_longitude = position.coords.longitude;
$.getJSON('/stores').done(function(data) {
$.each(data.features, function(i, item) {
var store_latitude = item.geometry.coordinates[1];
var store_longitude = item.geometry.coordinates[0];
var user = new google.maps.LatLng(user_latitude, user_longitude);
var store = new google.maps.LatLng(store_latitude, store_longitude);
var directionsService = new google.maps.DirectionsService();
var request = {
origin:user,
destination:store,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
var dfd = directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
// add distance and store id to the array stores
stores.push({distance: response, id: item.properties.Nid});
}
});
promises.push(dfd); //ADDED store each object in array
});
//Now you can do the following without having any async issue.
$.when.apply(null, promises).done(function() {
/* sort & do stuff here */
sort_stores(stores);
console.log(stores);
});
});
};
EDIT
Here is another approach. Since you need to wait until all responses are returned, you can customize your sort function to check for the response count. If it's equal to total (which means all calls have finished successfully) then sort the array.
sort_stores = function(stores, responseCount, totalCount ) {
if (responseCount == totalCount) {
stores.sort(function(a, b){
return a.distance - b.distance;
});
}
};
Then change the add_stores_to_array function as follows.
add_stores_to_array = function(position) {
var user_latitude = position.coords.latitude;
var user_longitude = position.coords.longitude;
$.getJSON('/stores').done(function(data) {
var totalCount = data.features.length; //ADDED Get total count
var responseCount = 0; //ADDED
$.each(data.features, function(i, item) {
var store_latitude = item.geometry.coordinates[1];
var store_longitude = item.geometry.coordinates[0];
var user = new google.maps.LatLng(user_latitude, user_longitude);
var store = new google.maps.LatLng(store_latitude, store_longitude);
var directionsService = new google.maps.DirectionsService();
var request = {
origin:user,
destination:store,
travelMode: google.maps.DirectionsTravelMode.DRIVING
};
directionsService.route(request, function(response, status) {
if (status == google.maps.DirectionsStatus.OK) {
var response = Math.ceil(response.routes[0].legs[0].distance.value / 1000);
// add distance and store id to the array stores
stores.push({distance: response, id: item.properties.Nid});
responseCount++; //ADDED
sort_stores(stores, responseCount, totalCount); //ADDED Call sort function here
}
});
});
});
};
I have some latitudes and longitudes i want to find out their addresses.
function OnSuccess(response) {
showLatLng(0, JSON.parse(response.d).Table, JSON.parse(response.d).Table.length);
}
Where JSON.parse(response.d).Table contains the result set.
function showLatLng(index, resultSet, totalLen) {
var lat = resultSet[index].x;
var lng = resultSet[index].y;
var latlng = new google.maps.LatLng(lat, lng);
new google.maps.Geocoder().geocode({ 'latLng': latlng },
function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[0]) {
var z = " <tr>" +
"<td>" + results[0].formatted_address + "</td>" +
"</tr>";
$("#result").append(z);
if (index < totalLen) {
showLatLng(index + 1, resultSet, totalLen);
}
}
}
});
}
This code is working only 4 to 5 times then stops and no error in firebug.
Please give me a better way to do this.
EDIT:
Lucas You are absolutely right.
Previously I have used loop with timeout as below
$.each(JSON.parse(response.d).Table, function (index, value) {
setTimeout(function () {
showLatLng(index , value.x, value.y);
}, (index + 1) * 7000);
});
But for some reason (because I think it is working on async call) it is skipping some results.
pass Latitude and Longitude value to script
function GetAddress() {
var lat = parseFloat(document.getElementById("txtLatitude").value);
var lng = parseFloat(document.getElementById("txtLongitude").value);
var latlng = new google.maps.LatLng(lat, lng);
var geocoder = geocoder = new google.maps.Geocoder();
geocoder.geocode({ 'latLng': latlng }, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
alert("Location: " + results[1].formatted_address);
}
}
});
}
I have two set of lat and lng.
I want both address and stored in some variable:
var geocoder = new google.maps.Geocoder();
for(var i=0; i<json_devices.length; i++)
{
var lat = json_devices[i].latitude;
var lng = json_devices[i].longitude;
console.log(lat);
console.log(lng);
var latlng = new google.maps.LatLng(lat,lng);
geocoder.geocode({'latLng': latlng}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
address=results[1].formatted_address;
} else {
alert('No results found');
}
} else {
alert('Geocoder failed due to: ' + status);
}
});
console.log(address);
}
In this, lat & lan get correctly. But address are not stored in variable. What is the mistake?
I am using this method and it is working perfect for me.
Please have a look on it.
public String getAddressFromLatLong(GeoPoint point) {
String address = "Address Not Found";
Geocoder geoCoder = new Geocoder(
getBaseContext(), Locale.getDefault());
try {
List<Address> addresses = geoCoder.getFromLocation(
point.getLatitudeE6() / 1E6,
point.getLongitudeE6() / 1E6, 1);
if (addresses.size() > 0) {
address =addresses.get(0).getAddressLine(0);
if(address.length()<=0)
address =addresses.get(0).getSubLocality();
}
}
catch (Exception e) {
e.printStackTrace();
}
return address;
}
Here the Google geocode is asynchonous type of function call.
From DOCS:
Accessing the Geocoding service is asynchronous, since the Google Maps
API needs to make a call to an external server. For that reason, you
need to pass a callback method to execute upon completion of the
request. This callback method processes the result(s). Note that the
geocoder may return more than one result.
So you can't get the address like that, instead use the common approach called callback.
Here I have created a sample code to explain the process, which can be altered by yourself.
var geocoder;
function codeLatLng(callback) {
geocoder = new google.maps.Geocoder();
var input = document.getElementById("latlng").value;
var latlngStr = input.split(",", 2);
var lat = parseFloat(latlngStr[0]);
var lng = parseFloat(latlngStr[1]);
var latlng = new google.maps.LatLng(lat, lng);
geocoder.geocode({
'latLng': latlng
}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
if (results[1]) {
address = results[1].formatted_address;
callback(address);
} else {
alert("No results found");
}
} else {
alert("Geocoder failed due to: " + status);
}
});
}
$('input[type="button"]').on('click', function () {
codeLatLng(function (address) { //function call with a callback
console.log(address); // THE ADDRESS WILL BE OBTAINED
})
});
JSFIDDLE
Hi I have some trouble with an array with a for in JavaScript. Let's have a look:
var Villes = [
['Versailles+France', 'upr2.png'],
['Paris+France', 'upr5.png'],
['Bruxelle+Belgique', 'upr4.png'],
['Manchester+Angleterre', 'upr1.png'],
['Monaco+Monaco', 'upr3.png']
];
function initialize() {
var mapOptions = {
zoom: 5,
center: new google.maps.LatLng(46.225453,2.219238),
mapTypeId: google.maps.MapTypeId.ROADMAP
};
var map = new google.maps.Map(document.getElementById("map-canvas"), mapOptions);
var geocoder = new google.maps.Geocoder();
for (var i = 0; i < Villes.length; i++) {
var ville = Villes[i];
geocoder.geocode( { 'address': ville[0]}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({position: results[0].geometry.location,map: map,icon: ville[1]});
alert(ville[1] + status);
} else {
alert("Geocode n'a pas fonctionner. Erreur: " + status);
}
});
}
}
My Map comes with all my marker but the icon never change like ville[1] is a static call to upr3.png I'm not used to JS and it's the first time I see that.
By the time the callback you give to geocode is called, i has the value of end of loop.
The usual generic solution is to protect it by an immediately called function expression :
for (var i = 0; i < Villes.length; i++) {
(function(ville){
geocoder.geocode( { 'address': ville[0]}, function(results, status)
...
});
})(Villes[i]);
}
As the scope of a variable is the function in which it is declared, this makes the new variable ville immune to the variation of the loop.
The geocode call is asynchronous, which means that you will loop through all the places and send requests, then the responses will arrive. At that time you have already run through the loop, and the ville variable has the value of the last place.
Put a function expression around the code in the loop, so that each iteration has its own copy of the variable:
for (var i = 0; i < Villes.length; i++) {
(function(ville){
geocoder.geocode( { 'address': ville[0]}, function(results, status) {
if (status == google.maps.GeocoderStatus.OK) {
var marker = new google.maps.Marker({position: results[0].geometry.location,map: map,icon: ville[1]});
alert(ville[1] + status);
} else {
alert("Geocode n'a pas fonctionner. Erreur: " + status);
}
});
})(Villes[i]);
}
The problem is that ville is not an array, so by the time the for loop finishes, ville only has the value of the last item in your initial array (png3). You need call the function right after setting the value to ville