diadia

興味があることをやってみる。自分のメモを残しておきます。

ある座標点からxkm以内に存在するデータオブジェクトをmapに表示したい

PostGISを使う場合はST_DWithin()関数を使えばよいだろう。
ではgeodjangoではどうやって特定の範囲内のデータオブジェクトを取得するか?
psycopg2を使ってデータオブジェクトのみ取得することももやり方としてはできるだろう。
しかしここではgeodjangoの備え付けの機能を使うとしたらどのように書くかを記しておく。

参考:https://docs.djangoproject.com/en/2.2/ref/contrib/gis/tutorial/#automatic-spatial-transformations
https://docs.djangoproject.com/en/2.2/ref/contrib/gis/db-api/#distance-lookups

特定の範囲内に存在するデータオブジェクトはdistance_ltを使う

djangoではデータオブジェクトはfilterを使って取得してきた。icontainsとか使うやり方でdistance_ltやdistance_lteを使えば特定の範囲内のデータオブジェクトは取得できる。
問題はどのように使うかだ。ここで引っかかってしまったのできちんと記録しておく。

ドキュメントの例文では以下のように書いてある。

qs = SouthTexasCity.objects.filter(point__distance_lte=(pnt, D(km=7)))

はじめにこの意味を確認するとpntから7km以内のSouthTexasCityのクエリ結果を示している。Dはfrom django.contrib.gis.measure import Dで別途インポートする必要がある。そして当初pntはデータオブジェクトを用いるのだろうと思っていた。しかしながら使うのはデータオブジェクトではなくgeometryのオブジェクトで、GEOSGeometryやPointクラスのインスタンスでなければならないことが分かった。GEOSGeometryやPointについては現状違いがわからない。少なくともpointのデータを作ることに関しては両者どちらでも作成できると思われる。

GEOSGeometryの使い方

from django.contrib.gis.geos import GEOSGeometry, Point
my_place    = UserPointModel.objects.get(id=1)
point = 'POINT({lon} {lat})'.format(lon=my_place.point.x,lat=my_place.point.y)
pnt = GEOSGeometry(point, srid=4326)

Pointの使い方

from django.contrib.gis.geos import GEOSGeometry, Point
my_place    = UserPointModel.objects.get(id=1)
pnt = Point(my_place.point.x, my_place.point.y, srid=4326)

本題のdistance_lteの使い方

サンプル
class MapsView(View):
    def get(self, request):
        my_place    = UserPointModel.objects.get(id=1)
        pnt = Point(my_place.point.x, my_place.point.y, srid=4326)

        all_objects = UserPointModel.objects.filter(point__distance_lte=(pnt, D(km=2)))
        context = {"all_objects":all_objects}
        return render(request, "maps/list.html",context)