My first thought was that you're not limited to looping over your data only once. The data will still be there after you loop over it (unless you change or destroy it in the process).
So, you'd use two loops.
First loop calculates the center point.
Second loop calculates all of the distances from that point.
But as I look over your code I see there's a bit more to it than that.
Inside your main loop where you iterate over namearray
, the loop calls geocoder.geocode()
for each element of this array. Then inside the geocoder callback is where the work is done.
The interesting thing is that this code is not actually part of your main loop at all. It appears inside that loop, but it's called much later, as the geocode responses come back one by one.
So let's assume that there are no errors in the geocoding (bad assumption, but we'll run with it for a moment). Your geocoder callback already pushes each geocoding result onto mapArray
. So what you could do is have this callback code only do this and not do any of the center/circle calculations. So your geocode call might look more like this:
geocoder.geocode({
'address': address
}, function (results, status) {
if (status == google.maps.GeocoderStatus.OK) {
mapArray.push({ latLng: results[0].geometry.location });
AvgLat += results.lat();
AvgLng += results.lng();
} else {
mapArray.push({ error: status });
++nErrors;
}
// Might want to update a progress indicator here
// if the geocoding takes a while?
if( mapArray.length == namearray.length ) {
// Everything is geocoded, now get to work
drawMap();
}
});
(nErrors
should be defined above the loop where you initialize your other variables with var nErrors = 0;
)
Then completely outside your current main loop, you add this function:
function drawMap() {
if( nErrors ) {
// complain about error
return;
}
var center = {
lat: AvgLat / j,
lng: AvgLng / j,
};
map.setCenter( new google.maps.LatLng( center.lat, center.lng ) );
for( var i = 0; i < mapArray.length; i++ ) {
// Create marker and draw circle here
}
}
So a couple of other points. First, calculating the average lat/lng this way doesn't sound quite right. I would give some thought (and testing) to cases where markers are different sides of the 0° and 180° meridians. You may be OK there, I just never count on it!
Also is the "average" lat/long actually what you want here? (It may be, just asking.) If you have many markers near each other and very few markers at some distance (e.g. 10 markers in CA and 1 in NY), the "average" will be very near to that cluster of markers. That may be what you want, but if not, there are other ways to calculate various kinds of "centers".
For example, one common way is to create an empty LatLngBounds
object, and then extend those boundaries with each position you receive. So up at the top with your other variable initialization you'd add:
var bounds = new google.maps.LatLngBounds;
And then inside each successful geocode callback you'd replace this:
AvgLat += results.lat();
AvgLng += results.lng();
with:
bounds.extend( results );
Then in drawMap()
you'd replace this:
var center = {
lat: AvgLat / j,
lng: AvgLng / j,
};
map.setCenter( new google.maps.LatLng( center.lat, center.lng ) );
with:
map.fitBounds( bounds );
var center = bounds.getCenter();
and use center.lat()
and center.lng()
if you need those individual values - but in the places where you have newLatLng
you can now just use center
directly because it is a LatLng
object.
Even if you do want to use an "average" of the points for your circle calculations, you may want to use this bounding box approach for the map centering instead of centering the map on the calculated average.
One last thing is to watch out for rate limits and throttling on the geocoding. Maybe you're OK for a small number of markers, but it's possible that you may need to make the geocode calls with a time interval between them instead of firing them all of in a hard loop.