やりたかったことは、指定した緯度経度(GPSから取得した現在位置)に近い場所の検索と距離の算出でした。
DjangoだとPostGIS+GeoDjangoを使えばできますが、今回はデータベースがMySQLであるため、filterでdistanceが使えませんでした。
GeoDjango Database API | Django documentation | Django
いい方法がないかググっていたら、Google Developersサイト内のGoogle Maps APIに関する記事で、PHPとMySQLを使う例があり、参考になりました。
Creating a Store Locator with PHP, MySQL & Google Maps | Google Maps APIs | Google Developers
このページにあるSQLを少しいじってDjangoで使う例を紹介します。
モデルの作成
Google Developersの例に習って店舗のモデル(店舗名, 緯度, 経度)を作ってみます。
myapp/models.py
# coding: utf-8 from django.db import models class Shop(models.Model): name = models.CharField(max_length=255) latitude = models.DecimalField(u'緯度', max_digits=9, decimal_places=6, default=0) longitude = models.DecimalField(u'経度', max_digits=9, decimal_places=6, default=0) class Meta: db_table = 'shop' def __unicode__(self): return self.name
データ投入
店舗ではないけど、適当な場所の位置情報を登録します。
$ python manage.py shell >>> from myapp.models import Shop >>> Shop.objects.create(name=u'株式会社ビープラウド', latitude=35.6802361, longitude=139.70130849999998) <Shop: 株式会社ビープラウド> >>> Shop.objects.create(name=u'JR代々木駅', latitude=35.6830905, longitude=139.70222620000004) <Shop: JR代々木駅> >>> Shop.objects.create(name=u'北参道駅', latitude=35.678156, longitude=139.70567800000003) <Shop: 北参道駅> >>> Shop.objects.create(name=u'JR原宿駅', latitude=35.670168, longitude=139.70268699999997) <Shop: JR原宿駅>
指定した緯度経度から近い場所を検索する
指定した緯度経度から近い場所を取得する関数を作成します。
myapp/api.py
from myapp.models import Shop def get_shop_list(lat, lng): shop_queryset = Shop.objects.raw(""" SELECT *, ( 6371 * acos( cos(radians(%%s)) * cos(radians(latitude)) * cos(radians(longitude) - radians(%%s)) + sin(radians(%%s)) * sin(radians(latitude)) ) ) AS distance FROM %(table)s HAVING distance < 1 ORDER BY distance """ % {'table': Shop._meta.db_table}, [lat, lng, lat]) return shop_queryset
Google Developersのページに書かれているSQLを参考にし、rawメソッドでQuerySetを取得しています。
"HAVING distance < 1" で1km以内の結果だけを返すようにしています。
実行結果
作成した関数を実行してみます。distanceの値はキロメートル単位なので、1000倍してメートル単位で表示しています。
>>> from myapp.api import get_shop_list >>> for shop in get_shop_list(35.6830905, 139.70222620000004): # 代々木駅の緯度経度 ... print shop.name, shop.latitude, shop.longitude, "%.1fm" % (shop.distance * 1000) ... JR代々木駅 35.683090 139.702226 0.0m 株式会社ビープラウド 35.680236 139.701308 328.1m 北参道駅 35.678156 139.705678 631.1m
代々木駅から1km以内の場所を近い順に表示できました。
ここで紹介したコードはbitbucketに置いてます。
tokibito / sample_nullpobug / source / django / geo_mysql_example — Bitbucket