2013-03-05 144 views
0

在我建設的網站中,我用城市的外鍵存儲了事件。就像這樣:有必要使用GeoDjango來查詢Django中的距離嗎?

class Event(models.Model): 
    name = models.CharField(max_length=255) 
    ... 
    ciudad = models.ForeignKey(City) 

class City(models.Model): 
    name = models.CharField(max_length=500) 
    ... 
    lat = models.FloatField() 
    lon = models.FloatField() 

我想在一些城市一些公里到查詢事件。 我實際上做的是:

# isInRange takes two cities and a distance in kms and calculates 
# if the distance between the cities (by the fields lat and lon and 
# one nice formula) is minor than the given distance. 
results = [] 
for event in Event.objects.all(): 
    if isInRange(city, event.city, kms): 
     results.append(event) 

我知道,是非常低效的。我知道GeoDjango可以做到這一點,但這是我在整個項目中必須做的唯一「地理事物」。我必須毫無理由地使用那種「複雜」的解決方案,或者有辦法以更高效的方式來做到這一點?

回答

8

如果你不需要在你的範圍內非常確切,你可以使用近似值來計算經度和緯度範圍。概念解釋here

使用城市位置和距離,查找緯度變化(無論在哪裏都保持不變),並且經度的近似變化(根據緯度變化)。然後計算一個邊界框。

import math 

# earth_radius = 3960.0 # for miles 
earth_radius = 6371.0 # for kms 
degrees_to_radians = math.pi/180.0 
radians_to_degrees = 180.0/math.pi 

def change_in_latitude(distance): 
    "Given a distance north, return the change in latitude." 
    return (distance/earth_radius)*radians_to_degrees 

def change_in_longitude(latitude, distance): 
    "Given a latitude and a distance west, return the change in longitude." 
    # Find the radius of a circle around the earth at given latitude. 
    r = earth_radius*math.cos(latitude*degrees_to_radians) 
    return (distance/r)*radians_to_degrees 

def bounding_box(latitude, longitude, distance): 
    lat_change = change_in_latitude(distance) 
    lat_max = latitude + lat_change 
    lat_min = latitude - lat_change 
    lon_change = change_in_longitude(latitude, distance) 
    lon_max = longitude + lon_change 
    lon_min = longitude - lon_change 
    return (lon_max, lon_min, lat_max, lat_min) 

要距離kms內計算事件的city

lon_max, lon_min, lat_max, lat_min = bounding_box(city.lat, city.lon, kms) 
events = Event.objects.filter(
    city__lat__lte=lat_max, 
    city__lat__gte=lat_min, 
    city__lon__lte=lon_max, 
    city__lon__gte=lon_min 
) 

請記住,誤差變大的距離越大,越接近你是兩極。反經絡(國際日期線)附近的地方也存在問題,但很容易檢查(檢查經度是否> 180或< -180)。

如果你想得到更多的acurate結果,你可以使用這個方法作爲第一遍,然後使用你的函數,所以你不必單獨通過每個事件。

+0

很好的答案!我明白錯誤的事情。這是因爲距離應該是一個圓形,這會計算一個僞矩形,不是嗎?無論如何,我會使用這種方法。如果我注意到錯誤太多,我將切換到GeoDjango。再次,很好的回答和謝謝! – sanfilippopablo 2013-03-05 18:02:18