GeoDjango: ¿Encuentra todos los puntos dentro del radio?


9

Estoy trabajando en GeoDjango (con un backend Django 1.7 y PostGIS 2.1.4). Tengo un modelo con un PointField en mi base de datos y estoy tratando de encontrar todos los artículos dentro de un radio de 10 km de un punto.

Aquí está mi código modelo:

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

Aquí está mi código de vista (abreviado para facilitar la lectura):

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

Esto me da:

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

Veo el momento de la consola: POINT (1.0000000000000000 52.5000000000000000).

¿Cómo debo estructurar la consulta de manera diferente?

Si lo intento simplemente usando pointen lugar de point.location(según la documentación dwithin ) entonces consigo un error diferente: ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.

ACTUALIZAR:

Esto parece funcionar, pero no sé si es correcto:

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

Los resultados se ven bien, pero no sé si mi conversión a grados es razonable.


No, no lo es, esta relación solo funciona para el ecuador.
Michal Zimmermann

Respuestas:


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

Puede hacer esto sin GeoDjango si alguien está interesado, solo necesita 2 extensiones en postgres: cubo, tierra

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)
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.