【问题标题】:It is necessary to use GeoDjango to query distances in Django?在Django中查询距离有必要使用GeoDjango吗?
【发布时间】:2023-12-17 10:32:01
【问题描述】:

在我正在构建的站点中,我存储了带有城市外键的事件。像这样:

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

我想查询 some 城市 some 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 中这样做是可能的,但这是我在整个项目中必须做的唯一“地理事情”。我必须毫无理由地使用那个“复杂”的解决方案,还是有办法以更有效的方式做到这一点?

【问题讨论】:

    标签: django distance geodjango


    【解决方案1】:

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

    如果您想要更准确的结果,您可以使用此方法作为第一遍,然后使用您的函数,这样您就不必逐个处理每个事件。

    【讨论】:

    • 很好的答案!我理解错误的事情。这是因为距离应该是一个圆形,这会计算一个伪矩形,不是吗?无论如何,我会使用这种方法。如果我注意到错误太多,我将切换到 GeoDjango。再次,很好的答案,谢谢!
    【解决方案2】:

    我在这篇文章 Django sort by distance 中描述的使用自定义管理器的更好解决方案

    【讨论】: