2008-09-18 202 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