Math 限制经纬度

Math 限制经纬度,math,geolocation,Math,Geolocation,我有一个从用户指定的点查找附近位置(300米)的服务 我用哈弗森公式来检查一个位置是否靠近这个点 我的问题是它的速度很慢,因为它正在检查数据库中的所有点 我想做的是限制初始查询,并将haversine公式应用于较小范围内的点列表 e、 g 是否有一种从给定点获取边界的松散方法? 或者基本上是把纬度/经度转换成米?用一个球体近似地球,两个连续纬度之间的距离可以通过 dPerLat = pi * r / 180°, 其中r是地球的半径。这将是大约111公里 因此,如果您的参考点是(lat,lon

我有一个从用户指定的点查找附近位置(300米)的服务

我用哈弗森公式来检查一个位置是否靠近这个点

我的问题是它的速度很慢,因为它正在检查数据库中的所有点

我想做的是限制初始查询,并将haversine公式应用于较小范围内的点列表 e、 g

是否有一种从给定点获取边界的松散方法?
或者基本上是把纬度/经度转换成米?

用一个球体近似地球,两个连续纬度之间的距离可以通过

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值


注意两极。

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

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值


注意两极。

如果您可以修改数据库结构,有一种非常简单的方法可以做到这一点:与其(或除此之外)存储纬度和经度,不如将位置坐标转换为3D空间,其中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
这将很好地缩减列表,您可以对结果集进行haversine计算


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

经度有点复杂,因为转换因子会根据你在北方或南方的距离而变化,但它仍然不太复杂:你只需乘以纬度的余弦即可

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

棘手的问题出现在坐标系的不连续性附近,例如当你接近极点时。当你开始在纬度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
如果abs(中心纬度)>89.999
从abs(位置.纬度-中心.纬度)<0.003的位置选择*
其他的
如果abs(中心经度)>179.997
从位置选择*
其中abs(位置纬度-中心纬度)<0.003
180-abs(位置经度)<(0.006/cos(中心纬度))
其他的
从位置选择*
其中abs(位置纬度-中心纬度)<0.003
和abs(位置经度-中心经度)<(0.003/cos(中心经度))
恩迪夫
恩迪夫
如果您想要一个简洁的语句,而不得不测试可能两倍多的点,则只能比较经度的绝对值:

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)
从位置选择*
其中abs(位置纬度-中心纬度)<0.003

和abs(abs(location.longitude)-abs(center.longitude))如果可以修改数据库结构,那么