更快速地计算两点之间的地理距离

时间:2011-06-20 09:14:19

标签: java optimization geolocation gps gis

我从互联网上的某个地方借用了以下方法(不记得在哪里)。但它正在做一个直接的过程,找到两个GPS点之间的距离。它运行得很好,除了它可能有点慢,因为我在数百万点运行它。 我想知道是否有人知道一种计算成本更低的方法。

准确度需要在“正确”的一般区域,但不需要100%准确。

private double distFrom(double lat1, double lng1, double lat2, double lng2) {
    double earthRadius = 3958.75;
    double dLat = Math.toRadians(lat2-lat1);
    double dLng = Math.toRadians(lng2-lng1);
    double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
           Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
           Math.sin(dLng/2) * Math.sin(dLng/2);
    double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
    return   earthRadius * c;
  }
}

P.s我确实找到了许多其他相关问题,但他们并没有真正关注我的速度问题。

4 个答案:

答案 0 :(得分:17)

如果您不介意忽略地球的轻微扁率(并且您发布的Haversine代码就是这样做的话),请考虑将所有球形(纬度/长度)坐标预转换为3D 单位长度< / strong>笛卡尔坐标首先,按:

  

http://en.wikipedia.org/wiki/Spherical_coordinate_system

然后,笛卡尔坐标p1p2之间的球面距离就是:

r * acos(p1 . p2)

由于p1p2将具有单位长度,因此每对减少四次乘法,两次加法和一次逆触发操作。

另请注意,点积的计算是优化的理想候选者,例如:通过GPU,MMX扩展,矢量库等

此外,如果您的意图是按距离订购对,可能忽略更远的对,您可以通过对列表中的列表进行排序来推迟等式中昂贵的r*acos()部分。点积值,因为对于所有有效输入(即范围[-1, 1]),它保证:

acos(x) < acos(y) if x > y

然后,您只需获取您真正感兴趣的acos()值。

Re:使用acos()时可能存在的不准确之处,如果您使用的是单精度float变量,那么这些不准确。使用带有16位有效数字的double可以使距离精确到1米或更短。

答案 1 :(得分:1)

这是thersineine算法,将为您提供相当高的准确度。

如果它真的是“数百万”点,也许实现你所做的计算缓存...如果你遇到一对坐标,这两个坐标都足够接近你距离的一对坐标已经计算过,然后使用缓存的值?

或者尝试缓存一些中间步骤,例如弧度转换程度。

答案 2 :(得分:1)

如果你牺牲精确度,你可以做出一些改进。据我记忆,对于小sin(x)x approximately等于x。此外,您似乎计算了多次相同的事情,例如:Math.sin(dLat/2)(实际上可以近似为dLat/2,如上所述。)

但是,如果您正在进行数百万次这样的操作,我会在其他地方。

  • 您的算法是否最佳?也许你做了太多简单的计算?

  • 如果积分来自数据库,您可以在数据库服务器端执行计算作为存储过程吗?

  • 如果您正在寻找最近的积分,您能以某种方式索引它们吗?

  • 地理空间索引可以帮助您吗?

答案 3 :(得分:1)

您可以尝试使用球面三角函数的余弦定律:

a = sin(lat1) * sin(lat2)
b = cos(lat1) * cos(lat2) * cos(lon2 - lon1)
c = arccos(a + b)
d = R * c

但是 对于短距离来说是不准确的(可能只是略微加快)。

有一个完整的讨论here。 然而,胡言乱语的公式是最正确的方式,所以除了别人的建议之外,你可能没有多少办法。 @ Alnitak的答案可能有效,但笛卡尔转换的球形不一定很快。