Given a set of latitude and longitude points, how can I calculate the latitude and longitude of the center point of that set (aka a point that would center a view on all points)?
EDIT: Python solution I've used:
Convert lat/lon (must be in radians) to Cartesian coordinates for each location.
X = cos(lat) * cos(lon)
Y = cos(lat) * sin(lon)
Z = sin(lat)
Compute average x, y and z coordinates.
x = (x1 + x2 + ... + xn) / n
y = (y1 + y2 + ... + yn) / n
z = (z1 + z2 + ... + zn) / n
Convert average x, y, z coordinate to latitude and longitude.
Lon = atan2(y, x)
Hyp = sqrt(x * x + y * y)
Lat = atan2(z, hyp)
z
, pls?
Thanks! Here is a C# version of OP's solutions using degrees. It utilises the System.Device.Location.GeoCoordinate class
public static GeoCoordinate GetCentralGeoCoordinate(
IList<GeoCoordinate> geoCoordinates)
{
if (geoCoordinates.Count == 1)
{
return geoCoordinates.Single();
}
double x = 0;
double y = 0;
double z = 0;
foreach (var geoCoordinate in geoCoordinates)
{
var latitude = geoCoordinate.Latitude * Math.PI / 180;
var longitude = geoCoordinate.Longitude * Math.PI / 180;
x += Math.Cos(latitude) * Math.Cos(longitude);
y += Math.Cos(latitude) * Math.Sin(longitude);
z += Math.Sin(latitude);
}
var total = geoCoordinates.Count;
x = x / total;
y = y / total;
z = z / total;
var centralLongitude = Math.Atan2(y, x);
var centralSquareRoot = Math.Sqrt(x * x + y * y);
var centralLatitude = Math.Atan2(z, centralSquareRoot);
return new GeoCoordinate(centralLatitude * 180 / Math.PI, centralLongitude * 180 / Math.PI);
}
The simple approach of just averaging them has weird edge cases with angles when they wrap from 359' back to 0'.
A much earlier question on SO asked about finding the average of a set of compass angles.
An expansion of the approach recommended there for spherical coordinates would be:
Convert each lat/long pair into a unit-length 3D vector.
Sum each of those vectors
Normalise the resulting vector
Convert back to spherical coordinates
I found this post very useful so here is the solution in PHP. I've been using this successfully and just wanted to save another dev some time.
/**
* Get a center latitude,longitude from an array of like geopoints
*
* @param array data 2 dimensional array of latitudes and longitudes
* For Example:
* $data = array
* (
* 0 = > array(45.849382, 76.322333),
* 1 = > array(45.843543, 75.324143),
* 2 = > array(45.765744, 76.543223),
* 3 = > array(45.784234, 74.542335)
* );
*/
function GetCenterFromDegrees($data)
{
if (!is_array($data)) return FALSE;
$num_coords = count($data);
$X = 0.0;
$Y = 0.0;
$Z = 0.0;
foreach ($data as $coord)
{
$lat = $coord[0] * pi() / 180;
$lon = $coord[1] * pi() / 180;
$a = cos($lat) * cos($lon);
$b = cos($lat) * sin($lon);
$c = sin($lat);
$X += $a;
$Y += $b;
$Z += $c;
}
$X /= $num_coords;
$Y /= $num_coords;
$Z /= $num_coords;
$lon = atan2($Y, $X);
$hyp = sqrt($X * $X + $Y * $Y);
$lat = atan2($Z, $hyp);
return array($lat * 180 / pi(), $lon * 180 / pi());
}
Very useful post! I've implemented this in JavaScript, hereby my code. I've used this successfully.
function rad2degr(rad) { return rad * 180 / Math.PI; }
function degr2rad(degr) { return degr * Math.PI / 180; }
/**
* @param latLngInDeg array of arrays with latitude and longtitude
* pairs in degrees. e.g. [[latitude1, longtitude1], [latitude2
* [longtitude2] ...]
*
* @return array with the center latitude longtitude pairs in
* degrees.
*/
function getLatLngCenter(latLngInDegr) {
var LATIDX = 0;
var LNGIDX = 1;
var sumX = 0;
var sumY = 0;
var sumZ = 0;
for (var i=0; i<latLngInDegr.length; i++) {
var lat = degr2rad(latLngInDegr[i][LATIDX]);
var lng = degr2rad(latLngInDegr[i][LNGIDX]);
// sum of cartesian coordinates
sumX += Math.cos(lat) * Math.cos(lng);
sumY += Math.cos(lat) * Math.sin(lng);
sumZ += Math.sin(lat);
}
var avgX = sumX / latLngInDegr.length;
var avgY = sumY / latLngInDegr.length;
var avgZ = sumZ / latLngInDegr.length;
// convert average x, y, z coordinate to latitude and longtitude
var lng = Math.atan2(avgY, avgX);
var hyp = Math.sqrt(avgX * avgX + avgY * avgY);
var lat = Math.atan2(avgZ, hyp);
return ([rad2degr(lat), rad2degr(lng)]);
}
Javascript version of the original function
/**
* Get a center latitude,longitude from an array of like geopoints
*
* @param array data 2 dimensional array of latitudes and longitudes
* For Example:
* $data = array
* (
* 0 = > array(45.849382, 76.322333),
* 1 = > array(45.843543, 75.324143),
* 2 = > array(45.765744, 76.543223),
* 3 = > array(45.784234, 74.542335)
* );
*/
function GetCenterFromDegrees(data)
{
if (!(data.length > 0)){
return false;
}
var num_coords = data.length;
var X = 0.0;
var Y = 0.0;
var Z = 0.0;
for(i = 0; i < data.length; i++){
var lat = data[i][0] * Math.PI / 180;
var lon = data[i][1] * Math.PI / 180;
var a = Math.cos(lat) * Math.cos(lon);
var b = Math.cos(lat) * Math.sin(lon);
var c = Math.sin(lat);
X += a;
Y += b;
Z += c;
}
X /= num_coords;
Y /= num_coords;
Z /= num_coords;
var lon = Math.atan2(Y, X);
var hyp = Math.sqrt(X * X + Y * Y);
var lat = Math.atan2(Z, hyp);
var newX = (lat * 180 / Math.PI);
var newY = (lon * 180 / Math.PI);
return new Array(newX, newY);
}
In the interest of possibly saving someone a minute or two, here is the solution that was used in Objective-C instead of python. This version takes an NSArray of NSValues that contain MKMapCoordinates, which was called for in my implementation:
#import <MapKit/MKGeometry.h>
+ (CLLocationCoordinate2D)centerCoordinateForCoordinates:(NSArray *)coordinateArray {
double x = 0;
double y = 0;
double z = 0;
for(NSValue *coordinateValue in coordinateArray) {
CLLocationCoordinate2D coordinate = [coordinateValue MKCoordinateValue];
double lat = GLKMathDegreesToRadians(coordinate.latitude);
double lon = GLKMathDegreesToRadians(coordinate.longitude);
x += cos(lat) * cos(lon);
y += cos(lat) * sin(lon);
z += sin(lat);
}
x = x / (double)coordinateArray.count;
y = y / (double)coordinateArray.count;
z = z / (double)coordinateArray.count;
double resultLon = atan2(y, x);
double resultHyp = sqrt(x * x + y * y);
double resultLat = atan2(z, resultHyp);
CLLocationCoordinate2D result = CLLocationCoordinate2DMake(GLKMathRadiansToDegrees(resultLat), GLKMathRadiansToDegrees(resultLon));
return result;
}
<GLKit/GLKMath.h>
and use GLKMathDegreesToRadians
and GLKMathRadiansToDegrees
very nice solutions, just what i needed for my swift project, so here's a swift port. thanks & here's also a playground project: https://github.com/ppoh71/playgounds/tree/master/centerLocationPoint.playground
/*
* calculate the center point of multiple latitude longitude coordinate-pairs
*/
import CoreLocation
import GLKit
var LocationPoints = [CLLocationCoordinate2D]()
//add some points to Location ne, nw, sw, se , it's a rectangle basicaly
LocationPoints.append(CLLocationCoordinate2D(latitude: 37.627512369999998, longitude: -122.38780611999999))
LocationPoints.append(CLLocationCoordinate2D(latitude: 37.627512369999998, longitude: -122.43105867))
LocationPoints.append(CLLocationCoordinate2D(latitude: 37.56502528, longitude: -122.43105867))
LocationPoints.append(CLLocationCoordinate2D(latitude: 37.56502528, longitude: -122.38780611999999))
// center func
func getCenterCoord(LocationPoints: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D{
var x:Float = 0.0;
var y:Float = 0.0;
var z:Float = 0.0;
for points in LocationPoints {
let lat = GLKMathDegreesToRadians(Float(points.latitude));
let long = GLKMathDegreesToRadians(Float(points.longitude));
x += cos(lat) * cos(long);
y += cos(lat) * sin(long);
z += sin(lat);
}
x = x / Float(LocationPoints.count);
y = y / Float(LocationPoints.count);
z = z / Float(LocationPoints.count);
let resultLong = atan2(y, x);
let resultHyp = sqrt(x * x + y * y);
let resultLat = atan2(z, resultHyp);
let result = CLLocationCoordinate2D(latitude: CLLocationDegrees(GLKMathRadiansToDegrees(Float(resultLat))), longitude: CLLocationDegrees(GLKMathRadiansToDegrees(Float(resultLong))));
return result;
}
//get the centerpoint
var centerPoint = getCenterCoord(LocationPoints)
print("Latitude: \(centerPoint.latitude) / Longitude: \(centerPoint.longitude)")
Java Version if anyone needs it. Constants defined static to not calculate them twice.
/**************************************************************************************************************
* Center of geometry defined by coordinates
**************************************************************************************************************/
private static double pi = Math.PI / 180;
private static double xpi = 180 / Math.PI;
public static Coordinate center(Coordinate... arr) {
if (arr.length == 1) {
return arr[0];
}
double x = 0, y = 0, z = 0;
for (Coordinate c : arr) {
double latitude = c.lat() * pi, longitude = c.lon() * pi;
double cl = Math.cos(latitude);//save it as we need it twice
x += cl * Math.cos(longitude);
y += cl * Math.sin(longitude);
z += Math.sin(latitude);
}
int total = arr.length;
x = x / total;
y = y / total;
z = z / total;
double centralLongitude = Math.atan2(y, x);
double centralSquareRoot = Math.sqrt(x * x + y * y);
double centralLatitude = Math.atan2(z, centralSquareRoot);
return new Coordinate(centralLatitude * xpi, centralLongitude * xpi);
}
If you are interested in obtaining a very simplified 'center' of the points (for example, to simply center a map to the center of your gmaps polygon), then here's a basic approach that worked for me.
public function center() {
$minlat = false;
$minlng = false;
$maxlat = false;
$maxlng = false;
$data_array = json_decode($this->data, true);
foreach ($data_array as $data_element) {
$data_coords = explode(',',$data_element);
if (isset($data_coords[1])) {
if ($minlat === false) { $minlat = $data_coords[0]; } else { $minlat = ($data_coords[0] < $minlat) ? $data_coords[0] : $minlat; }
if ($maxlat === false) { $maxlat = $data_coords[0]; } else { $maxlat = ($data_coords[0] > $maxlat) ? $data_coords[0] : $maxlat; }
if ($minlng === false) { $minlng = $data_coords[1]; } else { $minlng = ($data_coords[1] < $minlng) ? $data_coords[1] : $minlng; }
if ($maxlng === false) { $maxlng = $data_coords[1]; } else { $maxlng = ($data_coords[1] > $maxlng) ? $data_coords[1] : $maxlng; }
}
}
$lat = $maxlat - (($maxlat - $minlat) / 2);
$lng = $maxlng - (($maxlng - $minlng) / 2);
return $lat.','.$lng;
}
This returns the middle lat/lng coordinate for the center of a polygon.
In Django this is trivial (and actually works, I had issues with a number of the solutions not correctly returning negatives for latitude).
For instance, let's say you are using django-geopostcodes (of which I am the author).
from django.contrib.gis.geos import MultiPoint
from django.contrib.gis.db.models.functions import Distance
from django_geopostcodes.models import Locality
qs = Locality.objects.anything_icontains('New York')
points = [locality.point for locality in qs]
multipoint = MultiPoint(*points)
point = multipoint.centroid
point
is a Django Point
instance that can then be used to do things such as retrieve all objects that are within 10km of that centre point;
Locality.objects.filter(point__distance_lte=(point, D(km=10)))\
.annotate(distance=Distance('point', point))\
.order_by('distance')
Changing this to raw Python is trivial;
from django.contrib.gis.geos import Point, MultiPoint
points = [
Point((145.137075, -37.639981)),
Point((144.137075, -39.639981)),
]
multipoint = MultiPoint(*points)
point = multipoint.centroid
Under the hood Django is using GEOS - more details at https://docs.djangoproject.com/en/1.10/ref/contrib/gis/geos/
Here is the Android version based on @Yodacheese's C# answer using Google Maps api:
public static LatLng GetCentralGeoCoordinate(List<LatLng> geoCoordinates) {
if (geoCoordinates.size() == 1)
{
return geoCoordinates.get(0);
}
double x = 0;
double y = 0;
double z = 0;
for(LatLng geoCoordinate : geoCoordinates)
{
double latitude = geoCoordinate.latitude * Math.PI / 180;
double longitude = geoCoordinate.longitude * Math.PI / 180;
x += Math.cos(latitude) * Math.cos(longitude);
y += Math.cos(latitude) * Math.sin(longitude);
z += Math.sin(latitude);
}
int total = geoCoordinates.size();
x = x / total;
y = y / total;
z = z / total;
double centralLongitude = Math.atan2(y, x);
double centralSquareRoot = Math.sqrt(x * x + y * y);
double centralLatitude = Math.atan2(z, centralSquareRoot);
return new LatLng(centralLatitude * 180 / Math.PI, centralLongitude * 180 / Math.PI);
}
in app build.gradle add:
implementation 'com.google.android.gms:play-services-maps:17.0.0'
Dart Implementation for Flutter to find Center point for Multiple Latitude, Longitude.
import math package
import 'dart:math' as math;
Latitude and Longitude List
List<LatLng> latLongList = [LatLng(12.9824, 80.0603),LatLng(13.0569,80.2425,)];
LatLng getCenterLatLong(List<LatLng> latLongList) {
double pi = math.pi / 180;
double xpi = 180 / math.pi;
double x = 0, y = 0, z = 0;
if(latLongList.length==1)
{
return latLongList[0];
}
for (int i = 0; i < latLongList.length; i++) {
double latitude = latLongList[i].latitude * pi;
double longitude = latLongList[i].longitude * pi;
double c1 = math.cos(latitude);
x = x + c1 * math.cos(longitude);
y = y + c1 * math.sin(longitude);
z = z + math.sin(latitude);
}
int total = latLongList.length;
x = x / total;
y = y / total;
z = z / total;
double centralLongitude = math.atan2(y, x);
double centralSquareRoot = math.sqrt(x * x + y * y);
double centralLatitude = math.atan2(z, centralSquareRoot);
return LatLng(centralLatitude*xpi,centralLongitude*xpi);
}
Here is the python Version for finding center point. The lat1 and lon1 are latitude and longitude lists. it will retuen the latitude and longitude of center point.
import numpy as np
def GetCenterFromDegrees(lat1,lon1):
if (len(lat1) <= 0):
return false;
num_coords = len(lat1)
X = 0.0
Y = 0.0
Z = 0.0
for i in range (len(lat1)):
lat = lat1[i] * np.pi / 180
lon = lon1[i] * np.pi / 180
a = np.cos(lat) * np.cos(lon)
b = np.cos(lat) * np.sin(lon)
c = np.sin(lat);
X += a
Y += b
Z += c
X /= num_coords
Y /= num_coords
Z /= num_coords
lon = np.arctan2(Y, X)
hyp = np.sqrt(X * X + Y * Y)
lat = np.arctan2(Z, hyp)
newX = (lat * 180 / np.pi)
newY = (lon * 180 / np.pi)
return newX, newY
This is is the same as a weighted average problem where all the weights are the same, and there are two dimensions.
Find the average of all latitudes for your center latitude and the average of all longitudes for the center longitude.
Caveat Emptor: This is a close distance approximation and the error will become unruly when the deviations from the mean are more than a few miles due to the curvature of the Earth. Remember that latitudes and longitudes are degrees (not really a grid).
If you wish to take into account the ellipsoid being used you can find the formulae here http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.pdf
see Annexe B
The document contains lots of other useful stuff
B
Out of object in PHP. Given array of coordinate pairs, returns center.
/**
* Calculate center of given coordinates
* @param array $coordinates Each array of coordinate pairs
* @return array Center of coordinates
*/
function getCoordsCenter($coordinates) {
$lats = $lons = array();
foreach ($coordinates as $key => $value) {
array_push($lats, $value[0]);
array_push($lons, $value[1]);
}
$minlat = min($lats);
$maxlat = max($lats);
$minlon = min($lons);
$maxlon = max($lons);
$lat = $maxlat - (($maxlat - $minlat) / 2);
$lng = $maxlon - (($maxlon - $minlon) / 2);
return array("lat" => $lat, "lon" => $lng);
}
Taken idea from #4
I did this task in javascript like below
function GetCenterFromDegrees(data){
// var data = [{lat:22.281610498720003,lng:70.77577162868579},{lat:22.28065743343672,lng:70.77624369747241},{lat:22.280860953131217,lng:70.77672113067706},{lat:22.281863655593973,lng:70.7762061465462}];
var num_coords = data.length;
var X = 0.0;
var Y = 0.0;
var Z = 0.0;
for(i=0; i<num_coords; i++){
var lat = data[i].lat * Math.PI / 180;
var lon = data[i].lng * Math.PI / 180;
var a = Math.cos(lat) * Math.cos(lon);
var b = Math.cos(lat) * Math.sin(lon);
var c = Math.sin(lat);
X += a;
Y += b;
Z += c;
}
X /= num_coords;
Y /= num_coords;
Z /= num_coords;
lon = Math.atan2(Y, X);
var hyp = Math.sqrt(X * X + Y * Y);
lat = Math.atan2(Z, hyp);
var finalLat = lat * 180 / Math.PI;
var finalLng = lon * 180 / Math.PI;
var finalArray = Array();
finalArray.push(finalLat);
finalArray.push(finalLng);
return finalArray;
}
Dart/Flutter Calculate the center point of multiple latitude/longitude coordinate pairs
Map<String, double> getLatLngCenter(List<List<double>> coords) {
const LATIDX = 0;
const LNGIDX = 1;
double sumX = 0;
double sumY = 0;
double sumZ = 0;
for (var i = 0; i < coords.length; i++) {
var lat = VectorMath.radians(coords[i][LATIDX]);
var lng = VectorMath.radians(coords[i][LNGIDX]);
// sum of cartesian coordinates
sumX += Math.cos(lat) * Math.cos(lng);
sumY += Math.cos(lat) * Math.sin(lng);
sumZ += Math.sin(lat);
}
var avgX = sumX / coords.length;
var avgY = sumY / coords.length;
var avgZ = sumZ / coords.length;
// convert average x, y, z coordinate to latitude and longtitude
var lng = Math.atan2(avgY, avgX);
var hyp = Math.sqrt(avgX * avgX + avgY * avgY);
var lat = Math.atan2(avgZ, hyp);
return {
"latitude": VectorMath.degrees(lat),
"longitude": VectorMath.degrees(lng)
};
}
So many of these answers are just variations on an odd approach that doesn't find the true center of the bounding box that comprises all the points. Rather it finds the center of most points (a weighted center of sorts). If you want the true center of all points regardless of clustering and weights, you can get the bounding box and easily find the center of those 4 corners. If you aren't concerned about factoring in earth curvature, you can get away with something as simple as (C# code):
var lat = (coordinates.Min(x => x.lat) + coordinates.Max(x => x.lat))/2;
var lon = (coordinates.Min(x => x.lon) + coordinates.Max(x => x.lon))/2;
return new Tuple<double, double>(lat, lon);
If you want all points to be visible in the image, you'd want the extrema in latitude and longitude and make sure that your view includes those values with whatever border you want.
(From Alnitak's answer, how you calculate the extrema may be a little problematic, but if they're a few degrees on either side of the longitude that wraps around, then you'll call the shot and take the right range.)
If you don't want to distort whatever map that these points are on, then adjust the bounding box's aspect ratio so that it fits whatever pixels you've allocated to the view but still includes the extrema.
To keep the points centered at some arbitrary zooming level, calculate the center of the bounding box that "just fits" the points as above, and keep that point as the center point.
As an appreciation for this thread, here is my little contribution with the implementation in Ruby, hoping that I will save someone a few minutes from their precious time:
def self.find_center(locations)
number_of_locations = locations.length
return locations.first if number_of_locations == 1
x = y = z = 0.0
locations.each do |station|
latitude = station.latitude * Math::PI / 180
longitude = station.longitude * Math::PI / 180
x += Math.cos(latitude) * Math.cos(longitude)
y += Math.cos(latitude) * Math.sin(longitude)
z += Math.sin(latitude)
end
x = x/number_of_locations
y = y/number_of_locations
z = z/number_of_locations
central_longitude = Math.atan2(y, x)
central_square_root = Math.sqrt(x * x + y * y)
central_latitude = Math.atan2(z, central_square_root)
[latitude: central_latitude * 180 / Math::PI,
longitude: central_longitude * 180 / Math::PI]
end
I used a formula that I got from www.geomidpoint.com and wrote the following C++ implementation. The array
and geocoords
are my own classes whose functionality should be self-explanatory.
/*
* midpoints calculated using formula from www.geomidpoint.com
*/
geocoords geocoords::calcmidpoint( array<geocoords>& points )
{
if( points.empty() ) return geocoords();
float cart_x = 0,
cart_y = 0,
cart_z = 0;
for( auto& point : points )
{
cart_x += cos( point.lat.rad() ) * cos( point.lon.rad() );
cart_y += cos( point.lat.rad() ) * sin( point.lon.rad() );
cart_z += sin( point.lat.rad() );
}
cart_x /= points.numelems();
cart_y /= points.numelems();
cart_z /= points.numelems();
geocoords mean;
mean.lat.rad( atan2( cart_z, sqrt( pow( cart_x, 2 ) + pow( cart_y, 2 ))));
mean.lon.rad( atan2( cart_y, cart_x ));
return mean;
}
Scala version:
import scala.math._
case class Coordinate(latitude: Double, longitude: Double)
def center(coordinates: List[Coordinate]) = {
val (a: Double, b: Double, c: Double) = coordinates.fold((0.0, 0.0, 0.0)) {
case ((x: Double, y: Double, z: Double), coord: Coordinate) =>
val latitude = coord.latitude * Pi / 180
val longitude = coord.longitude * Pi / 180
(x + cos(latitude) * cos(longitude), y + cos(latitude) * sin(longitude), z + sin(latitude))
}
val total = coordinates.length
val (x: Double, y: Double, z: Double) = (a / total, b / total, c / total)
val centralLongitude = atan2(y, x)
val centralSquareRoot = sqrt(x * x + y * y)
val centralLatitude = atan2(z, centralSquareRoot)
Coordinate(centralLatitude * 180 / Pi, centralLongitude * 180 / Pi);
}
Success story sharing