GeoDjango& amp;中的最佳查询Postgis距离以米为单位的位置

时间:2015-08-11 11:35:27

标签: django geolocation gis postgis geodjango

我在Postgis数据库中使用GeoDjango的默认SRID WGS84,并且已经发现直接以度为单位的查找比以公里为单位快得多,因为数据库可以跳过我想的预测。

基本上,Place.objects.filter(location__distance__lte=(point, D(km=10)))Place.objects.filter(location__dwithin=(point, 10))慢几个数量级,因为第一个查询会产生对表的完整扫描。但有时我需要查找距离阈值以千米为单位的地方。

是否有一种精确的方法将10公里转换为查询的度数? 也许是另一个具有相同性能的等效查找,而不是我应该使用它?

2 个答案:

答案 0 :(得分:1)

您有几种方法可以解决您的问题,其中有两种方法:

如果你不太关心精确度,你可以使用dwithin并使用天真的仪表来转换degree(x meters) -> x / 40000000 * 360。你会在赤道附近获得近乎精确的结果,但是当你向北或向南移动时你的距离会缩小(我们生活在球体上)。想象一下,一个区域在开始时是一个圆圈,并缩小到接近其中一个极点的无限窄椭圆。

如果您关心精确度,您可以使用:

max_distance = 10000 # distance in meter
buffer_width = max_distance / 40000000. * 360. / math.cos(point.y / 360. * math.pi)
bufferd_point = point.buffer(buffer_width)
Place.objects.filter(
    location__distance__lte=(point, D(m=max_distance)),
    location__overlaps=buffered_point
)

基本思路是查询point度范围内的所有点。这个部分是非常高效的,因为圆圈处于度数并且可以使用地理索引。但是圆圈有时太大了,所以我们将滤镜留在米里以过滤掉可能比允许的max_distance更远的地方。

答案 1 :(得分:1)

对 frankV 回答的一个小更新。

    max_distance = 10000 # distance in meter
buffer_width = max_distance / 40000000. * 360. / math.cos(point.y / 360. * math.pi)
buffered_point = point.buffer(buffer_width)
Place.objects.filter(
    location__distance__lte=(point, D(m=max_distance)),
    location__intersects=buffered_point
)

我发现 __overlaps 不适用于 postgresql 和一点,但 __intersects 可以。

为确保它有助于加快查询速度,请检查查询的解释计划(queryset.query 以获取使用的查询。)