GeoDjango: Encontre todos os pontos dentro do raio?


9

Estou trabalhando no GeoDjango (com um back-end do Django 1.7 e PostGIS 2.1.4). Eu tenho um modelo com um PointField no meu banco de dados e estou tentando encontrar todos os itens no raio de 10 km de um ponto.

Aqui está o código do meu modelo:

class Place(models.Model):
    id = models.IntegerField(primary_key=True)
    location = models.PointField(null=True, blank=True)
    objects = models.GeoManager()

Aqui está o meu código de exibição (abreviado para facilitar a leitura):

    from django.contrib.gis.geos import Point
    from django.contrib.gis.measure import Distance
    lat = 52.5
    lng = 1.0
    radius = 10
    point = Point(lng, lat)
    print point
    places = Place.objects.filter(location__dwithin=(point.location, Distance(km=radius)))

Isso me dá:

AttributeError: 'Point' object has no attribute 'location'

Eu vejo o ponto no console: POINT (1.0000000000000000 52.5000000000000000).

Como devo estruturar a consulta de maneira diferente?

Se eu tentar usar apenas pointem vez de point.location(como pela documentação dwithin ), então eu recebo um erro diferente: ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.

ATUALIZAR:

Isso parece funcionar, mas não sei se está correto:

    radius = int(radius)
    degrees = radius / 111.325
    point = Point(float(lng), float(lat))
    places = Place.objects.filter(location__dwithin=(point, degrees))

Os resultados parecem bons, mas não sei se minha conversão para graus é razoável.


Não, não é, essa proporção só funciona para o equador.
Michal Zimmermann

Respostas:


21
from django.contrib.gis.geos import Point
from django.contrib.gis.measure import Distance  


lat = 52.5
lng = 1.0
radius = 10
point = Point(lng, lat)    
Place.objects.filter(location__distance_lt=(point, Distance(km=radius)))

1

Você pode fazer isso sem o GeoDjango se alguém estiver interessado, você só precisa de 2 extensões no postgres: cubo, distância da terra

from django.db.models import Lookup, Field, fields, Func, QuerySet


class LLToEarth(Func):
    function = 'll_to_earth'
    arg_joiner = ', '
    arity = 2  # The number of arguments the function accepts.

    def __init__(self, *expressions, output_field=None, **extra):
        if output_field is None:
            output_field = fields.Field()
        super().__init__(*expressions, output_field=output_field, **extra)


class EarthBox(LLToEarth):
    function = 'earth_box'
    arg_joiner = ', '


@Field.register_lookup
class Near(Lookup):
    lookup_name = 'in_georange'
    operator = '@>'

    def as_sql(self, compiler, connection):
        lhs, lhs_params = self.process_lhs(compiler, connection)
        rhs, rhs_params = self.process_rhs(compiler, connection)
        params = lhs_params + rhs_params
        return '%s @> %s' % (lhs, rhs), params


def filter_in_range(
        queryset: QuerySet,
        latitude: float,
        longitude: float,
        range_in_meters: int,
        latitude_column_name: str='latitude',
        longitude_column_name: str='longitude',
        field_name: str = 'earthbox',
        lookup_exp: str = 'in_georange',
):
    earthbox = {field_name: EarthBox(LLToEarth(latitude, longitude), range_in_meters)}
    lookup = '%s__%s' % (field_name, lookup_exp)
    in_range = {lookup: LLToEarth(latitude_column_name, longitude_column_name)}
    return queryset.annotate(**earthbox).filter(**in_range)
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.