2016-08-25 168 views
0

我有一项服务可以从用户指定的地点查找附近的位置(300米)。获取经度和纬度的限制

我使用haversine公式检查一个位置点附近 https://en.wikipedia.org/wiki/Haversine_formula

我的问题是,它是缓慢的,因为它的检查,对所有在我的DB之分。

我想要做的是限制初始查询并将半正定公式应用于较小边界区域中的点列表 例如,

results = (SELECT * FROM location WHERE location.latitude BETWEEN 14.223 AND 14.5) 
AND location.longitude BETWEEN 121.5 AND 122 

haversine(results, user_point) 

是否有从给定点获得边界的松散方式? 或基本上经纬度/米长转换为米?

回答

1

如果你可以修改你的数据库结构,有一个超级简单的方法来做到这一点:代替(或除了)存储经度和纬度,将你的位置坐标转换为三维空间,列为x,y,和以米为单位的z。那么你可以做

SELECT * FROM location 
    WHERE location.x BETWEEN center.x - 300 AND center.x + 300 
    AND location.y BETWEEN center.y - 300 AND center.y + 300 
    AND location.z BETWEEN center.z - 300 AND center.z + 300 

这将减少你的清单非常好,你可以对结果集做正弦计算。


如果您坚持使用只有经度和纬度的数据库,仍然可以缩小搜索范围。纬度很容易:只要你忽略了当你接近极点时出现的复杂情况,北向或南向的一个纬度总是相当于111公里的距离。这意味着300米的距离为0.0027 ...纬度,尽管您可能稍微保守一点,使用0.003或0.004。

经度有点棘手,因为转换因子根据你的北方或南方有多远而变化,但它还不算太复杂:你只需乘以纬度的余弦。

distance = cos(latitude) * 111.19... km/degree * delta_angle 

在赤道上,它和纬度一样:赤道经度变化一度为111公里。在北纬80度或南纬80度处,乘以cos(80 degrees) = 0.17...,结果经度1度变化仅为19.3公里。出于您的目的,您可以将其反转并找到经度范围,以选择300 m/cos(latitude)/(111.19... km/degree) = (0.0027... degrees)/cos(latitude)。该系数与第一段的数量相同;这不是巧合。

棘手的问题出现在坐标系的不连续处附近,例如当你靠近两极时。你可以看到为什么当你开始在纬度堵漏像89.9996度:

0.0027... degrees/cos(89.9996 degrees) = 386... degrees 

嘛,怎么可能当只有360度的整圈?这是一个指标,你已经达到了你的300米半径一直延伸到杆的位置,并以一种说话的方式回到你的起始位置。那时候,你可能只需要搜索数据库中所有的点就足够接近极点。当然,你应该在89.999度左右开始这样做,因为那是你正在搜索的地区的600米直径完全包围了杆。

还有另一个问题在(国际日期线附近),或者更确切地说是“反经络”,与经度从-180跳到+180度有关。即使它们在地理上相距仅几米,但在赤道上的点为+179.9999度和在-179.9999度处的点将具有非常不同的坐标。由于您只是将其作为初步过滤器进行更详细的搜索,因此最简单的方法是穿过反子午线的0.006度(大致为300米半径圆的直径)内的每个点,然后再通过半胱氨酸计算将确定这些点是否确实接近。总结一下,你可以使用上面提到的经度和纬度的界限,只是为极点和反子午线添加特殊情况。在某种伪SQL /代码混合的:

IF abs(center.latitude) > 89.999 
    SELECT * FROM location WHERE abs(location.latitude - center.latitude) < 0.003 
ELSE 
    IF abs(center.longitude) > 179.997 
    SELECT * FROM location 
     WHERE abs(location.latitude - center.latitude) < 0.003 
     AND 180 - abs(location.longitude) < (0.006/cos(center.latitude)) 
    ELSE 
    SELECT * FROM location 
     WHERE abs(location.latitude - center.latitude) < 0.003 
     AND abs(location.longitude - center.longitude) < (0.003/cos(center.latitude)) 
    ENDIF 
ENDIF 

如果你想有潜在的测试两倍多点,你只能比较经度的绝对值为代价的精辟语句:

SELECT * FROM location 
    WHERE abs(location.latitude - center.latitude) < 0.003 
    AND abs(abs(location.longitude) - abs(center.longitude)) <= min(0.003/cos(center.latitude), 180) 
0

逼近与球体大地,两个连续的纬度之间的距离可以通过

dPerLat = pi * r/180°, 

其中r是地球的半径来计算。这将是约111公里。

所以,如果您的参考点是(lat, long)和搜索半径为d,那么你要在范围内搜索纬度

lat* \in [lat - d/dPerLat, lat + d/dPerLat] 

然后,对于一个给定的纬度,连续经度的距离是:

dPerLong = pi * r * cos(lat)/180° 

同样,要搜索的经度范围是+- d/dPerLong。您应该使用给出您保守(最大)范围的lat值,即具有最高绝对值的lat值。

小心两极。