在我正在建设的网站,我已经存储了一个外键到城市的事件。这样的:
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中可以做到这一点,但这是我在整个项目中唯一需要做的"地理事情"。我必须使用那个"复杂"的解决方案,没有借口,或者有一种更有效的方法来做到这一点?
如果您不需要非常精确的范围,您可以使用近似值来计算纬度和经度范围。这里解释的概念:
使用城市位置和距离,找到纬度的变化(无论在哪里都保持不变)和经度的近似变化(根据纬度变化)。然后计算一个边界框。
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)
计算city
距离kms
内的事件:
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年).
如果你想要更准确的结果,你可以使用这个方法作为第一次传递,然后再使用你的函数,这样你就不必逐个遍历每个事件。
一个更好的解决方案是使用我在这篇文章中描述的自定义管理器Django按距离排序