Calculer le point central de multiples paires de coordonnées latitude/longitude

étant donné un ensemble de points de latitude et de longitude, Comment puis-je calculer la latitude et la longitude du point central de cet ensemble (alias un point qui centrerait une vue sur tous les points)?

EDIT: solution Python que j'ai utilisé:

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)
122
demandé sur Gio 2011-07-13 01:11:17

13 réponses

l'approche simple de juste les moyennes a des cas de bord bizarres avec des angles quand ils enveloppent de 359' retour à 0'.

A question beaucoup plus tôt sur SO a demandé de trouver la moyenne d'un ensemble d'angles de compas.

une extension de l'approche recommandée pour les coordonnées sphériques serait:

  • Convertissez chaque paire lat/long en vecteur 3D de longueur unitaire.
  • somme de chacun de ces vecteurs
  • normaliser le vecteur résultant
  • revenir aux coordonnées sphériques
42
répondu Alnitak 2017-05-23 11:47:32

Merci! Voici une version C# des solutions OP utilisant des degrés. Il utilise le système .Appareil.Emplacement.GeoCoordinate classe

    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);
    }
69
répondu Yodacheese 2015-05-04 16:00:44

j'ai trouvé ce post très utile donc voici la solution en PHP. Je l'ai utilisé avec succès et je voulais juste sauver un autre dev un certain temps.

/**
 * 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());
}
38
répondu Tom Tucker 2015-05-04 16:00:52

Très utile post! J'ai implémenté ceci en JavaScript, voici mon code. J'ai utilisé cela avec succès.

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

22
répondu Gio 2015-07-29 12:09:06

dans l'intérêt de sauver peut-être quelqu'un une minute ou deux, voici la solution qui a été utilisée dans Objective-C au lieu de python. Cette version prend une série de valeurs NS qui contiennent MKMapCoordinates, ce qui a été demandé dans ma mise en œuvre:

#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;
}
11
répondu Daryll H. 2018-06-01 19:45:02

version Javascript de la fonction originale

/**
 * 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);
}
8
répondu Kelvin Spencer 2017-02-14 19:39:43

si vous êtes intéressé à obtenir un 'centre' très simplifié des points (par exemple, pour simplement centrer une carte au centre de votre polygone gmaps), alors voici une approche de base qui a fonctionné pour moi.

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

renvoie la coordonnée moyenne lat/lng pour le centre d'un polygone.

4
répondu Kerry Emerson 2013-05-15 09:55:36

très agréable solutions, juste ce dont j'avais besoin pour mon projet swift, swift port. merci et voici aussi un projet de terrain de jeu: 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
répondu ppoh71 2016-06-02 15:12:28

à Django c'est trivial (et ça marche, j'ai eu des problèmes avec un certain nombre de solutions ne rendant pas correctement les négatifs pour la latitude).

par exemple, disons que vous utilisez django-géopostcodes (dont je suis l'auteur).

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 est une instance Django Point qui peut ensuite être utilisée pour faire des choses telles que récupérer tous les objets qui sont à moins de 10km de ce point central;

Locality.objects.filter(point__distance_lte=(point, D(km=10)))\
    .annotate(distance=Distance('point', point))\
    .order_by('distance')

changer ceci en Python brut est trivial;

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

sous le capot Django utilise GEOS - plus de détails à https://docs.djangoproject.com/en/1.10/ref/contrib/gis/geos /

2
répondu alexhayes 2017-03-28 13:07:43

c'est la même chose qu'un problème de moyenne pondérée où tous les poids sont les mêmes, et il y a deux dimensions.

trouvez la moyenne de toutes les latitudes pour votre latitude centrale et la moyenne de toutes les longitude pour la longitude centrale.

caveat Emptor: c'est une approximation de distance proche et l'erreur deviendra indisciplinée quand les déviations de la moyenne sont plus de quelques miles en raison de la courbure de la Terre. Rappeler que les latitudes et les longitude sont des degrés (pas vraiment une grille).

1
répondu jpredham 2011-07-12 21:17:42

Si vous souhaitez prendre en compte l'ellipsoïde utilisé, vous pouvez trouver les formules ici http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.pdf

voir Annexe B

Le document contient beaucoup d'autres choses utiles

B

1
répondu Bill 2012-04-15 10:11:24

si vous voulez que tous les points soient visibles dans l'image, vous voulez les extrêmes en latitude et longitude et assurez-vous que votre vue inclut ces valeurs avec n'importe quelle frontière que vous voulez.

(D'après la réponse D'Alnitak, la façon dont vous calculez les extrema peut être un peu problématique, mais s'ils sont à quelques degrés de chaque côté de la longitude qui entoure, alors vous appellerez le tir et prendre la bonne distance.)

si vous ne voulez pas déformer quelle que soit la carte sur laquelle ces points sont situés, ajustez ensuite le rapport d'aspect de la zone limitante pour qu'il corresponde à tous les pixels que vous avez alloués à la vue, mais inclut toujours l'extrema.

pour garder les points centrés à un certain niveau de zoom arbitraire, calculez le centre de la zone limite qui" correspond juste " aux points comme ci-dessus, et gardez ce point comme le point central.

0
répondu John 2011-07-12 21:23:39

de l'objet en PHP. Donné le tableau des paires de coordonnées, retourne le centre.

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

Prises à l'idée de #4

0
répondu DabitNG 2017-06-20 14:25:32