地理位置SQL查询找不到确切的位置

时间:2010-04-23 00:02:41

标签: sql mysql geolocation

我一直在测试我的地理位置查询已有一段时间了,直到现在我还没有发现任何问题。

我正在尝试搜索给定半径范围内的所有城市,通常我正在使用该城市的坐标搜索城市周围的城市,但最近我尝试在城市周围搜索并发现城市本身没有返回。

我将这些城市作为我的数据库的摘录:

city            latitude    longitude  
Saint-Mathieu   45.316708   -73.516253  
Saint-Édouard   45.233374   -73.516254  
Saint-Michel    45.233374   -73.566256  
Saint-Rémi      45.266708   -73.616257  

但当我在Saint-Rémi市周围进行查询时,会出现以下问题......

SELECT tblcity.city, tblcity.latitude, tblcity.longitude, 
truncate((degrees(acos( sin(radians(tblcity.latitude)) 
* sin(radians(45.266708)) 
+ cos(radians(tblcity.latitude)) 
* cos(radians(45.266708)) 
* cos(radians(tblcity.longitude - -73.616257) ) ) ) 
* 69.09*1.6),1) as distance 
FROM tblcity HAVING distance < 10 ORDER BY distance desc 

我得到了这些结果:

city            latitude    longitude     distance  
Saint-Mathieu   45.316708   -73.516253    9.5  
Saint-Édouard   45.233374   -73.516254    8.6  
Saint-Michel    45.233374   -73.566256    5.3  

搜索中遗失了圣雷米镇。

所以我尝试了一个修改过的查询,希望能得到更好的结果:

SELECT tblcity.city, tblcity.latitude, tblcity.longitude, 
truncate(( 6371 * acos( cos( radians( 45.266708 ) ) 
* cos( radians( tblcity.latitude ) ) 
* cos( radians( tblcity.longitude ) 
- radians( -73.616257 ) ) 
+ sin( radians( 45.266708 ) ) 
* sin( radians( tblcity.latitude ) ) ) ),1) AS distance 
FROM tblcity HAVING distance < 10 ORDER BY distance desc 

但我得到的结果相同......

然而,如果我通过改变纬度的最后一位或长1来修改Saint-Rémi的坐标,两个查询都将返回圣雷米。此外,如果我将查询置于上述任何其他城市的中心,则会在结果中返回搜索到的城市。

任何人都可以了解可能导致我上述查询的内容,而不是显示搜索到的圣雷米市吗?我在下面添加了一个表格示例(删除了额外的字段)。

我正在使用MySQL 5.0.45,先谢谢。

CREATE TABLE `tblcity` ( 
`IDCity` int(1) NOT NULL auto_increment, 
`City` varchar(155) NOT NULL default '', 
`Latitude` decimal(9,6) NOT NULL default '0.000000', 
`Longitude` decimal(9,6) NOT NULL default '0.000000', 
PRIMARY KEY (`IDCity`) 
) ENGINE=MyISAM AUTO_INCREMENT=52743 DEFAULT CHARSET=latin1 AUTO_INCREMENT=52743; 

INSERT INTO `tblcity` (`city`, `latitude`, `longitude`) VALUES 
('Saint-Mathieu', 45.316708, -73.516253), 
('Saint-Édouard', 45.233374, -73.516254), 
('Saint-Michel', 45.233374, -73.566256), 
('Saint-Rémi', 45.266708, -73.616257); 

2 个答案:

答案 0 :(得分:3)

在你的第一个问题中,我相信你已经减去了减法中的经度。余弦的球面定律是:

d = acos(sin(lat1)*sin(lat2) + cos(lat1)*cos(lat2)*cos(long2−long1))*R

如果lat1替换为tblcity.latitude,则long1必须替换为tblcity.longitude。我想你在查询中不小心替换了long2。这个更好吗?

SELECT tblcity.city, tblcity.latitude, tblcity.longitude, 
truncate((degrees(acos( sin(radians(tblcity.latitude)) 
* sin(radians(45.266708)) 
+ cos(radians(tblcity.latitude)) 
* cos(radians(45.266708)) 
* cos(radians(-73.616257 - tblcity.longitude) ) ) ) 
* 69.09*1.6),1) as distance 
FROM tblcity HAVING distance < 10 ORDER BY distance desc 

我还没有查看过你的第二个查询,但希望这会有所帮助。

答案 1 :(得分:2)

你正在使用“球面余弦定律”公式,这对于小距离(如零!)的圆角误差是可以接受的 - 见this discussion。加入acos()的长表达式估计略高于1.0,超出界限。

以下是使用Python进行计算所示的问题:

>>> from math import sin, cos, acos, radians
>>> lat = radians(45.266708)
>>> long_expression = sin(lat) * sin(lat) + cos(lat) * cos(lat) * cos(0.0)
>>> repr(long_expression)
'1.0000000000000002'
>>> acos(long_expression)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: math domain error
>>>

似乎MySQL正在替换NULL而不是引发异常。我对MySQL知之甚少,但你应该能够通过做ifnull(acos(long_expression), 0.0)coalesce(acos(long_expression), 0.0)之类的事情来克服这个问题。

或者你也可以使用半正式公式,将圆形问题从你的门阶转移到地球的另一侧。

更新:我已经在Python中使用该公式进行了测试,以计算每个37582唯一(lat,lon)2中每个点与同一点之间应该为零的距离 - file of US zip codes中的元组。

其中:

  • 31591(84.1%)产生零距离
  • 4244(11.3%)产生9.5厘米的距离。
  • 831(2.2%)产生了13.4厘米的距离。
  • 916(2.4%)产生的“cos”值为1.0000000000000002,如果没有检测到并避免,会导致acos()异常。

似乎明确测试lat1 = lat2 and lon1 = lon2并避免在这种情况下使用公式(只使用零)可能是一个好主意 - 它会给出一致的答案并避免困惑。