有必要使用GeoDjango来查询Django中的距离吗?

时间:2013-03-05 02:51:37

标签: django distance geodjango

在我正在建设的网站中,我已经存储了带有城市外键的活动。像这样:

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()

我想在一些 kms的某个城市查询事件。 我实际做的是这个:

# 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中这样做是可行的,但这是我在整个项目中必须做的唯一“地理事物”。我必须毫无理由地使用这种“复杂”的解决方案,或者有办法以更有效的方式做到这一点?

2 个答案:

答案 0 :(得分:9)

如果您不需要在您的范围内非常精确,则可以使用近似值来计算纬度和经度范围。概念解释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)。

如果您想要更精确的结果,可以使用此方法作为第一遍,然后使用您的功能,这样您就不必单独完成每个事件。

答案 1 :(得分:0)

使用自定义管理器的更好解决方案,正如我在本文Django sort by distance

中所描述的那样