简体   繁体   English

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

[英]Quicker way to calculate geographic distance between two points

I borrowed the following method from somewhere on the internet (Can't remember where). 我从互联网上的某个地方借用了以下方法(不记得在哪里)。 But its doing a straight forward process, finding the distance between two gps points. 但它正在做一个直接的过程,找到两个GPS点之间的距离。 It works just fine, except that it may be a little slow, as I'm running it across millions of points. 它运行得很好,除了它可能有点慢,因为我在数百万点运行它。 I was wondering if anyone knows an approach that would be computationally less expensive. 我想知道是否有人知道一种计算上更便宜的方法。

The accuracy needs to be in the general area of 'correct' but doesn't need to be 100% accurate. 准确度需要在“正确”的一般区域,但不需要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;
  }
}

Ps I did indeed find a number of other relevant questions, but they don't really focus on my speed concern. 我确实找到了许多其他相关问题,但他们并没有真正关注我的速度问题。

If you don't mind ignoring the slight oblateness of the Earth (and your posted Haversine code does just that anyway) consider pre-converting all of your spherical (lat/long) coordinates into 3D unit-length cartesian coordinates first, per: 如果您不介意忽略地球的轻微扁率(并且您发布的Haversine代码就是这样做的话)考虑首先将所有球形(纬度/长度)坐标预先转换为3D 单位长度笛卡尔坐标,每个:

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

Then your spherical distance between cartesian coordinates p1 and p2 is simply: 那么笛卡尔坐标p1p2之间的球面距离就是:

r * acos(p1 . p2)

Since p1 and p2 will have unit length this reduces to four multiplications, two additions and one inverse trig operation per pair. 由于p1p2将具有单位长度,因此每对减少到四次乘法,两次加法和一次反向触发操作。

Also note that the calculation of dot products is an ideal candidate for optimisation, eg via GPU, MMX extensions, vector libraries, etc. 另请注意,点积的计算是优化的理想选择,例如通过GPU,MMX扩展,矢量库等。

Furthermore, if your intent is to order the pairs by distance, potentially ignoring more distant pairs, you can defer the expensive r*acos() part of the equation by sorting the list just on the dot product value since for all valid inputs (ie the range [-1, 1] ) it's guaranteed that: 此外,如果您的意图是按距离订购配对,可能忽略更远的配对,您可以通过在点积值上对列表进行排序来推迟等式中昂贵的r*acos()部分,因为对于所有有效输入(即范围[-1, 1] )保证:

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

You then just take the acos() of the values you're actually interested in. 然后你只需要获得你真正感兴趣的值的acos()

Re: the potential inaccuracies with using acos() , those are really only significant if you're using single-precision float variables. Re:使用acos()的潜在不准确性,如果您使用的是单精度float变量,那么这些非常重要。 Using a double with 16 significant digits should get you distances accurate to within one metre or less. 使用带有16位有效数字的double精度表可以使距离精确到一米或更小。

That's the haversine algorithm, will provide you with a decent level of accuracy. 那是thersineine算法,将为您提供相当高的准确度。

If it really is "millions" of points, perhaps implement a cache of calculations that you've made... if you come across a pair of coordinates, both of which are sufficiently close to a pair whose distance you've already calculated, then use the cached value? 如果真的是“数百万”点,也许可以实现你所做的计算缓存...如果你遇到一对坐标,这两个坐标都足够接近你已经计算过的距离对,然后使用缓存值?

Or try to cache some of the intermediate steps, eg degree to radians conversions. 或者尝试缓存一些中间步骤,例如度数到弧度的转换。

If you sacrifice accuracy there are some improvements you can make. 如果您牺牲准确性,您可以做出一些改进。 As far as I remember, sin(x) is approximately equal to x for small x . 至于我记得sin(x)大约等于x的小x Also it looks like you are computing the same things several times, like: Math.sin(dLat/2) (which can actually be approximated to dLat/2 as stated above). 此外,您似乎计算了几次相同的事情,例如: Math.sin(dLat/2) (实际上可以近似为dLat/2 ,如上所述)。

However if you are doing millions of these operations, I would somewhere else. 但是,如果您正在进行数百万次这样的操作,我会在其他地方。

  • Is your algorithm optimal? 你的算法是最优的吗? Maybe you are doing too many simple computations? 也许你做了太多简单的计算?

  • If points come from database, can you perform the computations as stored procedures on the database server side? 如果点来自数据库,您可以在数据库服务器端执行计算作为存储过程吗?

  • If you are looking for closest points, can you index them somehow? 如果你正在寻找最近的点,你能以某种方式索引它们吗?

  • Can geospatial indexes help you? 地理空间索引可以帮助您吗?

You might try the law of cosines for spherical trigonometry: 您可以尝试球面三角函数的余弦定律:

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

But it will be inaccurate for short distances (and probably just marginally faster). 但是对于短距离来说这是不准确的(并且可能只是稍微快一些)。

There is a complete discussion here . 有一个完整的讨论在这里 However, the haversine formula is the most correct way, so aside from what others have suggested there may not be much you can do. 然而,胡言乱语的公式是最正确的方式,所以除了别人的建议之外,你可能没有多少办法。 @Alnitak's answer may work, but spherical to Cartesian conversions are not necessarily fast. @ Alnitak的答案可能有效,但笛卡尔转换的球形并不一定快。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM