math - puntos - Calcule el punto central de múltiples pares de coordenadas de latitud/longitud
latitud y longitud google maps (13)
¡Gracias! Aquí hay una versión C # de las soluciones de OP que usan grados. Utiliza la clase 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);
}
Dado un conjunto de puntos de latitud y longitud, ¿cómo puedo calcular la latitud y la longitud del punto central de ese conjunto (también conocido como un punto que centraría una vista en todos los puntos)?
EDIT: solución de Python que he usado:
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)
¡Publicación muy útil! Implementé esto en JavaScript, por este medio mi código. Lo he usado con éxito.
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)]);
}
Con el interés de posiblemente salvar a alguien un minuto o dos, esta es la solución que se usó en Objective-C en lugar de python. Esta versión toma un NSArray de NSValues que contienen MKMapCoordinates, que se solicitó en mi implementación:
+ (CLLocationCoordinate2D)centerCoordinateForCoordinates:(NSArray *)coordinateArray {
double x = 0;
double y = 0;
double z = 0;
for(NSValue *coordinateValue in coordinateArray) {
CLLocationCoordinate2D coordinate = [coordinateValue MKCoordinateValue];
double lat = Deg_to_Rad(coordinate.latitude);
double lon = Deg_to_Rad(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(Rad_to_Deg(resultLat), Rad_to_Deg(resultLon));
return result;
}
El enfoque simple de promediarlos tiene casos de bordes extraños con ángulos cuando se envuelven desde 359 ''a 0''.
Una pregunta mucho más antigua sobre SO preguntó acerca de encontrar el promedio de un conjunto de ángulos de brújula.
Una expansión del enfoque recomendado allí para las coordenadas esféricas sería:
- Convierta cada par latitud / longitud en un vector 3D de longitud de unidad.
- Suma cada uno de esos vectores
- Normaliza el vector resultante
- Convertir de nuevo a coordenadas esféricas
En Django esto es trivial (y de hecho funciona, tuve problemas con varias soluciones que no devolvían correctamente los valores negativos de latitud).
Por ejemplo, digamos que estás usando django-geopostcodes (de los cuales yo soy el autor).
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
es una instancia de Django Point
que luego se puede usar para hacer cosas tales como recuperar todos los objetos que están a 10 km de ese punto central;
Locality.objects.filter(point__distance_lte=(point, D(km=10)))/
.annotate(distance=Distance(''point'', point))/
.order_by(''distance'')
Cambiar esto a Python sin procesar es 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
Debajo del capó, Django está utilizando GEOS: más detalles en https://docs.djangoproject.com/en/1.10/ref/contrib/gis/geos/
Encontré esta publicación muy útil, así que aquí está la solución en PHP. He estado usando esto con éxito y solo quería salvar a otro desarrollador en algún momento.
/**
* 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());
}
Esto es lo mismo que un problema de promedio ponderado donde todos los pesos son iguales, y hay dos dimensiones.
Encuentra el promedio de todas las latitudes para la latitud de tu centro y el promedio de todas las longitudes para la longitud central.
Caveat Emptor: esta es una aproximación de distancia cercana y el error se volverá ingobernable cuando las desviaciones de la media sean más de algunas millas debido a la curvatura de la Tierra. Recuerde que las latitudes y longitudes son grados (no realmente una cuadrícula).
Fuera de objetos en PHP. Dada la matriz de pares de coordenadas, regresa al centro.
/**
* 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);
}
Tomado idea de # 4
Si desea que todos los puntos sean visibles en la imagen, querría los extremos en latitud y longitud y asegúrese de que su vista incluya esos valores con el borde que desee.
(Según la respuesta de Alnitak, la forma en que calcule los extremos puede ser un poco problemático, pero si se encuentran a unos pocos grados a cada lado de la longitud que se enrolla, llamará a la toma y tomará el rango correcto).
Si no desea distorsionar el mapa en el que estén estos puntos, ajuste la relación de aspecto del cuadro delimitador para que se ajuste a los píxeles que haya asignado a la vista, pero que aún incluya los extremos.
Para mantener los puntos centrados en un nivel de acercamiento arbitrario, calcule el centro del cuadro delimitador que "se ajusta a" los puntos como arriba, y mantenga ese punto como punto central.
Si desea tener en cuenta el elipsoide utilizado, puede encontrar las fórmulas aquí http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.pdf
ver el Anexo B
El documento contiene muchas otras cosas útiles
segundo
Si está interesado en obtener un "centro" muy simplificado de los puntos (por ejemplo, simplemente centrar un mapa en el centro de su polígono de gmaps), aquí hay un enfoque básico que funcionó para mí.
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;
}
Esto devuelve la coordenada latitudinal / latitud media para el centro de un polígono.
Versión de Javascript de la función original
/**
* 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);
}
soluciones muy agradables, justo lo que necesitaba para mi proyecto rápido, así que aquí hay un puerto rápido. gracias y también hay un proyecto de patio de recreo: 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)")