给定一组纬度和经度点,我如何计算该组中心点的纬度和经度(也就是将视图集中在所有点上的点)?
编辑:我使用过的 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)
z
?
谢谢!这是使用度数的 OP 解决方案的 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);
}
当它们从 359' 回绕到 0' 时,仅平均它们的简单方法会出现带有角度的奇怪边缘情况。
A much earlier question on SO 询问如何找到一组罗盘角度的平均值。
那里为球坐标推荐的方法的扩展将是:
将每个纬度/经度对转换为单位长度的 3D 向量。
对每个向量求和
归一化结果向量
转换回球坐标
我发现这篇文章非常有用,所以这里是 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());
}
很有用的帖子!我已经在 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)]);
}
原函数的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);
}
为了可能节省一两分钟的时间,这里是在 Objective-C 中而不是 python 中使用的解决方案。此版本采用包含 MKMapCoordinates 的 NSValues 的 NSArray,这在我的实现中被调用:
#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>
并使用 GLKMathDegreesToRadians
和 GLKMathRadiansToDegrees
非常好的解决方案,正是我的 swift 项目所需要的,所以这里有一个 swift 端口。谢谢&这里还有一个游乐场项目: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 版本,如果有人需要的话。常量定义为 static 以不计算两次。
/**************************************************************************************************************
* 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);
}
如果您有兴趣获得一个非常简化的点“中心”(例如,简单地将地图居中到 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 坐标。
在 Django 中,这是微不足道的(实际上有效,我遇到了一些解决方案没有正确返回纬度负数的问题)。
例如,假设您正在使用 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
实例,然后可用于执行诸如检索该中心点 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/ 上的更多详细信息
这是基于@Yodacheese 使用 Google Maps api 的 C# 答案的 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);
}
在应用程序 build.gradle 添加:
implementation 'com.google.android.gms:play-services-maps:17.0.0'
Flutter 的 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);
}
这是查找中心点的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
这与加权平均问题相同,其中所有权重都相同,并且有两个维度。
找到您的中心纬度的所有纬度的平均值和中心经度的所有经度的平均值。
注意事项:这是一个近距离的近似值,当由于地球的曲率与平均值的偏差超过几英里时,误差将变得难以控制。请记住,纬度和经度是度数(不是真正的网格)。
如果您想考虑所使用的椭球体,您可以在此处找到公式 http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.pdf
见附件 B
该文档包含许多其他有用的东西
乙
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中获取想法
我在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;
}
Dart/Flutter 计算多个经纬度坐标对的中心点
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)
};
}
这些答案中的许多只是一种奇怪方法的变体,它没有找到包含所有点的边界框的真正中心。相反,它会找到大多数点的中心(某种加权中心)。如果您想要所有点的真实中心而不考虑聚类和权重,您可以获得边界框并轻松找到这 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);
如果您希望图像中的所有点都可见,您需要纬度和经度的极值,并确保您的视图包含这些值以及您想要的任何边框。
(根据 Alnitak 的回答,您如何计算极值可能有点问题,但如果它们在环绕的经度的任一侧有几度,那么您将做主并采取正确的范围。)
如果您不想扭曲这些点所在的任何地图,请调整边界框的纵横比,使其适合您分配给视图但仍包含极值的任何像素。
要将点保持在某个任意缩放级别的中心,请计算“恰好适合”上述点的边界框的中心,并将该点保持为中心点。
作为对这个线程的感谢,这是我对 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++ 实现。 array
和 geocoords
是我自己的类,其功能应该是不言自明的。
/*
* 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;
}
斯卡拉版本:
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);
}