【问题标题】:Geolocation SQL query not finding exact location地理位置 SQL 查询未找到确切位置
【发布时间】:2011-02-11 07:52:49
【问题描述】:

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

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

我的数据库中有这些城市的摘录:

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  

搜索中缺少 Saint-Rémi 镇。

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

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 

但是我得到了同样的结果......

但是,如果我通过将 lat 或 long 的最后一位数字更改 1 来稍微修改 Saint-Rémi 的坐标,则两个查询都将返回 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); 

【问题讨论】:

    标签: sql mysql geolocation


    【解决方案1】:

    您使用的是“余弦球面定律”公式,该公式在小距离(如零!)处容易出现舍入误差——请参阅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%) 产生了 1.0000000000000002 的“cos”值,如果未检测和避免,将导致 acos() 中的异常。

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

    【讨论】:

      【解决方案2】:

      在您的第一个查询中,我相信您已经在减法中反转了经度。余弦球面定律是:

      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 
      

      我尚未查看您的第二个查询,但希望对您有所帮助。

      【讨论】:

      • 感谢 Mike,我尝试了您建议的查询,得到了相同的结果,但仍然省略了 Saint-Rémi 镇。我不会声称我知道查询背后的数学,我必须谷歌它才能创建它,但你上面的等式确实帮助我更好地理解查询背后的数学。谢谢!
      • 我之前比较懒,没有实际测试过。不过,我很惊讶它失败了,所以我现在尝试了。诚然,您通过提供 SQL 输入来创建表变得很容易。无论如何,它奏效了!不知道现在该说什么... ;o)
      • 嗯。即使经度颠倒了,您的原始查询也对我有用,这是有道理的,因为减法将归零。现在我不知道为什么它不适合你。您能否将“HAVING distance
      • 谢谢迈克,非常感谢您的帮助。您将 HAVING 更改为“city = 'Saint-Rémi'”的建议是一个好主意,并产生了一个有趣的结果,为 Saint-Rémi 市返回的距离为 NULL。我使用我的查询和您更正的查询都收到了 NULL 距离。我什至用在线科学计算器做了数学计算,得出的答案是 0。我真的被这个问题难住了,这可能是我的版本或安装 mysql 的问题吗?
      • 我不确定,但这是合乎逻辑的结论,因为它适用于我的(版本 5.1.37)。我建议将距离方程更改为单个项,然后迭代地添加项并运行查询。在某些时候,距离会变为空,您就会知道是什么导致了问题。
      猜你喜欢
      • 2014-01-18
      • 1970-01-01
      • 1970-01-01
      • 2017-01-23
      • 2018-05-31
      • 1970-01-01
      • 1970-01-01
      • 2014-07-02
      • 1970-01-01
      相关资源
      最近更新 更多