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

给定一组经纬度点,如何计算该组中心点的纬度和经度(又称以点为中心的点)?

编辑:我用过的Python解决scheme:

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) 

只是平均他们的简单方法有奇怪的边缘情况,当他们从359'回到0'。

关于SO的一个更早的问题问到如何find一组指南针angular度的平均值。

推荐用于球坐标的方法的扩展将是:

  • 将每个纬度/长度对转换为单位长度的3Dvector。
  • 总结每个这些向量
  • 规范化结果向量
  • 转换回球面坐标

谢谢! 这里是一个使用度的OP解决scheme的C#版本。 它利用了System.Device.Location.GeoCoordinate类

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

我发现这个post非常有用,所以这里是PHP的解决scheme。 我一直在使用这个成功,只是想保存另一个dev一些时间。

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

非常有用的职位! 我已经在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. eg [[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)]); } 

为了节省一两分钟的时间,下面是在Objective-C中使用的解决scheme,而不是python。 这个版本需要包含MKMapCoordinates的NSArray的NSArray,在我的实现中被调用:

 + (CLLocationCoordinate2D)centerCoordinateForCoordinates:(NSArray *)coordinateArray { double x = 0; double y = 0; double z = 0; for(NSValue *coordinateValue in coordinateArray) { CLLocationCoordinate2D coordinate = [coordinateValue MKCoordinateValue]; double lat = Deg_to_Rad(coordinate.latitude); double lon = Deg_to_Rad(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(Rad_to_Deg(resultLat), Rad_to_Deg(resultLon)); return result; } 

原版function的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); } 

如果你有兴趣获得一个非常简单的“中心点”(例如,简单地将一张地图放到你的gmaps多边形的中心),那么这里有一个基本的方法可以帮助我。

 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坐标。

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

在Django这是微不足道的(实际上,我有问题的一些解决scheme不正确地返回负值纬度)。

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

 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实例,然后可以用它来执行一些操作,例如检索距离该中心点10km范围内的所有对象;

 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/

如果你想考虑使用的椭球,你可以在这里find公式oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.html

见附件B

该文件包含许多其他有用的东西

这与所有权重相同的加权平均问题是相同的,并且有两个维度。

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

注意力敏感者:这是一个近距离的近似值,当由于地球曲率而偏离平均值超过几英里时,误差将变得不稳定。 请记住,纬度和经度是度(不是真正的网格)。

如果你想让所有的点在图像中可见,你需要经纬度的极值,并确保你的视图包含你想要的任何边界的值。

(从Alnitak的回答来看,你如何计算极值可能有点问题,但是如果它们在经度的任何一边环绕几度,那么你就可以调用这个射击并且select正确的范围)。

如果您不想扭曲这些点所在的任何地图,请调整边界框的纵横比,使其适合您分配给视图的任何像素,但仍包含极值。

为了保持以任意缩放级别为中心的点,计算边界框的中心,使之“恰好”符合上述的点,并将该点作为中心点。

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

从#4采取了想法

Swift 4版本

这是基于ppoh71的答案。 谢谢你的好问题和答案!

 func getCenterCoord(locationCoordinates: [CLLocationCoordinate2D]) -> CLLocationCoordinate2D { var x: Double = 0.0 var y: Double = 0.0 var z: Double = 0.0 for coordinatePair in locationCoordinates { let lat = coordinatePair.latitude * .pi / 180 let long = coordinatePair.longitude * .pi / 180 x += cos(lat) * cos(long) y += cos(lat) * sin(long) z += sin(lat) } x = x / Double(locationCoordinates.count) y = y / Double(locationCoordinates.count) z = z / Double(locationCoordinates.count) let resultLong = atan2(y, x) let resultHyp = sqrt(x * x + y * y) let resultLat = atan2(z, resultHyp) let result = CLLocationCoordinate2D(latitude: resultLat * 180 / .pi, longitude: resultLong * 180 / .pi) return result }