如何在GeoDjango中添加距离点作为注释的距离

时间:2014-06-12 10:24:00

标签: django django-models postgis geodjango

我有一个带有单个PointField的地理模型,我想为每个模型与给定点的距离添加一个注释,我可以稍后过滤并执行额外的jiggery pokery。

有明显的queryset.distance(to_point)函数,但这实际上并没有注释查询集,它只是为查询集中的每个模型添加了一个距离属性,这意味着我无法将.filter(distance__lte=some_distance)应用于它以后。

我也知道按字段过滤和距离本身就是这样:

queryset.filter(point__distance_lte=(to_point, D(mi=radius)))

但是由于我想要做多个过滤器(以获得不同距离范围内的模型计数),我真的不想让DB每次计算距离给定点的距离,因为这可能很昂贵。

有什么想法吗?具体来说,有没有办法将其添加为常规注释而不是每个模型的插入属性?

5 个答案:

答案 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')

Supported distance lookups

  • distance_lt
  • distance_lte
  • distance_gt
  • distance_gte
  • dwithin

答案 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