我有一项服务,可以从用户指定的点查找附近的位置(300米)。
我使用半正式公式来检查位置是否接近该点 https://en.wikipedia.org/wiki/Haversine_formula
我的问题是它很慢,因为它检查了我的数据库中的所有点。
我想要做的是限制初始查询并将hasrsine公式应用于较小有界区域中的点列表 e.g。
>>B = zip(A[0])
>>for idx,val in enumerate(A):
for idx1,val1 in enumerate(B):
B[idx1] = val1 + (A[idx][idx1],)
是否存在从给定点获取界限的松散方式? 或者基本上是lat / long到米的脏转换?
答案 0 :(得分:1)
如果您可以修改数据库结构,有一种超级简单的方法:代替(或除了)存储纬度和经度,将您的位置坐标转换为3D空间,包含x,y和x的列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 km。在北纬80度(或南纬)处,乘以因子cos(80 degrees) = 0.17...
,结果经度的1度变化仅为19.3 km。出于您的目的,您可以将其反转并找到要选择为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)
答案 1 :(得分: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值。
小心两极。