ゼロから始めるRails位置ゲーサーバ2・空間データ

こんにちは。クライアントチームサーバサイドエンジニア、コウです。
前回 Part 1 で、何故 PostgreSQL に決めたかについて書きました。
Part 2 においては、2つの空間データ型について簡単に紹介します。

※ 開発環境: macOS Sierra 10.12.5, PostgreSQL 9.6.3, PostGIS 2.3.2

2つの空間データ型

PostgreSQL のインストール方法PostGIS のインストール方法と、PostgreSQL ユーザー周りの設定はスキップさせてください。
MacOS だと、両方のインストールは homebrew で簡単に行なえます。
インストール及び設定ができたら、ターミナルに psql\dx を実行し、PostGISのバージョンを確認しましょう。
ref: macOS で PostGIS のインストールと設定の方法

  Name   | Version |   Schema   |                             Description
---------+---------+------------+---------------------------------------------------------------------
 postgis | 2.3.2   | public     | PostGIS geometry, geography, and raster spatial types and functions

Description に 書いてある geometry 型geography 型 は、今回の実装に関わる大事な空間データ型(Spatial Data Types)です。
もちろん、PostGISはほかの空間データ型もサポートしていますが、geometry 型と geography 型は基本中の基本です。

  • geometry 型 は平面(Planar)空間データ型です。geometry 型のメソッドで緯度経度から2点間の距離を計測した結果の単位は度(°)です。
  • geography 型 は回転楕円体(Ellipsoidal)空間データ型です。デフォルトSRID(空間参照系)は4326。geography 型のメソッドで緯度経度から2点間の距離を計測した結果の単位はメートル(m) です。

空間参照系(SRID)

SRID-4326 について、私はあまり詳しくないので、こちらの記事「空間参照系の概要」を参考してください。
デフォルトのgeometry 型にはSRIDがない(0)です。
なので、緯度経度計測する時に、SRIDを4326にしないと、結果が少々変わります。
私みたいにSRIDがあんまりわからない方々は、何も考えずにデフォルトのままにしてもいいと思います。

2つの型を使って計測してみる

両型共通なメソッド ST_Distance を使って、東京駅(Lat: 35.681167, Lng: 139.767052)と横浜駅(Lat: 35.465786, Lng: 139.622313)間の距離を計測して、 geometry と geography の違いを説明します。

まず、緯度経度をWKT (Well-known text) 形式にして:

  • 東京駅 POINT(139.767052 35.681167)
  • 横浜駅 POINT(139.622313 35.465786)

つぎに、2つのファンクション ST_GeomFromTextST_GeogFromText でWKTを geometry と geography に変換しないといけないです。
最後はSQLの実行です。

geometry の SQL

SELECT ST_Distance(
  ST_GeomFromText('POINT(139.767052 35.681167)'),
  ST_GeomFromText('POINT(139.622313 35.465786)')
);

実行結果は 0.259496345411655 度 です。

ST_Distance の日本語ドキュメントにおいて:

ジオメトリ型については、二つのジオメトリの、2次元の最小デカルト距離 (空間参照系に基づきます)を、投影法の単位で返します。

なので、ここでデカルト距離の計算式で検証してみました。

f:id:gssxgss:20170712181235p:plain

結果はバッチリですが、メートルへ変換しないと使えなさそうです。

geography のSQL

SELECT ST_Distance(
  ST_GeogFromText('POINT(139.767052 35.681167)'),
  ST_GeogFromText('POINT(139.622313 35.465786)')
);

実行結果は 27261.54830124 メートル です。

ジオグラフィ型については、デフォルトでは、二つのジオメトリ間の測地距離をメートル単位で返します。

国土地理院のツールで結果を検証してみます。

f:id:gssxgss:20170712181244p:plain

geography での計算結果もバッチリです〜

キミにきめた!

geometry と geography は異なった空間型なので、使えるファンクションも多少違います。
PostGIS のドキュメントにファンクションサポート一覧テーブルがあります: PostGIS Function Support Matrix
テーブルから見ると、 geometry のファンクションは geography のよりだいぶ多く、 geometry のCPU実行時間は geography より短いです。
でも、どちらにするか、プロジェクトの仕様により、選びましょう。
(PostGIS ドキュメントからのアドバイス:When to use Geography Data type over Geometry data type

今回の仕様は「ユーザーの緯度経度から半径nキロメートル内のマーカーを取得する」なので、geography 型は相応しいです。
というわけで、今回マーカーデータのテーブルの型は geography にしましょう。

次回の Part 3 から、Rails に突入します。
皆さんお楽しみにしてくださいね〜(<ゝω・)☆


document refs: PostGIS 2.3.3dev Manual, PostGIS 2.4.0dev マニュアル

(/・ω・)/ ★☆★☆★ サーバサイドエンジニア大絶賛募集中 ★☆★☆★ \(・ω・\)