我有一个带有单个PointField的地理模型,我想为每个模型与给定点的距离添加一个注释,我可以稍后过滤并执行额外的jiggery pokery。
有明显的queryset.distance(to_point)
函数,但这实际上并没有注释查询集,它只是为查询集中的每个模型添加了一个距离属性,这意味着我无法将.filter(distance__lte=some_distance)
应用于它以后。
我也知道按字段过滤和距离本身就是这样:
queryset.filter(point__distance_lte=(to_point, D(mi=radius)))
但是由于我想要做多个过滤器(以获得不同距离范围内的模型计数),我真的不想让DB每次计算距离给定点的距离,因为这可能很昂贵。
有什么想法吗?具体来说,有没有办法将其添加为常规注释而不是每个模型的插入属性?
答案 0 :(得分:4)
我找不到任何这样做的方式,所以最后我创建了自己的聚合类:
这仅适用于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)"
可以使用如下:
queryset.annotate(distance=Dist('longlat', point="POINT(1.022 -42.029)"))
任何人都知道更好的方法,请告诉我(或告诉我为什么我的傻)
答案 1 :(得分:2)
您可以使用GeoQuerySet.distance
cities = City.objects.distance(reference_pnt)
for city in cities:
print city.distance()
Link: GeoDjango distance documentaion
编辑:添加距离属性以及距离过滤查询
usr_pnt = fromstr('POINT(-92.69 19.20)', srid=4326)
City.objects.filter(point__distance_lte=(usr_pnt, D(km=700))).distance(usr_pnt).order_by('distance')
答案 2 :(得分:2)
现代方法之一是设置“output_field”arg以避免«不正确的几何输入类型:»。使用output_field django尝试将ST_Distance_Sphere float结果转换为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'
)
)
答案 3 :(得分:1)
像这样对我有用,即我可以在注释上应用过滤器。 为了可读性而分手。
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))
希望对别人有帮助:)
答案 4 :(得分:0)
一种注释和排序GeoDjango的方法。此模型包含一个包含lat和lng属性的Coordinates记录的外键。
def get_nearby_coords(lat, lng, max_distance=10):
"""
Return objects sorted by distance to specified coordinates
which distance is less than max_distance given in kilometers
"""
# Great circle distance formula
R = 6371
qs = Precinct.objects.all().annotate(
distance=Value(R)*Func(
Func(
F("coordinates__lat")*Value(math.sin(math.pi/180)),
function="sin",
output_field=models.FloatField()
) * Value(
math.sin(lat*math.pi/180)
) + Func(
F("coordinates__lat")* Value(math.pi/180),
function="cos",
output_field=models.FloatField()
) * Value(
math.cos(lat*math.pi/180)
) * Func(
Value(lng*math.pi/180) - F("coordinates__lng") * Value(math.pi/180),
function="cos",
output_field=models.FloatField()
),
function="acos"
)
).order_by("distance")
if max_distance is not None:
qs = qs.filter(distance__lt=max_distance)
return qs