位置情報サービスのはじめ方

第5回 位置情報を保存しよう(前編)

この記事を読むのに必要な時間:およそ 4.5 分

今回から2回に分けて,位置情報をDatastoreに格納する方法をいくつか紹介します※1)⁠

※1
今回利用するデータベースは,MySQLです。

数値型で保存する

緯度経度の情報をデータベースへ格納するときに,もっとも簡単な方法が数値型として保存する方法です。緯度経度がとりうる値の範囲は,以下の通りですので,システムに必要な小数点以下の数字を考慮して型を決めましょう。

緯度-90~90
経度-180~180

はてなフォトライフでは,写真に緯度経度のメタ情報を設定することができますが,高精度な緯度経度情報は必要ないので,型を以下のように指定しています。

latitude     decimal(7,4)
longitude  decimal(7,4)  

decimal(7,4)という指定は,10進数で7桁のデータで,小数点以下は4桁まで格納するというものです。

あるオブジェクトの緯度経度を保存し,表示するだけならこれだけで十分ですが,位置情報を中心に扱うサービスになると,格納したデータを緯度経度を指定して検索する必要が出てきます。

Geometry型で保存する

続いて,平面空間データ型であるGeometry型で保存する方法を紹介します。

Geometry型とは,ちょっと耳慣れませんが,データベースに位置情報を特殊な形式で保存することで,2点間の距離を求めたり,検索の条件に位置を指定して,その位置から距離が近い順のデータを取得したり,地図上の2点が示す矩形エリアの中のデータを取得したりと,数値型に比べて複雑な処理がデータベース側でできます。

例えば,spot_idと,緯度経度を保存するテーブルは以下のように定義できます。

CREATE TABLE spot (
 spot_id INT NOT NULL,
 latlon GEOMETRY NOT NULL,
 PRIMARY KEY (spot_id),
 SPATIAL KEY (latlon)
) Engine=InnoDB;

CREATE文の最後にEngine=MyISAMと指定されているのは,このGeometry型に定義されているSPATIAL型のINDEXがMyISAMでしか利用できないからです※2)⁠

※2
InnoDBでのGeometry型の利用は,MySQL5.0.16から可能になっていますがSPATIAL INDEXはMyISAMでしか利用できません。

SPATIAL INDEXは空間データのIndexで,先に例で示した,ある地点の近くのデータを取得するというSQLを書くことができます。

Geometry型へのデータの挿入

Geometry型のカラムにデータを挿入する場合,以下のようなSQLを発行します。

INSERT INTO spot (spot_id , latlon ) VALUES (1, GeomFromText('POINT(137.10 35.20)'));

GeomFromTextは,MySQLに定義されている関数で,文字列表現からGeometry型のデータを生成します。POINT(経度 緯度)と,経度の方が先に来ていることに注意しましょう。また,経度と緯度の間は,(カンマ)ではなくスペースであることにも注意してください。

Geometry型のデータの取得

上記SQL文を実行してデータを作成したあと,テーブルをselectしてみましょう。

> select * from spot2;
+---------+---------------------------+
| spot_id | latlon                    |
+---------+---------------------------+
|       1 |        33333#a@??????A@ |
+---------+---------------------------+
1 row in set (0.00 sec)

latlon型のところが文字化して表示されました。Geometry型から,必要な情報を取り出すときは,別の関数を使う必要があります。

select spot_id, X(latlon), Y(latlon), ASTEXT(latlon) from spot;
+---------+-----------+-----------+-------------------+
| spot_id | X(latlon) | Y(latlon) | ASTEXT(latlon)    |
+---------+-----------+-----------+-------------------+
|       1 |     137.1 |      35.2 | POINT(137.1 35.2) |
+---------+-----------+-----------+-------------------+
1 row in set (0.00 sec)

X,Y,ASTEXTがそれぞれ関数になっており,Xは経度を,Yは緯度を,ASTEXTは文字列表現を返します。

矩形エリアのスポットを取得する

それでは,(35.00 , 135.00) と (36.00 , 138.00)の2点で表現される矩形エリアの中にあるスポットを取得してみましょう。

> select spot_id, ASTEXT(latlon) from spot where MBRContains(GeomFromText('LINESTRING(138.00 36.00, 135.00 35.00)'), latlon);
+---------+-------------------+
| spot_id | ASTEXT(latlon)    |
+---------+-------------------+
|       1 | POINT(137.1 35.2) |
+---------+-------------------+
1 row in set (0.00 sec)

where句のところに,なにやら複雑な式が出てきましたが,内容をひとつずつ解析していきましょう。

LINESTRING(138.00 36.00, 135.00 35.00)
これは,⁠(35.00 , 135.00) と (36.00 , 138.00)の2点で表現される直線」の文字列表現にになります。先程のPOINT(経度 緯度)同様,矩形のエリアは,LINESTRING(地点Aの経度 地点Aの緯度, 地点Bの経度 地点Bの緯度)という文字列で表現されます。
GeomFromText(A)
LINESTRINGの文字列表現を,実際のGeometry型に変換しています。
MBRContains(A B)
この関数は,Aの最小外接矩形に,Bの最小外接矩形が含まれているかどうかを判定します。先のSQLではAの部分にLINESTRINGが,Bの部分にはgeometry型のlatlonカラムの値が入るので,Aで指定された直線が最小外接する矩形エリア内に入っているスポットで検索していることになります。

図1 

画像

このように,矩形エリアを示す4点ではなく,2点を与えることで,矩形エリア内に存在するデータを検索することができます。

著者プロフィール

栗栖義臣(くりすよしおみ)

株式会社はてな。Java, JavaScript, ActionScript, Perlなどをたしなむ。

優秀な若手エンジニアたちに囲まれながら奮闘する毎日。

URL : http://d.hatena.ne.jp/chris4403/

技術ネタ : http://d.hatena.ne.jp/chris4403+tech/

コメント

  • 図1について

    「矩形エリアのスポットを取得する」のところで、
    LINESTRING(138.00 36.00, 135.00 35.00)

    とされておりますが、
    図1のAおよびA'の経度は、137ではなく138なのではないのでしょうか?

    Commented : #1  uenno (2011/12/13, 14:19)

コメントの記入