Рассчитать центральную точку нескольких пар координат широта / долгота

148

Учитывая набор точек широты и долготы, как я могу рассчитать широту и долготу центральной точки этого набора (то есть точки, которая будет центрировать вид по всем точкам)?

РЕДАКТИРОВАТЬ: 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)
Зик
источник
2
Относительно вашего решения: Вероятно, ваши ошибки не будут слишком большими с вашим предположением о сферической земле, но землю лучше описать как эллипсоид.
Джон
1
Написал это как функцию Python и поделился ею по адресу gist.github.com/3718961
Элвин
14
Очень важно отметить, что это предполагает, что ваши широта и долгота указаны в радианах! Я почесал голову некоторое время, не осознавая этого. Чтобы преобразовать в радианы из десятичного числа, умножьте десятичное число * pi / 180. Затем, чтобы преобразовать обратно в радианы в десятичные, умножьте на 180 / пи. HTH
Райан Гилл
1
Извините за опоздание, но мне было интересно, какова математика этого алгоритма, кто-то может посоветовать мне некоторые чтения, где это объясняется? Спасибо!
tonix
1
Что такое z, пожалуйста?
SoS

Ответы:

48

Простой способ их простого усреднения имеет странные граничные случаи с углами, когда они переходят от 359 'назад к 0'.

Гораздо раньше вопрос о SO вопрос о нахождении в среднем набора компаса углов.

Расширение подхода, рекомендуемого там для сферических координат, будет:

  • Преобразуйте каждую пару широта / длинна в трехмерный вектор единичной длины.
  • Суммируйте каждый из этих векторов
  • Нормализовать полученный вектор
  • Преобразовать обратно в сферические координаты
Альнитак
источник
6
Кажется хорошо, я сделал нечто подобное, основываясь на том, что я нашел на этом веб-сайте: geomidpoint.com/calculation.html .
Зик
4
downvoter - пожалуйста, объясните и предложите лучшее решение, если можете.
Альнитак
90

Спасибо! Вот C # версия решений OP с использованием степеней. Он использует класс 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);
    }
Yodacheese
источник
40

Я нашел этот пост очень полезным, так что вот решение в 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());
}
Том Такер
источник
1
Я использовал это решение, но оно дает какое-то неправильное решение - если я ищу центр некоторых координат на карте, он как бы «взвешивает» точки и стремится остаться там, где есть больше точек.
LowFieldTheory
2
@Alnitak Здесь мы хотим найти центр области, ограниченной координатами. Вы уверены, что прокомментировали правильное место?
LowFieldTheory
29

Очень полезный пост! Я реализовал это в 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)]);
}

Gio
источник
1
Я знаю, что сообщение старое, но не могли бы вы опубликовать ссылку или что-то, объясняющее Math позади алгоритма, который вы опубликовали? Спасибо!
tonix
Работал отлично! Спасибо
andrewoodleyjr
Я протестировал скрипт с помощью Google Apps Script, но результат не является точным центром трека. Это где-то рядом, но не прямо на трассе. Есть ли лучшая формула, чтобы получить точную среднюю точку на пути?
Дирк
12

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);
}
Кельвин Спенсер
источник
12

В интересах возможной экономии кому-то минуты или двух, вот решение, которое использовалось в Objective-C вместо Python. Эта версия принимает NSArray NSValues, которые содержат MKMapCoordinates, который был вызван в моей реализации:

#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;
}
Дэрил Х.
источник
2
Для всех, для чего это стоит, вместо того, чтобы использовать собственный макрос для градусов в радианах, импортируйте <GLKit/GLKMath.h>и используйте GLKMathDegreesToRadiansиGLKMathRadiansToDegrees
pnizzle 13.09.16
8

очень хорошие решения, именно то, что мне нужно для моего быстрого проекта, так что вот быстрый порт. спасибо и вот еще проект для детской площадки: 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)")
Питер Полманн
источник
4

Если вы заинтересованы в получении очень упрощенного «центра» точек (например, для простого центрирования карты по центру полигона 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 для центра многоугольника.

Керри Эмерсон
источник
4

В Django это тривиально (и на самом деле работает, у меня были проблемы с рядом решений, неправильно возвращающих отрицательные значения по широте).

Например, предположим, что вы используете django-геопосткоды ( автором которых я являюсь).

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это Pointэкземпляр Django, который затем можно использовать для выполнения таких задач, как извлечение всех объектов, которые находятся в пределах 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/

alexhayes
источник
3

Версия 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);
}
Стан Соколов
источник
3

Вот версия Android, основанная на ответе @ Yodacheese's C # с использованием API Карт Google:

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'
Хоссейн Амини
источник
2

Реализация Dart для Flutter, чтобы найти центральную точку для нескольких широты, долготы.

импорт математического пакета

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);
}
MSARKrish
источник
1

Это то же самое, что и проблема средневзвешенного значения, когда все веса одинаковы, и есть два измерения.

Найти среднее значение всех широт для вашей центральной широты и среднее значение всех долгот для центральной долготы.

Предостережение: это приближение на близком расстоянии, и ошибка станет неуправляемой, когда отклонения от среднего значения будут превышать несколько миль из-за кривизны Земли. Помните, что широта и долгота - это градусы (на самом деле это не сетка).

jpredham
источник
1
[-179,0], [+ 179,0] среднее значение в [0,0], что несколько далеко от правильного результата;)
Писквор покинул здание
1

Вне объекта в 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

DabitNG
источник
1
Это не будет работать для координат, пересекающих 180-й меридиан. Например, две продольные точки, -175 и 175, вернут центр 0 в вашем алгоритме, в результате чего реальный центр будет либо -180, либо 180.
Лебедка
1

Вот версия Python для нахождения центральной точки. Lat1 и lon1 - списки широты и долготы. это вернет широту и долготу центральной точки.

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
Фейсал
источник
1

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)
    };
  }
Мигель Чикин
источник
0

Если вы хотите, чтобы все точки были видны на изображении, вам нужны экстремумы по широте и долготе и убедитесь, что ваше представление включает эти значения с любой границей, которую вы хотите.

(Из ответа Альнитака, как вы можете вычислить экстремумы, может быть немного проблематично, но если они находятся на несколько градусов по обе стороны от долготы, которая оборачивается вокруг, тогда вы сделаете точный выстрел и выберете правильный диапазон.)

Если вы не хотите искажать какую-либо карту, на которой находятся эти точки, то отрегулируйте соотношение сторон ограничительной рамки так, чтобы оно соответствовало пикселям, которые вы выделили для вида, но все еще включает в себя экстремумы.

Чтобы точки центрировались на некотором произвольном уровне масштабирования, рассчитайте центр ограничительной рамки, которая «просто соответствует» точкам, как указано выше, и сохраните эту точку в качестве центральной точки.

Джон
источник
0

Я сделал эту задачу в 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;
}
Рениш Готеча
источник
0

В качестве благодарности за эту ветку, вот мой небольшой вклад в реализацию в 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
Фатос Морина
источник
0

Я использовал формулу, полученную с 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;
   }
Крис Ланг
источник