Python 使用GeopositionField查找最近的数据库条目

Python 使用GeopositionField查找最近的数据库条目,python,django,geolocation,geocoding,django-geoposition,Python,Django,Geolocation,Geocoding,Django Geoposition,我在Django中使用来存储用户的坐标。现在我想找到一个与当前用户最接近的20个用户的列表。在我的地理位置字段中可以实现该功能吗?我知道GeoDjango使搜索距离变得很容易,但由于我使用的是Heroku和postgresql,我想降低成本,而使用postgressql,安装PostGIS似乎是唯一的选择 有什么建议吗?快速浏览一下源代码,就会发现地理位置字段只是将坐标存储为纯文本(,),因此没有有效的方法从数据库中提取正确的数据。如果您想要高效的数据库查询,您必须使用GeoDjango或Pos

我在Django中使用来存储用户的坐标。现在我想找到一个与当前用户最接近的20个用户的列表。在我的地理位置字段中可以实现该功能吗?我知道GeoDjango使搜索距离变得很容易,但由于我使用的是Heroku和postgresql,我想降低成本,而使用postgressql,安装PostGIS似乎是唯一的选择


有什么建议吗?

快速浏览一下源代码,就会发现
地理位置字段
只是将坐标存储为纯文本(
),因此没有有效的方法从数据库中提取正确的数据。如果您想要高效的数据库查询,您必须使用GeoDjango或PostGIS(或者找到另一种提供空间数据搜索的替代方法)

对于两点之间的距离,可以使用Geopy

从: 下面是distance.distance的示例用法:

>>> from geopy import distance  
>>> _, ne = g.geocode('Newport, RI')  
>>> _, cl = g.geocode('Cleveland, OH')  
>>> distance.distance(ne, cl).miles  
538.37173614757057 
在Django项目中实现这一点。在models.py中创建法线模型:

class User(models.Model):
    name = models.Charfield()
    lat = models.FloatField()
    lng = models.FloatField()
要优化一点,您可以先过滤用户对象以粗略估计附近的用户。这样,您就不必遍历数据库中的所有用户。此粗略估计是可选的。为了满足所有项目要求,您可能需要编写一些额外的逻辑:

#The location of your user.
lat, lng = 41.512107999999998, -81.607044999999999 

min_lat = lat - 1 # You have to calculate this offsets based on the user location.
max_lat = lat + 1 # Because the distance of one degree varies over the planet.
min_lng = lng - 1
max_lng = lng + 1    

users = User.objects.filter(lat__gt=min_lat, lat__lt=max__lat, lat__gt=min_lat, lat__lt=max__lat)

# If not 20 fall back to all users.
if users.count() <= 20:
     users = User.objects.all()

我认为你有两个选择:

  • 如果没有空间索引(Postgis和Geodjango使用PointField)和GeopositionField,就没有有效的方法。我发现解决这个问题的唯一方法是:

    • 您必须找到从源用户到所有用户的所有距离(这真的很重)
    • 然后对所有距离进行排序,并将您要查找的20个距离排在前面
    GeoppositionField将坐标存储为文本,但可以使用字段上的
    .latitude
    longitude
    进行检索

  • Postgresql 9.1+()中似乎支持K近邻问题。但是,我认为您必须向表中添加另一列来存储点(),或者为GeopositionField实现一个距离函数

  • 如果您使用Heroku的基本设置只是为了开发和计划更改为更高的计划,我建议使用第一种方法,因为其他Heroku计划支持Postgis,您可以轻松实现此方法,然后将其更改为简单的Postgis函数调用


    尽管如此,如果这是处理空间数据的唯一情况,我建议使用点域和KNN支持。因此,您将来不需要postgis支持。

    但要求是在不使用GeoDjango的情况下找到20个最近的用户。Yes:results=sorted(results,key=lambda k:k['distance'])按距离为您提供所有用户。[:20]给出前20个。也许你每次都需要很多用户来循环抛出。解决方案是首先粗略估计附近的用户。我将更新答案,并将粗略估计代码放在最接近的20之前。这相当聪明!但随着规模的扩大,这不是会变得更慢吗?与Postgis相比,它的效率不如空间/地理数据库。我想你应该找出哪些查询和计算花费了你(内存/速度/开发时间/金钱),并以此为基础做出决定。一个好的“粗略”估计会加快速度。也许。count()和偶极搜索?我也有类似的要求,最后也使用了geopy。我发现这个要点很有用:。它包括一个粗略的距离计算,以使查询更有效。是的,我有一个保存纬度和经度的列。我有一个从导出的边界框函数,我很容易得到一个框,它告诉我最大和最小纬度和经度的范围,但问题是如何有效地获得最近的20个用户。也许K-近邻能帮我解决这个问题。我会多读一读。但是这种方法比Postgis慢很多,不是吗?如果你使用Postgres 9.1+中实现的K-最近邻解决方案,它将与Postgis一样高效,因为它使用了空间索引(基于voronoi图)。
    results = []
    for user in users:
         d = distance.distance((lat, lng), (user.lat, user.lng))
         results.append( {'distance':d, 'user':user })
            results = sorted(results, key=lambda k: k['distance'])
    results = results[:20]