如何计算经度和纬度指定的两点之间的距离?
为了澄清起见,我想要以千米为单位的距离;这些要点使用 WGS84 系统,我想了解可用方法的相对准确性。
该链接可能会对您有所帮助,因为它详细说明了使用Haversine 公式计算距离的方法。
摘抄:
该脚本(使用 Java 语言)使用 “Haversine” 公式计算两点之间的大圆距离 - 即地球表面上的最短距离。
function getDistanceFromLatLonInKm(lat1,lon1,lat2,lon2) {
var R = 6371; // Radius of the earth in km
var dLat = deg2rad(lat2-lat1); // deg2rad below
var dLon = deg2rad(lon2-lon1);
var a =
Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(deg2rad(lat1)) * Math.cos(deg2rad(lat2)) *
Math.sin(dLon/2) * Math.sin(dLon/2)
;
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
var d = R * c; // Distance in km
return d;
}
function deg2rad(deg) {
return deg * (Math.PI/180)
}
我需要为项目计算很多点之间的距离,因此我继续尝试优化代码,我在这里找到了。平均而言,在不同的浏览器中,我的新实现比最受好评的答案快 2 倍 。
function distance(lat1, lon1, lat2, lon2) {
var p = 0.017453292519943295; // Math.PI / 180
var c = Math.cos;
var a = 0.5 - c((lat2 - lat1) * p)/2 +
c(lat1 * p) * c(lat2 * p) *
(1 - c((lon2 - lon1) * p))/2;
return 12742 * Math.asin(Math.sqrt(a)); // 2 * R; R = 6371 km
}
最近我需要在 python 中做同样的事情,所以这是一个python 实现 :
from math import cos, asin, sqrt
def distance(lat1, lon1, lat2, lon2):
p = 0.017453292519943295 #Pi/180
a = 0.5 - cos((lat2 - lat1) * p)/2 + cos(lat1 * p) * cos(lat2 * p) * (1 - cos((lon2 - lon1) * p)) / 2
return 12742 * asin(sqrt(a)) #2*R*asin...
为了完整起见:Wiki 上的Haversine 。
这是一个 C#实现:
static class DistanceAlgorithm
{
const double PIx = 3.141592653589793;
const double RADIUS = 6378.16;
/// <summary>
/// Convert degrees to Radians
/// </summary>
/// <param name="x">Degrees</param>
/// <returns>The equivalent in radians</returns>
public static double Radians(double x)
{
return x * PIx / 180;
}
/// <summary>
/// Calculate the distance between two places.
/// </summary>
/// <param name="lon1"></param>
/// <param name="lat1"></param>
/// <param name="lon2"></param>
/// <param name="lat2"></param>
/// <returns></returns>
public static double DistanceBetweenPlaces(
double lon1,
double lat1,
double lon2,
double lat2)
{
double dlon = Radians(lon2 - lon1);
double dlat = Radians(lat2 - lat1);
double a = (Math.Sin(dlat / 2) * Math.Sin(dlat / 2)) + Math.Cos(Radians(lat1)) * Math.Cos(Radians(lat2)) * (Math.Sin(dlon / 2) * Math.Sin(dlon / 2));
double angle = 2 * Math.Atan2(Math.Sqrt(a), Math.Sqrt(1 - a));
return angle * RADIUS;
}
}
这是 Haversine 公式的 Java 实现。
public final static double AVERAGE_RADIUS_OF_EARTH_KM = 6371;
public int calculateDistanceInKilometer(double userLat, double userLng,
double venueLat, double venueLng) {
double latDistance = Math.toRadians(userLat - venueLat);
double lngDistance = Math.toRadians(userLng - venueLng);
double a = Math.sin(latDistance / 2) * Math.sin(latDistance / 2)
+ Math.cos(Math.toRadians(userLat)) * Math.cos(Math.toRadians(venueLat))
* Math.sin(lngDistance / 2) * Math.sin(lngDistance / 2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
return (int) (Math.round(AVERAGE_RADIUS_OF_EARTH_KM * c));
}
请注意,这里我们将答案四舍五入到最接近的公里。
非常感谢所有这些。我在我的 Objective-C iPhone 应用程序中使用了以下代码:
const double PIx = 3.141592653589793;
const double RADIO = 6371; // Mean radius of Earth in Km
double convertToRadians(double val) {
return val * PIx / 180;
}
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
double dlon = convertToRadians(place2.longitude - place1.longitude);
double dlat = convertToRadians(place2.latitude - place1.latitude);
double a = ( pow(sin(dlat / 2), 2) + cos(convertToRadians(place1.latitude))) * cos(convertToRadians(place2.latitude)) * pow(sin(dlon / 2), 2);
double angle = 2 * asin(sqrt(a));
return angle * RADIO;
}
纬度和经度以十进制表示。我没有在 asin()调用中使用 min(),因为我使用的距离非常小,以至于他们不需要它。
直到我在 Radians 中传递值之前,它给出的答案都是错误的 - 现在,它与从 Apple 的 Map 应用程序获取的值几乎相同:-)
额外更新:
如果您使用的是 iOS4 或更高版本,则 Apple 提供了一些方法来执行此操作,因此可以使用以下方法实现相同的功能:
-(double)kilometresBetweenPlace1:(CLLocationCoordinate2D) place1 andPlace2:(CLLocationCoordinate2D) place2 {
MKMapPoint start, finish;
start = MKMapPointForCoordinate(place1);
finish = MKMapPointForCoordinate(place2);
return MKMetersBetweenMapPoints(start, finish) / 1000;
}
这是一个简单的 PHP 函数,可以给出非常合理的近似值(误差范围在 +/- 1%以下)。
<?php
function distance($lat1, $lon1, $lat2, $lon2) {
$pi80 = M_PI / 180;
$lat1 *= $pi80;
$lon1 *= $pi80;
$lat2 *= $pi80;
$lon2 *= $pi80;
$r = 6372.797; // mean radius of Earth in km
$dlat = $lat2 - $lat1;
$dlon = $lon2 - $lon1;
$a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlon / 2) * sin($dlon / 2);
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
$km = $r * $c;
//echo '<br/>'.$km;
return $km;
}
?>
如前所述;地球不是一个球体。这就像马克 · 麦格威尔(Mark McGwire)决定练习的古老棒球一样 - 充满了凹痕和颠簸。更简单的计算(像这样)将其视为一个球体。
根据您在此不规则卵形上的位置以及点之间的距离(距离越近,绝对误差范围越小),不同的方法可能会或多或少精确。您的期望越精确,数学就越复杂。
有关更多信息: 维基百科地理距离
我在这里发布我的工作示例。
列出表中指定点之间的距离(我们使用随机点 - lat:45.20327,long:23.7806)且其纬度和经度小于 50 KM 的所有点(在 MySQL 中,表字段为 coord_lat 和 coord_long):
列出所有 DISTANCE <50(以公里为单位)(考虑到地球半径 6371 KM):
SELECT denumire, (6371 * acos( cos( radians(45.20327) ) * cos( radians( coord_lat ) ) * cos( radians( 23.7806 ) - radians(coord_long) ) + sin( radians(45.20327) ) * sin( radians(coord_lat) ) )) AS distanta
FROM obiective
WHERE coord_lat<>''
AND coord_long<>''
HAVING distanta<50
ORDER BY distanta desc
上面的示例已在 MySQL 5.0.95 和 5.5.16(Linux)中进行了测试。
在其他答案中,缺少r 中的实现。
计算两个点之间的距离是与相当简单distm
从功能geosphere
包:
distm(p1, p2, fun = distHaversine)
哪里:
p1 = longitude/latitude for point(s)
p2 = longitude/latitude for point(s)
# type of distance calculation
fun = distCosine / distHaversine / distVincentySphere / distVincentyEllipsoid
由于地球不是完美的球形,因此椭球的Vincenty 公式可能是计算距离的最佳方法。因此,您可以在geosphere
包中使用:
distm(p1, p2, fun = distVincentyEllipsoid)
当然,您不一定必须使用geosphere
软件包,也可以使用以下函数来计算以R
为底的距离:
hav.dist <- function(long1, lat1, long2, lat2) {
R <- 6371
diff.long <- (long2 - long1)
diff.lat <- (lat2 - lat1)
a <- sin(diff.lat/2)^2 + cos(lat1) * cos(lat2) * sin(diff.long/2)^2
b <- 2 * asin(pmin(1, sqrt(a)))
d = R * b
return(d)
}
在大多数情况下,haversine 绝对是一个很好的公式,其他答案已经包含了它,因此我不会占用空间。但重要的是要注意,无论使用什么公式(不仅是一个公式)。由于可能的准确性范围以及所需的计算时间很大。公式的选择比简单的没有头脑的答案需要更多的思考。
这是美国国家航空航天局(NASA)某人的帖子,是我在讨论这些选项时发现的最好的帖子
http://www.cs.nyu.edu/visual/home/proj/tiger/gisfaq.html
例如,如果您只是按半径 100 英里内的距离对行进行排序。平坦的地球公式将比 haversine 快得多。
HalfPi = 1.5707963;
R = 3956; /* the radius gives you the measurement unit*/
a = HalfPi - latoriginrad;
b = HalfPi - latdestrad;
u = a * a + b * b;
v = - 2 * a * b * cos(longdestrad - longoriginrad);
c = sqrt(abs(u + v));
return R * c;
注意,只有一个余弦和一个平方根。与 Haversine 公式相比,它们中的 9 分。
您可以使用 CLLocationDistance 中的构建来计算以下内容:
CLLocation *location1 = [[CLLocation alloc] initWithLatitude:latitude1 longitude:longitude1];
CLLocation *location2 = [[CLLocation alloc] initWithLatitude:latitude2 longitude:longitude2];
[self distanceInMetersFromLocation:location1 toLocation:location2]
- (int)distanceInMetersFromLocation:(CLLocation*)location1 toLocation:(CLLocation*)location2 {
CLLocationDistance distanceInMeters = [location1 distanceFromLocation:location2];
return distanceInMeters;
}
在您的情况下,如果您想将公里数除以 1000。