在Django中返回附近位置

6 投票
3 回答
5177 浏览
提问于 2025-04-19 09:22

我有一个Django模型,它有一个自定义属性叫做 LocationField

class List(models.Model):
    id = models.AutoField(primary_key=True)
    title = models.CharField(max_length=200)
    location = LocationField(blank=True, max_length=255)

这个属性的值是以 纬度, 经度 的格式存储为字符串的。在我的模板中,我传递一个网址,格式是这样的: /nearby?lat='+某个值+'&long='+某个值

现在,我想根据传递的值返回 List 中的附近条目。

为此,我写了一个 views.py 的函数,代码如下:

def nearby(request):
    if request.GET['lat']:
        lat = request.GET['lat']
        longitude = request.GET['long']
        first_query = Playlist.objects.filter(location__istartswith=lat)
        for f in first_query:
           l = f.index(',')
           n_string = f[l:]

为了让你明白我做了什么, first_query 返回所有以相同 纬度 开头的条目。不过,现在我还想匹配 经度,所以我在运行一个 for 循环,寻找分隔 纬度, 经度 的逗号在我的 LocationField 中的位置。 n_string 会提取 LocationField 的子字符串,然后我打算将其与我的 经度 变量进行匹配。

我的问题有两个部分:

  1. 我该如何生成匹配纬度的查询,并将其返回到模板中?
  2. 我该如何检查,比如说,在该区域周围2平方公里的范围内?

有没有Django的相关包可以用来解决这个问题?

3 个回答

0

是的,有一个叫做 Geodjango 的工具包/项目可以用来做这个。你可以在这里查看官方的说明文档 这里

1

你应该使用GIS数据库来存储坐标数据,并进行相关操作,比如搜索等。

如果要存储位置,可以使用这个链接中的内容:https://docs.djangoproject.com/en/dev/ref/contrib/gis/model-api/#pointfield

作为数据库,你可以选择这个链接中的PostGIS:https://docs.djangoproject.com/en/dev/ref/contrib/gis/install/#postgis,或者这个Spatialite:https://docs.djangoproject.com/en/dev/ref/contrib/gis/install/spatialite/

如果想要搜索附近的地点,可以使用距离查找,具体例子可以参考这个链接:https://docs.djangoproject.com/en/dev/ref/contrib/gis/db-api/#distance-lookups

9

有至少三种方法可以做到这一点:

a) 哈弗辛距离(在MySQL中的例子)

def nearby_spots_old(request, lat, lng, radius=5000, limit=50):
    """
    WITHOUT use of any external library, using raw MySQL and Haversine Formula
    http://en.wikipedia.org/wiki/Haversine_formula
    """
    radius = float(radius) / 1000.0

    query = """SELECT id, (6367*acos(cos(radians(%2f))
               *cos(radians(latitude))*cos(radians(longitude)-radians(%2f))
               +sin(radians(%2f))*sin(radians(latitude))))
               AS distance FROM demo_spot HAVING
               distance < %2f ORDER BY distance LIMIT 0, %d""" % (
        float(lat),
        float(lng),
        float(lat),
        radius,
        limit
    )

    queryset = Spot.objects.raw(query)
    serializer = SpotWithDistanceSerializer(queryset, many=True)

    return JSONResponse(serializer.data)

b) 使用Geodjango(PostgreSQL + PostGIS)

def nearby_spots_new(request, lat, lng, radius=5000, limit=50):
    """
    WITH USE OF GEODJANGO and POSTGIS
    https://docs.djangoproject.com/en/dev/ref/contrib/gis/db-api/#distance-queries
    """
    user_location = fromstr("POINT(%s %s)" % (lng, lat))
    desired_radius = {'m': radius}
    nearby_spots = Spot.objects.filter(
        mpoint__distance_lte=(user_location, D(**desired_radius))).distance(
        user_location).order_by('distance')[:limit]
    serializer = SpotWithDistanceSerializer(nearby_spots, many=True)

    return JSONResponse(serializer.data)

c) 一些聪明的查询(可以想象一个圆圈被一个正方形包围)

可以查看我的回答:如何过滤一个Django模型,使其经纬度坐标落在一定半径内

撰写回答