计算纬度和经度坐标的中心点

5
我希望您能提供一个优雅的解决方案,计算多个坐标之间的中心点(例如,简单地将地图居中于多边形的中心)。
表格: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
-----------------------------------------------

当前计算:

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

看一下kmeans算法 - http://phpir.com/clustering - Mark Baker
1
你正在计算最小和最大纬度/经度的平均值,为什么不直接计算所有纬度/经度的平均值呢? - miki
@miki 平均所有位置并不起作用。例如,柏林是示例中最东边的城市,伦敦是最西边的城市。如果我们添加了100个城镇,它们都位于柏林的稍微向西的位置,地图的西/东居中位置可能不会改变,但平均值会靠近柏林。 - chux - Reinstate Monica
3个回答

9

由于您正在使用Google Maps,因此可以使用getBounds()方法和getCenter()方法。

我已重新排列您的坐标以形成凸多边形(所有顶点都指向“外部”,远离中心)。该多边形通过将第一个坐标作为polygonCoords数组中的第一个和最后一个值来关闭。

请参见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()
});

3

将纬度和经度求平均在许多情况下都有效,但在某些情况下存在问题。例如,你有两个城市,东京(经度= 140)和西雅图(经度-122),你的平均经度是18,位于欧洲某处。你会期望更接近国际日期变更线,即相隔180度。

最直接、无问题的方法是将向量平均,就好像每个向量都起源于地球的中心。

伪代码(假设弧度)

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

[编辑更正球坐标为笛卡尔坐标]


看起来是个很好的答案,但是 z 是什么,而且你所说的 argxargy 是指 avgxavgy 吗?还是少了点什么? - acraig5075
-1:这个算法有问题或者缺失了某些内容:使用您提供的例子,Seattle(48,-122)和Tokyo(41,140),得到的结果是错误的(193,32),而且使用OP提供的数值时,纬度/经度的顺序也是错误的(7.1,52.4)? - acraig5075
@acraig5075 谢谢。算法没问题,但坐标映射出了问题。我的西雅图/东京中点现在在白令海(56.2, -175.0)。 - chux - Reinstate Monica
1
我为了第二个答案而贬低自己的帖子。该方法可以很好地找到位置的平均值,但并不像 OP 请求的那样找到中心点。有关详细信息,请参阅其他帖子。唉。 - chux - Reinstate Monica
让我们在聊天中继续这个讨论:http://chat.stackoverflow.com/rooms/35019/discussion-between-chux-and-acraig5075 - chux - Reinstate Monica

2

谷歌使用墨卡托投影,将地球视为一个纵向的圆柱体。 因此,问题是找到该投影的中心。

对于每个纬度/经度对,将其转换为地图缩放的x,y坐标(使用弧度):

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

接下来,对于x和y,找出最小值和最大值的平均值作为中心点。按照以下方式转换回经纬度。

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

如果不考虑圆柱形地球投影的循环性质,计算中心经度就没有问题。但是,如果经度位于东半球和西半球(一些为负数,一些为正数),则可能需要进行额外的工作。

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城市结果:(52.4 N,7.0 E)


这是我的第二个答案,因为第一个没有抓住OP帖子的关键点。由于它有一定的价值,因此保留。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接