2011年8月11日木曜日

postgresqlを使った位置情報の計算について

仕事のヤル気がないのでブログ更新。
ちょっと休憩でブログ更新。

ちょうど今、仕事で緯度経度から位置情報の計算するプログラミングするのに基礎的な部分が気になったので数学を勉強しました。
元々「なんで?どうして?がおがおぶー!」(もうこの番組なくなっちゃいましたが)って感じで気になることはとことんやりたい人間なので位置情報はこの公式で出るんよ。ってWEBの情報で出てくると理由を調べたくなったのが理由です。
まぁ公式的にはこちらのサイト とかに書いてありますので興味がある人はどうぞ。
ついでに自分は正確性が求められるサービスでは無かったのでpostgresqlの機能とSQLで実現しました。
ソース的にはこちら

SELECT sqrt(power((対象緯度 - 自分の緯度) * 111, 2) + power((対象経度 - 自分の経度) * 91, 2)) AS distance
こんな感じ。
111と91は緯度経度の一度あたりの距離の近似値です。
詳しくはこちらのサイトが参考になります。
でテーブルに位置情報をもたせているならば
SELECT sqrt(power((hoge.lat - 33.333333) * 111, 2) + power((hoge.lon - 133.333333) * 91, 2)) AS distance FROM hoge 
ってするとhogeの中の位置情報を持った対象と自分との距離の一覧が出てくる感じです。
同じように位置情報で円を書いて絞り込みのwhere句を書くと
circle(point(対象経度 * 91.0, 対象緯度 * 111.0), 0) @ circle(point(自分の経度 * 91.0, 自分の緯度 * 111.0), 求めたい円の半径)
となります。
これをwhere句で差し込んだ例が
SELECT
  sqrt(power((hoge.lat - 33.333333) * 111, 2) + power((hoge.lon - 133.333333) * 91, 2)) AS distance
FROM hoge 
WHERE 
  circle(point(hoge.lon * 91.0, hoge.lat * 111.0), 0) @ circle(point(133.333333 * 91.0, 33.333333 * 111.0)
となります。
自分は緯度経度のカラムはNumber型にしてINDEXを貼っております。
これではベンチマークの結果、誤差がそれなりに出るようです。
そこをPostGISというライブラリがカバーしてくれるそうなので詳細なデータが必要な人は使ってみてください。
農研機構が使ってるらしいですよ(宣伝
ただPostGIS使ったりするとINDEXを有効利用してくれなかったりするらしいのでそこらへんの速度的な問題を解決してくれる人探してますw

####8/31追記 ここから####
INDEXについてツッコミをして頂きました。
アウトプットするとこうやってご指摘していただけるのでやっぱりアウトプットは大事ですね。
長方形の検索なら空間INDEXが有効なようです。
円は無理なのかな?
PostGISについてはもっと詳しく調べてみる必要がありますね。
####8/31追記 ここまで####


というわけでまとめ

モバイルWEBサービスなどで位置情報の連携を含めたサービスなどを作るときにコーディングの手間と集計を考えるとPostgresに位置情報を入れてDBで計算させるのがソースの可読性や運用面も踏まえてお手軽でいいのかなと思います。
クライアントさんに「ちょっとうちの会社から周囲〇〇メートルのお店のリストを表示させたいんだけど」的なこともSQLで実装できるので表示側はJAVAだろうがPHPだろうがRubyだろうが慣れた言語ですぐ実装出来ると思うのでみなさんの参考になれば幸いです。


さていい暇つぶしになった備忘録も書いたし盆休みに向けてラストスパートしたいと思います。
それではノシ