【问题标题】:Using GeopositionField to find closest database entries使用 GeopositionField 查找最近的数据库条目
【发布时间】:2023-03-13 11:45:01
【问题描述】:

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

有什么建议吗?

【问题讨论】:

    标签: python django geolocation geocoding django-geoposition


    【解决方案1】:

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

    来自documetation: 下面是 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()
    

    计算你的用户和users中每个用户的距离,按距离排序,得到前20个。

    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]
    

    【讨论】:

    • 但要求是在不使用 GeoDjango 的情况下找到 20 个 CLOSEST 用户。
    • 是的:results = sorted(results, key=lambda k: k['distance']) 为您提供所有用户的距离。 [:20] 给出前 20 个。也许您每次都要循环投掷很多用户。解决方法是先粗略估计附近的用户。我将更新答案并将粗略估计代码放在最接近的 20 之前。
    • 这很聪明!但这会随着它的扩展而变慢吗?与 Postgis 相比?
    • 它不如空间/地理数据库高效。我想您应该找出哪些查询和计算正在花费您(内存/速度/开发时间/金钱),并以此为基础做出决定。一个好的“粗略”估计会加快速度。也许 .count() 和偶极搜索?还有docs.djangoproject.com/en/1.2/topics/db/optimization
    • 我有类似的要求,最终也使用了 geopy。我发现这个要点很有用:gist.github.com/renyi/3385043。它包括粗略的距离计算,以提高查询效率。
    【解决方案2】:

    我认为你有两个选择:

    1. 没有空间索引(Postgis 和 Geodjango 使用 PointField)和 GeopositionField 就没有有效的方法。我发现处理这个问题的唯一方法是:

      • 您必须找到从源用户到所有用户的所有距离(这真的很重)。
      • 然后对所有距离进行排序,并排在您要查找的前 20 位。

      GeopositionField 将坐标存储为文本,但可以在字段上使用.latitudelongitude 检索。

    2. 似乎在 Postgresql 9.1+ (http://wiki.postgresql.org/images/4/46/Knn.pdf) 中支持 K-Nearest-Neighbors 问题。但是,我认为您必须在表中添加另一列来存储点 (http://www.postgresql.org/docs/9.2/static/datatype-geometric.html) 或为 GeopositionField 实现距离函数。

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

    虽然,如果这是您处理空间数据的唯一情况,我建议使用 Point 字段和 KNN 支持。因此,您将来不需要 postgis 支持。

    【讨论】:

    • 是的,我确实有一列可以保存纬度和经度。而且我有一个从janmatuschek.de/LatitudeLongitudeBoundingCoordinates 派生的边界框函数。我很容易得到一个框,它告诉我我的最大和最小纬度和经度的范围,但是有效地获得最近的 20 个用户是问题所在。也许 K-Nearest-Neighbors 会为我解决这个问题。我会更多地阅读它。但是这种方法比 Postgis 慢很多不是吗?
    • 如果您使用在 Postgres 9.1+ 中实现的 K-Nearest-Neighbors 解决方案,它将与 Postgis 一样有效,因为它使用空间索引(基于 voronoi 图)。
    【解决方案3】:

    对源代码的快速浏览表明GeopositionField 只是将坐标存储为纯文本 (&lt;latitude&gt;,&lt;longitude&gt;),因此没有一种有效的方法可以从数据库中提取正确的数据。如果您想要高效的数据库查询,则必须使用 GeoDjango 或 PostGIS(或寻找另一种提供空间数据搜索的替代方案)。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-07-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-06-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多