计算多个纬度/经度坐标对的中心点

给定一组经纬度点,我如何计算该组中心点的经纬度(也就是将所有点都集中在一起的点) ?

编辑: 我使用过的 Python 解决方案:

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)
143752 次浏览

简单的方法只是平均他们有奇怪的边缘情况与角度时,他们包装从359’回到0’。

一个 更早的关于 SO 的问题被问到如何找到一组罗盘角度的平均值。

对球面坐标建议采用的方法的扩展如下:

  • 将每个 lat/long 对转换为单位长度的3D 矢量。
  • 把每个向量加起来
  • 使结果向量正常化
  • 转换回球坐标

这和加权平均数问题是一样的,所有的权重都是相同的,而且有两个维度。

找出中心纬度的所有纬度的平均值和中心经度的所有经度的平均值。

注意: 这是一个近距离的近似值,如果由于地球的曲率而偏离平均值超过几英里,那么误差将变得难以控制。请记住,纬度和经度是度(不是真正的网格)。

如果你想让所有的点都可以在图像中看到,你需要在经纬度中使用极值,并确保你的视图中包含了你想要的边框。

(根据 Alnitak 的回答,如何计算极值可能有点问题,但是如果它们在经度的两边都有几度,那么你就可以选择正确的射程。)

如果你不想扭曲这些点所在的任何地图,那么调整边界框的高宽比,使它适合你已经分配给视图的任何像素,但仍然包括极值。

为了使这些点保持在任意缩放水平的中心位置,计算“刚好适合”上述这些点的边界框的中心,并保持该点为中心点。

如果你希望考虑椭球体的使用,你可以找到公式 在这里我们可以看到一些 http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/a_guide_to_coordinate_systems_in_great_britain.pdf

见附件 B

这份文件包含了许多其他有用的东西

B

谢谢! 这里是一个 C # 版本的 OP 的解决方案使用程度。它利用了 系统。装置。位置。地理坐标

    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);
}

如果您有兴趣获得一个非常简化的点的“中心”(例如,简单地将地图居中到 gmap 多边形的中心) ,那么这里有一个适用于我的基本方法。

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;
}

这将返回多边形中心的中间 lat/lng 坐标。

我发现这篇文章非常有用,所以这里是 PHP 的解决方案。我一直在成功地使用它,只是想节省另一个开发人员一些时间。

/**
* 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());
}

为了节省某人一两分钟的时间,下面是在 Objective-C 中而不是在 python 中使用的解决方案。这个版本采用了一个 NSArray,其中包含 MKMapConcerates,这在我的实现中是需要的:

#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;
}

非常有用的文章! 我已经在 JavaScript 中实现了这个,特此说明我的代码。我已经成功地使用了这个。

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)]);
}

非常好的解决方案,正是我迅速项目所需要的,所以这里有个快速端口。谢谢 & 这里还有一个游乐场项目: 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)")

原始函数的 Javascript 版本

/**
* 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);
}

在 Django 中,这是微不足道的(而且实际上是有效的,我遇到了许多解决方案不能正确返回纬度的负值的问题)。

例如,假设您正在使用 Django-geopostcode(我是其作者)。

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是一个 Django Point实例,然后可以用来做一些事情,例如检索距离该中心点10公里以内的所有物体;

Locality.objects.filter(point__distance_lte=(point, D(km=10)))\
.annotate(distance=Distance('point', point))\
.order_by('distance')

将其更改为原始 Python 是微不足道的;

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

在引擎盖下 Django 正在使用 GEOS-更多细节在 https://docs.djangoproject.com/en/1.10/ref/contrib/gis/geos/

在 PHP 中取出对象。给定坐标对数组,返回中心。

/**
* 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);
}

从第四条开始

下面是查找中心点的 Python 版本。 Lat1和 lon1是经纬度列表。 它会恢复中心点的经纬度。

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

我用 javascript 完成了这个任务,如下所示

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;
}

为了表示对这个线程的赞赏,这里是我对 Ruby 实现的一点贡献,希望我能为某人节省几分钟的宝贵时间:

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

我使用了从 www.geomidpoint.com 中得到的一个公式,并编写了下面的 c + + 实现。arraygeocoords是我自己的类,它们的功能应该是不言而喻的。

/*
* 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;
}

如果有人需要 Java 版本。常量定义静态不计算他们两次。

/**************************************************************************************************************
*   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);
}

Dart 颤动寻找多纬度、经度中心点的实现。

进口数学软件包

import 'dart:math' as math;

经纬度名单

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);
}

下面是基于@Yodacheese 的 C # 回答,使用谷歌地图 api 的 Android 版本:

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);


}

在 app build.gradle 中添加:

implementation 'com.google.android.gms:play-services-maps:17.0.0'

计算多个纬度/经度坐标对的中心点

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)
};
}

Scala 版本:

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);
}


所以这些答案中的许多只是一种奇怪方法的变体,这种方法没有找到包含所有点的边界盒的真正中心。相反,它找到了大多数点的中心(各种加权中心)。如果你想得到所有点的真正中心,而不考虑聚类和权重,你可以得到边界框,并且很容易找到这4个角的中心。如果你不关心地球曲率的因子,你可以做一些像(C # 代码)这样简单的事情:

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);