GeoDjango: Alle Punkte im Radius finden?


9

Ich arbeite in GeoDjango (mit einem Django 1.7 und PostGIS 2.1.4 Backend). Ich habe ein Modell mit einem PointField in meiner Datenbank und versuche, alle Elemente innerhalb eines Radius von 10 km um einen Punkt zu finden.

Hier ist mein Modellcode:

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

Hier ist mein Ansichtscode (zur besseren Lesbarkeit gekürzt):

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

Das gibt mir:

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

Ich sehe den Punkt in der Konsole : POINT (1.0000000000000000 52.5000000000000000).

Wie soll ich die Abfrage anders strukturieren?

Wenn ich versuche, nur pointanstatt point.location(gemäß der Dokumentation ) zu verwenden, erhalte ich einen anderen Fehler : ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.

AKTUALISIEREN:

Das scheint zu funktionieren, aber ich weiß nicht, ob es richtig ist:

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

Die Ergebnisse sehen in Ordnung aus, aber ich weiß nicht, ob meine Umrechnung in Grad angemessen ist.


Nein, ist es nicht, dieses Verhältnis funktioniert nur für Äquator.
Michal Zimmermann

Antworten:


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

Sie können dies ohne GeoDjango tun, wenn jemand interessiert ist. Sie benötigen nur 2 Erweiterungen für Postgres: Cube, Earthdistance

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)
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.