Tom*_*eld 8 django postgis django-models geodjango
我有一个带有单个 PointField 的地理模型,我希望为每个模型与给定点的距离添加注释,稍后我可以对其进行过滤并进行额外的 jiggery pokery。
有一个明显的queryset.distance(to_point)
功能,但这实际上并没有注释查询集,它只是向查询集中的每个模型添加一个距离属性,这意味着我.filter(distance__lte=some_distance)
以后不能应用它。
我也知道按场和距离本身进行过滤,如下所示:
queryset.filter(point__distance_lte=(to_point, D(mi=radius)))
Run Code Online (Sandbox Code Playgroud)
但由于我想做多个过滤器(以获得不同距离范围内的模型计数),我真的不想让 DB 每次都计算与给定点的距离,因为这可能很昂贵。
有任何想法吗?具体来说,有没有办法将其添加为常规注释而不是每个模型的插入属性?
我找不到任何烘焙方法,所以最后我创建了自己的聚合类:
这仅适用于 post_gis,但为另一个地理数据库制作一个不应该太棘手。
from django.db.models import Aggregate, FloatField
from django.db.models.sql.aggregates import Aggregate as SQLAggregate
class Dist(Aggregate):
def add_to_query(self, query, alias, col, source, is_summary):
source = FloatField()
aggregate = SQLDist(
col, source=source, is_summary=is_summary, **self.extra)
query.aggregates[alias] = aggregate
class SQLDist(SQLAggregate):
sql_function = 'ST_Distance_Sphere'
sql_template = "%(function)s(ST_GeomFromText('%(point)s'), %(field)s)"
Run Code Online (Sandbox Code Playgroud)
这可以按如下方式使用:
queryset.annotate(distance=Dist('longlat', point="POINT(1.022 -42.029)"))
Run Code Online (Sandbox Code Playgroud)
任何人都知道这样做的更好方法,请告诉我(或告诉我为什么我的很愚蠢)
这样做对我有用,即我可以在注释上应用过滤器。为了可读性而分解。
from models import Address
from django.contrib.gis.measure import D
from django.contrib.gis.db.models.functions import Distance
intMiles = 200
destPoint = Point(5, 23)
queryset0 = Address.objects.all().order_by('-postcode')
queryset1 = queryset0.annotate(distance=Distance('myPointField' , destPoint ))
queryset2 = queryset1.filter(distance__lte=D(mi=intMiles))
Run Code Online (Sandbox Code Playgroud)
希望它对某人有帮助:)
现代方法之一是设置“output_field”参数以避免«不正确的几何输入类型:»。我们的 output_field django 试图将 ST_Distance_Sphere 浮点结果转换为 GEOField 并且不能。
queryset = self.objects.annotate(
distance=Func(
Func(
F('addresses__location'),
Func(
Value('POINT(1.022 -42.029)'),
function='ST_GeomFromText'
),
function='ST_Distance_Sphere',
output_field=models.FloatField()
),
function='round'
)
)
Run Code Online (Sandbox Code Playgroud)