2017-05-09 94 views
3

这是一个意外的行为,出现在用@Nargiza解决这个问题的道路上:3d distance calculations with GeoDjangoDjango的距离函数不返回距离对象

继上Distance功能 Django文档:

Accepts two geographic fields or expressions and returns the distance between them, as a Distance object.

而从Distance对象,我们可以得到在每个supported units每一个的距离和。

但是:

设模型为:

class MyModel(models.Model): 
    ... 
    coordinates = models.PointField() 

然后执行以下操作:

p1 = MyModel.objects.get(id=1).coordinates 
p2 = MyModel.objects.get(id=2).coordinates 
d = Distance(p1, p2) # This is the function call, 
        # so d should be a Distance object 
print d.m 

应以米为单位打印p1p2之间的距离。

取而代之的是,我们得到了以下错误:

AttributeError: 'Distance' object has no attribute 'm' 

我们找到了一个解决方法,最终(d = Distance(m=p1.distance(p2))),但问题依然存在:

为什么Distance功能未返回Distance对象
这是一个错误,或者我们错过了什么?

提前感谢您的时间。

回答

2

这两者并不是彼此密切相关的。 Doc确实说它会“返回”距离对象,但还有一个额外的步骤:

django.contrib.gis。 db.models .functions.Distance是一个数据库函数,它需要两个表达式(可能包括数据库字段名称)并返回一个可用作查询一部分的对象Func

所以简单地说,它需要在数据库中执行。它将使用数据库函数(例如postgis ST_Distance)计算距离,然后将其作为django.contrib.gis.measure.Distance对象。

所以,除非一个人想乱用SQL编译器和数据库连接,两点之间获得的距离最简单的方法是Distance(m=p1.distance(p2))

编辑:一些代码来说明这一点:

您可以检查(django/contrib/gis/measure.py)中的距离(测量)等级的代码。它相当小并且易于理解。所有它做的是给你一个方便的方式转换,比较和算术运算,传输距离:

In [1]: from django.contrib.gis.measure import Distance 

In [2]: d1 = Distance(mi=10) 

In [3]: d2 = Distance(km=15) 

In [4]: d1 > d2 
Out[4]: True 

In [5]: d1 + d2 
Out[5]: Distance(mi=19.32056788356001) 

In [6]: _.km 
Out[6]: 31.09344 

现在,让我们来看看远处功能:

添加__str__方法模型,所以我们可以看到距离值,当它返回由查询集API和短db_table名,所以我们可以期待在查询:

class MyModel(models.Model): 
    coordinates = models.PointField() 

    class Meta: 
     db_table = 'mymodel' 

    def __str__(self): 
     return f"{self.coordinates} {getattr(self, 'distance', '')}" 

创建一些对象,并做了简单的select * from查询:

In [7]: from gisexperiments.models import MyModel 

In [8]: from django.contrib.gis.geos import Point 

In [10]: some_places = MyModel.objects.bulk_create(
    ...:  MyModel(coordinates=Point(i, i, srid=4326)) for i in range(1, 5) 
    ...:) 

In [11]: MyModel.objects.all() 
Out[11]: <QuerySet [<MyModel: SRID=4326;POINT (1 1) >, <MyModel: SRID=4326;POINT (2 2) >, <MyModel: SRID=4326;POINT (3 3) >, <MyModel: SRID=4326;POINT (4 4) >]> 

In [12]: str(MyModel.objects.all().query) 
Out[12]: 'SELECT "mymodel"."id", "mymodel"."coordinates" FROM "mymodel"' 

无聊。让我们用距离函数距离值添加到结果:

In [14]: from django.contrib.gis.db.models.functions import Distance 

In [15]: from django.contrib.gis.measure import D # an alias 

In [16]: q = MyModel.objects.annotate(dist=Distance('coordinates', origin)) 

In [17]: list(q) 
Out[17]: 
[<MyModel: SRID=4326;POINT (1 1) 157249.597768505 m>, 
<MyModel: SRID=4326;POINT (2 2) 314475.238061007 m>, 
<MyModel: SRID=4326;POINT (3 3) 471652.937856715 m>, 
<MyModel: SRID=4326;POINT (4 4) 628758.663018087 m>] 

In [18]: str(q.query) 
Out[18]: 'SELECT "mymodel"."id", "mymodel"."coordinates", ST_distance_sphere("mymodel"."coordinates", ST_GeomFromEWKB(\'\\001\\001\\000\\000 \\346\\020\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\\000\'::bytea)) AS "distance" FROM "mymodel"' 

你可以看到它使用ST_distance_sphere SQL函数不从"mymodel"."coordinates"我们origin点的字节表示计算使用的距离值。

现在,我们可以用它来过滤和排序和许多其他的东西,里面的所有数据库管理系统(快速):.m你需要传递浮点数到过滤器

In [19]: q = q.filter(distance__lt=D(km=400).m) 

In [20]: list(q) 
Out[20]: 
[<MyModel: SRID=4326;POINT (1 1) 157249.597768505 m>, 
<MyModel: SRID=4326;POINT (2 2) 314475.238061007 m>] 

注意,它赢得无法识别距离对象。

+0

让我知道如果我需要添加一些代码的答案。我想,但努力得到最小的东西。我会再试一次 – Igonato

+0

谢谢你的回复@Igonato,如果你能提供一个很好的代码示例!如果有其他答案,我会保持赏金:) –

+0

@JohnMoutafis增加了一些代码。我对GeoDjango相当陌生,也许有更好的方式来解释它,但这是我最好的。此外,修复了一个错误。函数距离返回Func,不是查找对象 – Igonato