Java 更正我计算两点之间大圆距离的算法
我使用下面的算法来计算两点之间的距离,但它产生了不合理的结果。我错在哪里Java 更正我计算两点之间大圆距离的算法,java,algorithm,Java,Algorithm,我使用下面的算法来计算两点之间的距离,但它产生了不合理的结果。我错在哪里 private static double distFrom(double latA, double lngA, double latB, double lngB) { double pk = 180/3.14169; double a1 = latA / pk; double a2 = lngA / pk; double b1 = latB / pk; double b2 = l
private static double distFrom(double latA, double lngA, double latB, double lngB) {
double pk = 180/3.14169;
double a1 = latA / pk;
double a2 = lngA / pk;
double b1 = latB / pk;
double b2 = lngB / pk;
double t1 = Math.cos(a1)*Math.cos(a2)*Math.cos(b1)*Math.cos(b2);
double t2 = Math.cos(a1)*Math.sin(a2)*Math.cos(b1)*Math.sin(b2);
double t3 = Math.sin(a1)*Math.sin(b1);
double tt = Math.acos(t1 + t2 + t3);
return 3959*tt;
}
将3.14169替换为Math.PI将略有改进,因为PI的第一位数字是3.14159
此外,还有很多算法。试着用英里、公里和令人作呕的英里来回答问题。最好用标准字母——L代表经度,B代表纬度。你使用的方法的公式很奇怪。根据维基的说法,这将是:
double B1 = latA / pk;
double B2 = latB / pk;
double dL = (lngA-lngB) / pk;
double t1 = Math.cos(B1)*Math.cos(B2)*Math.cos(dL);
double t2 = Math.sin(B1)*Math.sin(B2);
double tt = Math.acos(t1 + t2);
更少的计算->更少的舍入误差
但是请注意,这个基于acos的公式仅在数学上是正确的,而在计算上是错误的——它在短距离内具有较低的浮点精度!在上述基础上,有更好的基于asin的短距离计算公式和基于atan的通用公式。从中选择一个。这里是来自(“Vincenty公式的特例”)的atan版本的一个实现,它“适用于所有距离”:
/**
*WGS84中定义的平均地球半径,单位为公里(KM)
*/
公共静态最终双地球半径=6371.0087714;
/**
*以美国英里为单位的平均地球半径,以公里为单位换算
*/
公共静态最终双地球半径=0.621371192;
/**
*以海里为单位的平均地球半径,以公里为单位换算
*/
公共静态最终双地球半径=0.539956803;
/**
*使用“特殊”计算球体上两点之间的距离
*Vincenty公式的案例“用于精确性。这使用了更多的三角函数
*调用,但对于所有距离都更精确。
*
*
*有关更多详细信息,请参阅。
*
*@param-startat
*起点纬度
*@param starton
*起点经度
*@param endLat
*终点的纬度
*@param endLon
*终点的经度
*@param半径
*要使用的球形地球的半径
*
*@返回给定半径的点之间的距离
*/
公共静态双大环距离(双止点、双止点、双止点、双止点、双半径)
{
//换算成弧度
双lonDelta=数学托拉迪安(starton-endLon);
双拉特1=数学托拉迪安(startLat);
双拉特2=数学环面(endLat);
//计算重复使用的值
双cos1=数学cos(lat1);
双sin1=数学sin(lat1);
double cos2=数学cos(lat2);
双sin2=数学sin(lat2);
double cosDelta=数学cos(lonDelta);
double top1=cos2*Math.sin(lonDelta);
top1*=top1;//平方
双top2=(cos1*sin2)-(sin1*cos2*cosDelta);
top2*=top2;//平方
双头=数学sqrt(top1+top2);
双底=(sin1*sin2)+(cos1*cos2*cosDelta);
双弧度=数学坐标(上/下);
返回半径*rad;
}
为地球半径选择不同的值可设置所需的单位。这适用于任何半径的球体,因此,只要您知道起点和终点的纬度/经度,就可以使用3389.5公里的半径计算火星上的距离。您能更详细地解释一下您的输出是什么、您期望的输出是什么以及您已经尝试解决的问题吗,这是哈弗森公式,你可以使用这样一个实现:如果你告诉我们你给这个方法“赋予”了哪些值,得到了哪些结果,期望得到哪些结果,那就太好了。@gilleain的答案是有效的。谢谢。@EpicPandaForce事实上我更喜欢使用公里,但在我的国家,仍然使用英制。如果至少有一个数字是双精度的,那么整个结果就是双精度的,所以不,整数除法是不正确的。返回值也是一样。。成功:返回3959.0*tt@WvdL-不需要,变量tt是双精度的,因此计算和结果是双精度的!最后一行中的系数有4个数字,因此,使用5个以上的PI数字不会得到任何结果!当然,Pi中有一个误差,但它小于最后一行中的舍入误差。在我的公司,需要坚持Java编码规则,计算的距离不必100%准确。我的算法有问题,因为结果比预期的要大10倍。我们不担心小于一英里的错误。距离肯定是不正确的,因为我们还覆盖了大圆算法无法处理的山区。@sandahung如果使用浮点精度差的算法,用4个有意义的数字计数,结果中很容易得到零意义的数字。i、 这将是完全错误的。如果您不能将精度与结果一起计算,并且显然无法计算,请仅使用良好/定义良好/具有高浮点精度的公式。@Sandahung您能给出一些结果比预期结果大10倍的测试用例吗?仅尝试几点,您的方法似乎给出了相当合理的结果(最多可能有1-2%的错误)。@SamYonnou如果常数有4位数字,1%的错误已经是可接受的100倍。因此,代码必须更改-Sandah Aung在代码中是绝对正确的。@Sandahung 1。使用浮点数永远无法获得精确的结果。但你必须设定可接受的错误——绝对或相对形式。2.Java编码规则与任何一组公认的术语都没有矛盾。例如,没有人使用人造卫星,而是使用人造卫星。另一个问题是,如果您的团队不知道标准大地测量术语。是的,使用你所理解的。对不起,我会用标准的方法。3.一英里的距离是多少?4.山区和大圈之间有什么联系?你不能和大地水准面一起工作。
/**
* Mean earth radius in Kilometers (KM) as defined in WGS84
*/
public static final double EARTH_RADIUS_KM = 6371.0087714;
/**
* Mean earth radius in US Miles, converted from Kilometers
*/
public static final double EARTH_RADIUS_MI = EARTH_RADIUS_KM * 0.621371192;
/**
* Mean earth radius in Nautical Miles, converted from Kilometers
*/
public static final double EARTH_RADIUS_NM = EARTH_RADIUS_KM * 0.539956803;
/**
* Calculates the distance between two points on a sphere using a "special
* case of the Vincenty formula" for accuracy. This uses more trigonometric
* calls than other formulas, but is more accurate for all distances.
* <p/>
*
* For more details, see <a href=
* "https://en.wikipedia.org/wiki/Great-circle_distance">https://en.wikipedia.org/wiki/Great-circle_distance</a>.
*
* @param startLat
* The starting point's latitude
* @param startLon
* The starting point's longitude
* @param endLat
* The ending point's latitude
* @param endLon
* The ending point's longitude
* @param radius
* The radius of the spherical earth to be used
*
* @return The distance between the points for the given radius
*/
public static double greatCircleDistance( double startLat, double startLon, double endLat, double endLon, double radius )
{
// convert to radians
double lonDelta = Math.toRadians( startLon - endLon );
double lat1 = Math.toRadians( startLat );
double lat2 = Math.toRadians( endLat );
// compute repeatedly used values
double cos1 = Math.cos( lat1 );
double sin1 = Math.sin( lat1 );
double cos2 = Math.cos( lat2 );
double sin2 = Math.sin( lat2 );
double cosDelta = Math.cos( lonDelta );
double top1 = cos2 * Math.sin( lonDelta );
top1 *= top1; // squared
double top2 = (cos1 * sin2) - (sin1 * cos2 * cosDelta);
top2 *= top2; // squared
double top = Math.sqrt( top1 + top2 );
double bottom = (sin1 * sin2) + (cos1 * cos2 * cosDelta);
double rad = Math.atan( top / bottom );
return radius * rad;
}