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)
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
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);
}
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());
}
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)]);
}
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;
}
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);
}
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.
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)")
à 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 /
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).
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
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.
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