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

Finding objects within x miles of a point

我正在努力获取用户位置 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,所以我认为我的查询没有问题。

是什么导致了这种差异?

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

由于笛卡尔数学比地理空间数学快得多,因此查询可能会将坐标视为在平面上而不是在球体上。

docs这样解释:

Most people are familiar with using latitude and longitude to reference a location on the earth’s surface. However, latitude and longitude are angles, not distances. In other words, while the shortest path between two points on a flat surface is a straight line, the shortest path between two points on a curved surface (such as the earth) is an arc of a great circle. Thus, additional computation is required to obtain distances in planar units (e.g., kilometers and miles). Using a geographic coordinate system may introduce complications for the developer later on. For example, Spatialite does not have the capability to perform distance calculations between geometries using geographic coordinate systems, e.g. constructing a query to find all points within 5 miles of a county boundary stored as WGS84.

Portions of the earth’s surface may projected onto a two-dimensional, or Cartesian, plane. Projected coordinate systems are especially convenient for region-specific applications, e.g., if you know that your database will only cover geometries in North Kansas, then you may consider using projection system specific to that region. Moreover, projected coordinate systems are defined in Cartesian units (such as meters or feet), easing distance calculations.

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

In PostGIS, ST_Distance_Sphere does not limit the geometry types geographic distance queries are performed with. However, these queries may take a long time, as great-circle distances must be calculated on the fly for every row in the query. This is because the spatial index on traditional geometry fields cannot be used.

For much better performance on WGS84 distance queries, consider using geography columns in your database instead because they are able to use their spatial index in distance queries. You can tell GeoDjango to use a geography column by setting geography=True in your field definition.

您可以通过打印原始 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
  • 在该圆内找到点
  • 使用不同的数据库类型

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