GeoDjango:半径内のすべてのポイントを見つけますか?


9

私はGeoDjango(Django 1.7およびPostGIS 2.1.4バックエンドを使用)で作業しています。データベースにPointFieldを含むモデルがあり、ポイントから半径10 km以内のすべてのアイテムを検索しようとしています。

これが私のモデルコードです:

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

これが私のビューコードです(読みやすくするために省略されています):

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

これは私に与えます:

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

コンソールにポイントが表示されます:POINT (1.0000000000000000 52.5000000000000000)

クエリの構造を変えるにはどうすればよいですか?

dwithinのドキュメントに従ってpointではなく単に使用しようとすると、別のエラーが発生します。point.locationValueError: Only numeric values of degree units are allowed on geographic DWithin queries

更新:

これは動作するようですが、それが正しいかどうかはわかりません:

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

結果は問題ないように見えますが、度への変換が妥当かどうかはわかりません。


いいえ、そうではありません。この比率は赤道でのみ機能します。
Michal Zimmermann

回答:


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

誰かが興味を持っている場合は、GeoDjangoを使用せずにこれを行うことができます。postgresに必要な拡張機能は、cube、earthdistanceの2つだけです。

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)
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.