【问题标题】:Django's Distance function not returning a Distance objectDjango的距离函数不返回距离对象
【发布时间】:2017-05-09 07:59:14
【问题描述】:

这是在使用@Nargiza:3d distance calculations with GeoDjango 解决此问题的过程中出现的意外行为。

按照Distance 函数上的 Django 文档:

接受两个地理字段或表达式并返回它们之间的距离,作为一个距离对象

而从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 对象
这是一个错误,还是我们遗漏了什么?

提前感谢您的宝贵时间。

【问题讨论】:

    标签: python django geodjango


    【解决方案1】:

    这两者之间的关系并不密切。文档确实说它“返回”了距离对象,但还有一个额外的步骤:

    django.contrib.gis.db.models.functions.Distance 是一个 database 函数,它需要两个表达式(可能包括数据库字段名称)并返回一个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 点的字节表示来计算距离。

    我们现在可以将它用于过滤和排序以及许多其他事情,所有这些都在数据库管理系统中(快速):

    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>]
    

    注意.m你需要将浮点数传递给过滤器,它将无法识别距离对象。

    【讨论】:

    • 如果我需要在答案中添加一些代码,请告诉我。我想但很难得到最小的东西。我稍后再试
    • 感谢@Igonato 的回复,如果您能提供一个代码示例,那就太好了!如果有其他回应,我会继续提供赏金:)
    • @JohnMoutafis 添加了一些代码。我对 GeoDjango 很陌生,也许有更好的方法来解释它,但这是我最好的。另外,修复了一个错误。 Function Distance 返回 Func,而不是 Lookup 对象
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多