Geodango: Cari semua titik dalam radius?


9

Saya bekerja di GeoDjango (dengan backend Django 1.7 dan PostGIS 2.1.4). Saya memiliki model dengan PointField di database saya dan saya mencoba menemukan semua item dalam radius 10 km dari suatu titik.

Ini kode model saya:

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

Berikut ini adalah kode tampilan saya (disingkat agar mudah dibaca):

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

Ini memberi saya:

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

Saya melihat titik di konsol: POINT (1.0000000000000000 52.5000000000000000).

Bagaimana saya harus menyusun kueri secara berbeda?

Jika saya mencoba hanya menggunakan pointbukan point.location(sesuai dokumentasi dwithin ) maka saya mendapatkan error yang berbeda: ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.

MEMPERBARUI:

Ini tampaknya berhasil, tetapi saya tidak tahu apakah itu benar:

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

Hasilnya terlihat OK, tetapi saya tidak tahu apakah konversi saya ke derajat masuk akal.


Tidak, rasio ini hanya berfungsi untuk ekuator.
Michal Zimmermann

Jawaban:


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

Anda dapat melakukan ini tanpa GeoDjango jika seseorang tertarik, Anda hanya perlu 2 ekstensi di 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)
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.