mate64
mate64

Reputation: 10082

Calculate the center of latitude and longitude coordinates

I'm looking for a elegant solution that calculates the center between several coordinates (for example, to simply center a map to the center of a polygon).

Table: locations:

id |    city    |   latitude   |  longitude
-----------------------------------------------
1  |   Berlin   |   52.524268  |   13.406290
-----------------------------------------------
2  |   London   |   51.508129  |  -0.1280050    
-----------------------------------------------
3  |   Hamburg  |   53.551084  |   9.9936817
-----------------------------------------------
4  |  Amsterdam |   52.370215  |   4.8951678
-----------------------------------------------

The current calculation:

function calculateCenter($array_locations) {

    $minlat = false;
    $minlng = false;
    $maxlat = false;
    $maxlng = false;

    foreach ($array_locations as $geolocation) {

         if ($minlat === false) { $minlat = $geolocation['lat']; } else { $minlat = ($geolocation['lat'] < $minlat) ? $geolocation['lat'] : $minlat; }
         if ($maxlat === false) { $maxlat = $geolocation['lat']; } else { $maxlat = ($geolocation['lat'] > $maxlat) ? $geolocation['lat'] : $maxlat; }
         if ($minlng === false) { $minlng = $geolocation['lon']; } else { $minlng = ($geolocation['lon'] < $minlng) ? $geolocation['lon'] : $minlng; }
         if ($maxlng === false) { $maxlng = $geolocation['lon']; } else { $maxlng = ($geolocation['lon'] > $maxlng) ? $geolocation['lon'] : $maxlng; }
    }

    // Calculate the center
    $lat = $maxlat - (($maxlat - $minlat) / 2);
    $lon = $maxlng - (($maxlng - $minlng) / 2);

    return array($lat, $lon);
}

Upvotes: 5

Views: 14600

Answers (3)

david strachan
david strachan

Reputation: 7228

As you are using Google Maps you can use getBounds() method and getCenter() method.

I have rearranged your coordinates to form a Convex Polygon (All the vertices point 'outwards', away from the center).The polygon is closed by having the first coordinate as the first and last value in polygonCoords array.

See jsfiddle

var map; 
var polygon;
var bounds = new google.maps.LatLngBounds();
var i; 
var myLatLng = new google.maps.LatLng(52.5,6.6);
var myOptions = {
  zoom: 5,
  center: myLatLng,
  mapTypeId: google.maps.MapTypeId.TERRAIN
};
map = new google.maps.Map(document.getElementById("map_canvas"),
    myOptions);

var polygonCoords = [
    new google.maps.LatLng(52.524268,13.406290),
    new google.maps.LatLng(53.551084,9.9936817),
    new google.maps.LatLng(51.508129,-0.1280050),
    new google.maps.LatLng(52.370215,4.8951678),
    new google.maps.LatLng(52.524268,13.406290)//Start & end point 
    ];

polygon = new google.maps.Polygon({
   paths: polygonCoords,
   strokeColor: "#FF0000",
   strokeOpacity: 0.8,
   strokeWeight: 3,
   fillColor: "#FF0000",
   fillOpacity: 0.05
 });
 polygon.setMap(map);

for (i = 0; i < polygonCoords.length; i++) {
   bounds.extend(polygonCoords[i]);
}

// The Center of the polygon
var latlng = bounds.getCenter();

var marker = new google.maps.Marker({
  position: latlng, 
  map: map, 
  title:latlng.toString()
});

Upvotes: 9

chux
chux

Reputation: 154218

Google uses a Mercator projection, treating the earth as an elongated cylinder. Thus the problem to to find the center of that projection.

For each lat/long pair, convert to map scaled x,y co-ordinates (using radians):

x = long
y = ln(tan(pi/4 + lat/2))  // Mercator projection

Then, for x & y, find the average of the minimum and maximum to get your center. Convert back to lat/long as follows

Pseudo code  
center_long = average(minimum_x, maximum_x)
center_lat  = (atan(exp(average(minimum_y, maximum_y))) - pi/4)*2

The calculation of the center longitude works fine were it not for the circular nature of the cylindric Earth projection. If the longitudes are in both the Eastern and Western hemispheres (some negative, some positive), than additional work may be needed.

Pseudo code
sort the longitudes into ascending order
for each longitude 
  difference = longitude(i-1) - longitude(i)
  // for first, use longitude(0) - longitude(last)
  if (difference < 0) add 2*pi  (360 degrees)
  Keep track of index of minimal difference
The pair with the minimal difference represents the pair that most tightly contains all longitudes. 
Average this pair for the center longitude.
If this pair was index 0 & last, add pi (180 degrees)

OP 4 city result: (52.4 N, 7.0 E)


This is my second answer, for the first does not get the the crux of OP's post. Since it has some value it remains.

Upvotes: 2

chux
chux

Reputation: 154218

Averaging your latitudes and longitudes works in many cases, but have problems in a number of cases. Example, you have 2 cites, Tokyo (long = 140) and Seattle (long -122), your average longitude is 18, somewhere in Europe. You would expect something closer to the international date line, 180 degrees away.

The most direct, no problem method, is to average the vectors as if each originated from the earth's center.

Pseudo code, (assumes radians)

for each lat,long
  // assume 1 radii from the earth's center.
  // covert lat, long, and radii into x,y,z (spherical to cartesian coordinates)
  r=1, theta=pi/2 - lat, phi=long
  x = r*sin(theta)*cos(phi)
  y = r*sin(theta)*sin(phi)
  z = r*cos(theta)
  N++;
  // accumulate x,y,z
  sum_x += x, etc.
// average x,y,z
avg_x = sum_x/N, etc.
// convert x,y,z back to spherical co-ordinates to get the lat/long center. 
rho = sqrt(avg_x*avg_x + avg_y*avg_y + avg_z*avg_z)
lat = pi/2 - acos(avg_z/rho)  // acos() results are 0 to pi
long = atan2(avg_y, avg_x)  // 4 quadrant arctangent

[Edit Corrected spherical co-ordinates to cartesian]

Upvotes: 3

Related Questions