GeoDjango: Tìm tất cả các điểm trong bán kính?


9

Tôi đang làm việc trong GeoDjango (với phụ trợ Django 1.7 và PostGIS 2.1.4). Tôi có một mô hình với PointField trong cơ sở dữ liệu của mình và tôi đang cố gắng tìm tất cả các mục trong bán kính 10km của một điểm.

Đây là mã mô hình của tôi:

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

Đây là mã xem của tôi (được rút ngắn để dễ đọc):

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

Điều này mang lại cho tôi:

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

Tôi thấy điểm trong giao diện điều khiển : POINT (1.0000000000000000 52.5000000000000000).

Làm thế nào tôi nên cấu trúc truy vấn khác nhau?

Nếu tôi cố gắng chỉ sử dụng pointchứ không phải point.location(theo tài liệu dwithin ) thì tôi gặp một lỗi khác : ValueError: Only numeric values of degree units are allowed on geographic DWithin queries.

CẬP NHẬT:

Điều này có vẻ hiệu quả, nhưng tôi không biết liệu nó có đúng không:

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

Kết quả có vẻ ổn, nhưng tôi không biết liệu chuyển đổi sang độ của mình có hợp lý không.


Không, không, tỷ lệ này chỉ hoạt động đối với đường xích đạo.
Michal Zimmermann

Câu trả lời:


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

Bạn có thể làm điều này mà không cần GeoDjango nếu ai đó quan tâm, bạn chỉ cần 2 tiện ích mở rộng trên 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)
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.