galapagosit’s blog

本家 > http://galapagosit.com/

mysql(MYISAM)でGeoDjango

djangoで位置情報を使うためにはGeoDjangoという仕組みを使うと良いらしい。

GeoDjango | Django documentation | Django

この機能をフルで使う為にはデータベースをPostgreSQLにしなければならないのだが、簡単な機能であればmysqlでも大丈夫。

やり方

settings.py

INSTALLED_APPSに'django.contrib.gis'を追加する

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'django.contrib.gis',
)

DATABASES設定のENGINEを'django.contrib.gis.db.backends.mysql'に変更。
※'django.db.backends.mysql'では無いので注意

OPTIONSの部分でストレージエンジンをMYISAMに変更。
mysql5.6の時点ではMYISAMでないとGEOMETRY型(BLOB型)の列にスペシャルインデックスを貼る事ができない。
innodbでは貼れない

DATABASES = {
    'default': {
        'ENGINE': 'django.contrib.gis.db.backends.mysql',
        'NAME': 'dbname',
        'USER': 'root',
        'PASSWORD': '',
        'HOST': '127.0.0.1',
        'PORT': '',
        'OPTIONS': {
           "init_command": "SET storage_engine=MYISAM",
        }
    }
}

models.py

モデルの作成例。
ポイントは継承先とマネージャをデフォルトのものから変更する事。

from django.contrib.gis.db import models

# Create your models here.


class GeoDataLog(models.Model):
    latlng = models.GeometryField(spatial_index=True)

    created_at = models.DateTimeField(auto_now_add=True)
    updated_at = models.DateTimeField(auto_now=True)

    objects = models.GeoManager()

使い方

insert

point = Point(float(lat), float(lng))
GeoDataLog.objects.create(latlng=point)

select

普通にマネージャインスタンスからクエリセットを作って、とか出来なくはないが、 位置情報を使った複雑なクエリを発行したいなら、生のsqlで取るのが良さそう。

下記は指定の位置から緯度経度+-{latlng_range}内の短形領域内で指定の位置から近いデータを抽出する為のサンプル
SQLインジェクションには十分注意して下さい!

        sql = """
SELECT
    id AS id,
    GLength(
        GeomFromText(
            CONCAT(
                'LineString({lat} {lng},',
                X(latlng),
                ' ',
                Y(latlng),
                ')'
            )
        )
    ) AS length_km,
    X(latlng) AS lat,
    Y(latlng) AS lng
FROM geodata_geodatalog
WHERE
    MBRContains(
        GeomFromText(
            Concat(
                'LineString(',
                {lat} + {latlng_range},
                ' ',
                {lng} + {latlng_range},
                ',',
                {lat} - {latlng_range},
                ' ',
                {lng} - {latlng_range},
                ')'
            )
        ),
        latlng
    )
HAVING
    length_km < {length_limit_km}
ORDER BY length_km
""".format(
            lat=lat,
            lng=lng,
            latlng_range=latlng_range,
            length_limit_km=length_limit_km
        )

        cursor = connection.cursor()
        cursor.execute(sql)
        cursor.fetchall()