首页 文章

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

提问于
浏览
123

给定一组纬度和经度点,如何计算该集合中心点的纬度和经度(也就是将所有点的视图居中的点)?

编辑:我用过的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)

13 回答

  • 42

    只需平均它们的简单方法就有奇怪的边缘情况,当它们从359'回到0'时会有角度 .

    A much earlier question on SO询问是否找到一组罗盘角度的平均值 .

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

    • 将每个纬度/经度对转换为单位长度的3D矢量 .

    • 对每个向量求和

    • 标准化生成的矢量

    • 转换回球面坐标

  • 1

    谢谢!这是使用度数的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);
        }
    
  • 0

    我发现这篇文章非常有用所以这里是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());
    }
    
  • 4

    非常有用的帖子!我已经用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)]);
    }
    
  • 71

    为了节省一两分钟的时间,这里是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;
    }
    
  • 38

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

    如果您有兴趣获得点的非常简化的“中心”(例如,简单地将 Map 居中到您的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坐标 .

  • 2

    非常好的解决方案,正是我的快速项目所需要的,所以这里's a swift port. thanks & here'也是一个游乐场项目: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)")
    
  • 12

    在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 实例,可用于执行诸如检索距该中心点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/

  • 1

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

    求出中心纬度的所有纬度的平均值以及中心经度的所有经度的平均值 .

    警告Emptor:这是近距离近似,当由于地球的曲率而偏离平均值超过几英里时,误差将变得难以驾驭 . 请记住,纬度和经度都是度数(不是真正的网格) .

  • 4

    如果你想考虑使用的椭球,你可以在这里找到公式http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.pdf

    见附件B.

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

  • 0

    如果您希望图像中显示所有点,则需要纬度和经度的极值,并确保您的视图包含具有您想要的任何边界的值 .

    (根据Alnitak的回答,你如何计算极值可能有点问题,但如果它们在经度的两边都有几度,那么你就会调用射击并采取正确的射程 . )

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

    若要将点保持在某个任意缩放级别的中心,请计算边界框的中心,使其“适合”上述点,并将该点保持为中心点 .

  • 9

    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的想法

相关问题