2008-09-18 108 views
7

我使用这个公式来计算,其中有以十进制格式纬度和经度领域我(我的)SQL数据库条目之间的距离: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 appropriate alternative number使用米,英里等

回答

2

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

+0

我发现它是“大圆距离公式”,但看着它,是的,它可能是。 – deceze 2008-09-18 09:24:19

1

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

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

7

对于不支持trig的数据库(如SQLite),可以使用pythagorus。即使你的数据库支持trig,pythagorus也要快得多。需要注意的是:

  • 您需要在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 
+0

有没有办法将longitute和纬度转换为x,y坐标? – 2008-10-20 13:02:33

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