Учитывая набор точек широты и долготы, как я могу вычислить широту и долготу центральной точки этого набора (она же точка, которая центрирует вид на все точки)?
EDIT: Решение на 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)
Спасибо! Здесь находится версия для C# ОП's решения, используя градуса. Он использует [система.Устройства.Расположение.GeoCoordinate][1] класс
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);
}
[1]: http://msdn.microsoft.com/en-us/library/system.device.location.geocoordinate(в=против 110).аспн
Простой подход простого усреднения имеет странные крайние случаи с углами, когда они поворачиваются от 359' обратно к 0'.
В гораздо более раннем вопросе на SO спрашивалось о нахождении среднего значения набора углов по компасу.
Расширение подхода, рекомендованного там для сферических координат, будет таким:
Я нашел этот пост очень полезен, так вот решение в 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());
}
Очень полезный пост! Я'вэ реализовали это в 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)]);
}
</код>
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);
}
В интересах возможно спасли кого-то здесь на минуту или две, - это решение, которое используется в Objective-C вместо питона. Эта версия имеет массив 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;
}
очень хорошие решения, в чем я нуждался для моего Свифта проект, поэтому здесь'с быстрым портом. благодаря &ампер; здесь's также площадка проекта: 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 это тривиальные (и на самом деле работает, у меня были проблемы с рядом решений, не правильно возвращаясь негативы для широты).
Например, позвольте'говорят, что вы используете Джанго-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
"точка" - это Джанго "точка" экземпляр, который затем может быть использован, чтобы делать такие вещи, как извлечь все объекты, которые находятся в 10км от этой центральной точки;
Locality.objects.filter(point__distance_lte=(point, D(km=10)))\
.annotate(distance=Distance('point', point))\
.order_by('distance')
Изменение этого в RAW 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
Под капотом Джанго использует Геос - подробности на https://docs.djangoproject.com/en/1.10/ref/contrib/gis/geos/
Если вы заинтересованы в получении очень упрощенный 'центр' из пунктов (например, чтобы поменять карту в центр 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;
}
Это возвращает среднего широта/СПГ координаты центра многоугольника.
Вот Python версии для нахождения точки центра. В lat1 и lon1 списки широты и долготы. он будет retuen широту и долготу точки.
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
Это то же самое, что и задача взвешенного среднего, где все веса одинаковы, и есть два измерения.
Найдите среднее значение всех широт для центральной широты и среднее значение всех долгот для центральной долготы.
Осторожно: Это приближение к близкому расстоянию, и ошибка станет неуправляемой, когда отклонения от среднего значения превысят несколько миль из-за кривизны Земли. Помните, что широты и долготы - это градусы (не совсем сетка).
Если вы хотите учитывать эллипсоид используется, Вы можете найти формулы здесь http://www.ordnancesurvey.co.uk/oswebsite/gps/docs/A_Guide_to_Coordinate_Systems_in_Great_Britain.pdf
см. приложение Б
Документ содержит много других полезных вещей
Б
Я использовал формулу, которую я получил от www.geomidpoint.com и написал следующее реализация c++. В массив
и `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;
}
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);
}
Если вы хотите, чтобы все точки были видны на изображении, вам нужны экстремумы широты и долготы, и убедитесь, что ваше представление включает эти значения с любой границей, которую вы хотите.
(Судя по ответу Alnitak'а, способ вычисления экстремумов может быть немного проблематичным, но если они находятся в нескольких градусах по обе стороны от долготы, которая оборачивается вокруг, то вы'азывайте и берите нужный диапазон).
Если вы не хотите искажать карту, на которой находятся эти точки, то отрегулируйте соотношение сторон ограничивающей рамки так, чтобы она поместилась во все пиксели, которые вы выделили для просмотра, но при этом включала экстремумы.
Чтобы сохранить точки в центре при произвольном уровне масштабирования, вычислите центр ограничивающей рамки, которая "просто помещается" в точки, как указано выше, и сохраните эту точку в качестве центральной.
Из объекта в 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
Я сделал эту задачу на 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;
}
В качестве благодарности за эту тему, вот мой маленький вклад с внедрением в Руби, надеясь, что я спасу кого-то несколько минут из своего драгоценного времени:
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