我尝试实现一个函数,该函数从通过纬度和经度值(以度为单位)表示的位置列表中给出平均值。旧版本的函数只是平均这两个值:
public static void CalculateMedianLocation(IEnumerable<GeographyPoint> locations, out Double latitude, out Double longitude)
{
Double totalLatitude = 0;
Double totalLongitude = 0;
var count = 0;
foreach (var location in locations)
{
totalLatitude += location.Latitude;
totalLongitude += location.Longitude;
count++;
}
if (count <= 0)
{
throw new ArgumentOutOfRangeException();
}
latitude= totalLatitude / count;
longitude = totalLongitude / count;
}
然而,这并不是一个很好的方法,因为可能导致扭曲。基于this answer类似的问题,我尝试使用笛卡尔坐标平均值将其更改为更好的方法:
private static Vector3 DegreesToVector(Double latitude, Double longitude)
{
var latitudeRadians = latitude.ToRadians();
var longitudeRadians = longitude.ToRadians();
var x = Math.Sin(latitudeRadians) * Math.Cos(longitudeRadians);
var y = Math.Sin(latitudeRadians) * Math.Sin(longitudeRadians);
var z = Math.Cos(latitudeRadians);
return new Vector3(x, y, z);
}
private static void VectorToDegrees(Vector3 vector, out Double latitude, out Double longitude)
{
latitude = Math.Atan2(vector.Z, Math.Sqrt(vector.X * vector.X + vector.Y * vector.Y)).ToDegrees();
longitude = Math.Atan2(-vector.Y, vector.X).ToDegrees();
}
public static void CalculateMedianLocation(IEnumerable<GeographyPoint> locations, out Double latitude, out Double longitude)
{
var total = new Vector3();
var count = 0;
foreach (var location in locations)
{
total += DegreesToVector(location.Latitude, location.Longitude);
count++;
}
if (count <= 0)
{
throw new ArgumentOutOfRangeException();
}
VectorToDegrees(total / count, out latitude, out longitude);
}
然而,这给了我完全错误的价值观,我不明白为什么。我检查了这个解决方案基于的数学,似乎一切都是正确的。我错过了什么?
编辑:我使用here中的Vector3实现,扩展ToDegrees()
和ToRadians()
实现如下:
public static Double ToRadians(this Double val)
{
return (Math.PI / 180.0) * val;
}
public static Double ToDegrees(this Double val)
{
return val * (180.0 / Math.PI);
}
这些都不应成为这个问题的原因。