SQL查询以计算坐标接近度

时间:2020-03-06 14:20:29  来源:igfitidea点击:

我正在使用此公式来计算我的(My)SQL数据库中具有十进制格式的纬度和经度字段的条目之间的距离:

6371 * ACOS(SIN(RADIANS( %lat1% )) * SIN(RADIANS( %lat2% )) + COS(RADIANS( %lat1% )) * COS(RADIANS( %lat2% )) * COS(RADIANS( %lon2% ) - RADIANS( %lon1% )))

适当替换%lat1%和%lat2%可以在WHERE子句中使用它来查找另一个条目的特定半径内的条目,在ORDER BY子句中与LIMIT一起使用它可以找到最接近的x个条目,等等。

我主要是为了给自己写笔记,但是总是欢迎改进。 :)

注意:如以下Valerion所述,此单位为公里。用适当的替代数字代替6371,以使用米,英里等。

解决方案

我认为这是Haversine公式正确吗?

我在车辆跟踪应用程序上使用完全相同的方法,并且已经做了很多年。它工作得很好。快速检查一下一些旧代码后,我将结果乘以6378137,如果内存可用,该结果将转换为米,但是我已经很长时间没有接触它了。

我相信SQL 2008具有一个新的空间数据类型,我想它可以在不知道此公式的情况下进行这些类型的比较,并且还可以提供可能很有趣的空间索引,但我没有对此进行研究。

对于不支持Trig的数据库(例如SQLite),可以使用pythagorus。即使数据库确实支持trig,毕达哥拉斯也要快得多。注意事项是:

  • 我们需要将坐标存储在x,y网格中,而不是(或者同时)存储在lat,lng中;
  • 该计算假定为"平坦的地球",但这对于相对本地的搜索来说是很好的。

这是我正在研究的Rails项目中的一个示例(重要的一点是中间的SQL):

class User < ActiveRecord::Base
  ...
  # has integer x & y coordinates
  ...

  # Returns array of {:user => <User>, :distance => <distance>}, sorted by distance (in metres).
  # Distance is rounded to nearest integer.
  # point is a Geo::LatLng.
  # radius is in metres.
  # limit specifies the maximum number of records to return (default 100).
  def self.find_within_radius(point, radius, limit = 100)

    sql = <<-SQL
      select id, lat, lng, (#{point.x} - x) * (#{point.x} - x) + (#{point.y} - y) * (#{point.y} - y) d 
      from users where #{(radius ** 2)} >= d 
      order by d limit #{limit}
    SQL

    users = User.find_by_sql(sql)
    users.each {|user| user.d = Math.sqrt(user.d.to_f).round}
    return users
  end