在一个点的x英里范围内查找对象

时间:2015-11-22 22:37:02

标签: django geodjango

我正努力让所有活动都在距离用户10英里的地方。我的模型看起来像这样:

class User(models.Model):
    location = models.PointField()
    ...


class Event(models.Model):
    location = models.PointField()
    ...

在我的测试中,当我检查用户与事件之间的距离时,我得到值11.5122663513

from geopy.distance import vincenty

print vincenty(request.user.location, event.location).miles # 11.5122663513

然而,当我查询用户所在位置10英里范围内的所有事件时,会返回该事件:

Event.objects.filter(location__distance_lte=(request.user.location, D(mi=10))).count() # 1

只有当我将半径降低到4英里以下时,过滤器才会生效:

Event.objects.filter(location__distance_lte=(request.user.location, D(mi=3))).count() # 0

我几乎完全遵循docs' example,所以我认为我的查询不是问题。

可能导致这种差异的原因是什么?

1 个答案:

答案 0 :(得分:2)

这在很大程度上取决于您使用的数据库类型。

因为笛卡尔数学比地理空间数学快得多,所以查询可能将坐标看作是在平面上而不是在球体上。

docs以这种方式解释:

  

大多数人都熟悉使用纬度和经度   参考地球表面的位置。但是,纬度和   经度是角度,而不是距离。换句话说,虽然   平面上两点之间的最短路径是直线,   曲面上两点之间的最短路径(如   地球)是一个大圆弧。因此,额外的计算   需要获得平面单位的距离(例如千米和米)   英里)。使用地理坐标系可能会引入   后来开发人员的并发症。例如,Spatialite可以   没有能力执行之间的距离计算   使用地理坐标系的几何形状,例如建造一个   查询以查找存储为县界的5英里范围内的所有点   WGS84。

     

地球表面的某些部分可能投射到二维面上,   或笛卡儿,飞机。特别是投影坐标系   方便区域特定的应用程序,例如,如果你知道   您的数据库只会覆盖北堪萨斯州的几何图形,那么您可以   考虑使用特定于该区域的投影系统。此外,   投影坐标系以笛卡尔单位定义(如   米或英尺),缓和距离计算。

此外,这可能会受到您的数据库选择的影响。如果您使用的是Postgres / PostGIS,则在文档中有以下注释:

  

在PostGIS中,ST_Distance_Sphere不限制几何类型   使用地理距离查询。但是,这些   查询可能需要很长时间,因为必须有大圆距离   为查询中的每一行动态计算。这是因为   传统几何字段的空间索引不能使用。

     

要在WGS84距离查询上获得更好的性能,请考虑使用   数据库中的地理列,因为他们能够   在距离查询中使用它们的空间索引。你可以告诉GeoDjango   通过在字段中设置geography = True来使用地理列   定义

您可以通过打印原始SQL来自行检查:

qs = Event.objects.filter(location__distance_lte=(request.user.location, D(mi=10))
print qs.query

根据您的数据库类型以及您计划存储的数据量,您有几个选项:

  • 在python中第二次过滤积分
  • 尝试设置geography=True
  • 设置明确的SRID
  • 取一个给定半径的buffer it out into a circle点,然后使用contains
  • 在该圈内找到点
  • 使用其他数据库类型

如果您共享原始查询,则更容易弄清楚发生了什么。