7

我正在使用这个公式来计算我的 (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以使用米、英里等。

4

4 回答 4

7

对于不支持三角函数的数据库(例如 SQLite),您可以使用勾股定理。

这是一种更快的方法,即使您的数据库确实支持三角函数,但需要注意以下几点:

  • 您需要将坐标存储在 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
于 2008-09-23T19:53:25.513 回答
2

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

于 2008-09-18T09:18:18.947 回答
1

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

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

于 2008-09-18T10:27:13.773 回答
1

我一直在使用它,但忘记了我在哪里得到它。

SELECT n, SQRT(POW((69.1 * (n.field_geofield_lat - :lat)) , 2 ) + POW((53 * (n.field_geofield_lon - :lon)), 2)) AS distance FROM field_revision_field_geofield n ORDER BY distance ASC
于 2012-07-23T23:06:28.147 回答