はじめに
PostGISにはベクタとしてジオメトリ型があります。CREATE TABLE
でカラムにGEOMETRY
を指定したり、'POINT(135 35)'::GEOMETRY
などとキャストしたり、でおなじみになっていることと思います。ベクタデータ型としては、もうひとつジオグラフィ型があります。
ここでは、ジオグラフィについて説明します。
ジオグラフィ型
ジオグラフィ型は、ジオメトリ型と同じで、ポイント、ラインストリング、ポリゴンをサポートします。ただし、空間参照系は EPSG:4326, EPSG:4612 等の地理座標系(経度緯度)のみサポートします。ここまで書くと、ジオメトリの特化のような印象を持たれると思います。なぜあえてジオグラフィ型が導入されたのでしょう。
地理座標系の計算は面倒
投影座標系の場合、歪みがあることはあるのですが、それに目をつぶれば、地図上でユークリッド距離(定規をあてた距離)を縮尺で割れば実際の距離が算出できます。これは球面(たいていの場合は、正確に言うと回転楕円体面)を平面に投影したからこそできます。
地理座標系の場合は、そうはいきません。一般的に地理的な距離は地表面上に拘束されますが、ユークリッド距離を測ろうとすると、地中を通ってしまいますので、地理的な距離を計測できません。
たとえば、北極点から南極点の「距離」は、ユークリッド距離をとると、概ね1万2千キロ、地表を通ると概ね2万キロです。前者の方が近いですが、地球内の核を通る必要があり、現実的ではありません。
さらにいうと、極点間の距離は特別な点同士なので簡単に出ますが、地理座標系上の任意の2点間の距離を求めるには、球面三角法を使う必要があります。球面でなく回転楕円体面での計算となると、さらに面倒になります。
地理座標系に関する関数
PostGISの関数は、当初は、投影座標系を前提とした関数を提供していました。これらの関数は地理座標系でも通りますが、面倒な地理座標系に関する計算を行わなかったのです。PostGIS 2.xでは、投影座標系に関する関数だけでなく、地理座標系に関する関数も整備されつつあります。
この際、同じ関数名で地理座標系に関する関数を使いたいと思うわけです。距離を求めるのはST_Distance()
であって欲しい、ST_Distance_Spheroid()
だとすっきりしない。
そこで、関数のオーバロードを使って、ジオメトリ型なら投影座標系を前提とした計算を行い、ジオグラフィ型なら地理座標系を前提とした計算を行うようにしています。
つまりジオグラフィ型とは
PostGISに地理座標系を前提とした計算を行ってもらうためのスイッチです。
ジオグラフィ型の性質
ジオメトリ型との違い
前述しているところもありますが、ジオグラフィ型がジオメトリ型と比べて制限されている点を列挙します。
- 空間参照系は地理座標系のみサポート
- シンプルフィーチャー(ポイント、ラインストリング、ポリゴンと、これらのマルチ系)のみサポート(CIRCULARSTRING等はサポートされない)
ジオメトリと行き来できる
ジオメトリからジオグラフィ、ジオグラフィからジオメトリのキャストは、両者とも通ります。ただし、CIRCULARSTRING等の、ジオグラフィがサポートしていないフィーチャータイプでは、当然ながらキャストできません。
わざわざジオメトリ型にキャストしたうえで、ジオグラフィ型にキャストしなおしてみます。
db=# SELECT ('SRID=4612;POINT(135 35)'::GEOMETRY)::GEOGRAPHY;
geography
----------------------------------------------------
0101000020041200000000000000E060400000000000804140
(1 行)
次に、地理座標系でなく投影座標系である場合にエラーが出ることを確認しましょう。
db=# SELECT 'SRID=3857;POINT(135 35)'::GEOGRAPHY;
ERROR: Only lon/lat coordinate systems are supported in geography.
行 1: SELECT 'SRID=3857;POINT(135 35)'::GEOGRAPHY;
なお、EPSG:3857 (Webメルカトル)で135 35
となるポイントは、アフリカのあたりです。
EWKB, EWKTでの違いはない
「ジオメトリと行き来できる」から想像がつくかと思いますが、EWKB, EWKT表現においては、ジオメトリとジオグラフィの違いはありません。
db=# SELECT ST_AsEWKT('0101000020041200000000000000e060400000000000804140'::GEOGRAPHY);
st_asewkt
-------------------------
SRID=4612;POINT(135 35)
(1 行)
db=# SELECT ST_AsEWKT('0101000020041200000000000000e060400000000000804140'::GEOMETRY);
st_asewkt
-------------------------
SRID=4612;POINT(135 35)
(1 行)
ジオグラフィ関数をためしてみる
地理座標系ジオメトリの「距離」
空間参照系では、「地理座標系では定規をあてて距離を測れない」と言いました。
無理やりに測ることができますので、見てみましょう。
東経135度 北緯35度から東経140度 北緯40度までの「距離」を求めてみます。
まずは「お手本」を知っておきます。http://vldb.gsi.go.jp/sokuchi/surveycalc/surveycalc/bl2stf.html で計算してみると、距離は709253.729[m]
となりました(なお方位は37.047375[deg]
)。
db=# SELECT ST_Distance('SRID=4326;POINT(135 35)'::GEOMETRY, 'SRID=4326;POINT(140 40)'::GEOMETRY);
st_distance
------------------
7.07106781186548
(1 行)
2点間の距離が「7.07度」である、というのです。この値に意味は見て取れません。
あえて言うなら、地球の半径をR
として、R*7.07/180*PI
で、距離っぽいものが求まります。R=6378137
とすると次のようになるでしょうか。
db=# SELECT 6378137.0 * ST_Distance('SRID=4326;POINT(135 35)'::GEOMETRY, 'SRID=4326;POINT(140 40)'::GEOMETRY) / 180.0 * pi();
?column?
------------------
787147.668181572
(1 行)
787147.668181572[m]
となりましたが、「お手本」の709253.729[m]
と比べてどうでしょうか?全く違う値となりました。
この計算は、正距円筒図法に投影した場合の距離と言えるでしょう。http://ja.wikipedia.org/wiki/正距円筒図法 を見ていただければ分かるかと思いますが、非常にゆがんでいます。
ジオグラフィでは
さきほどのクエリをいじって、GEOMETRY
にキャストしているところをGEOGRAPHY
にキャストしたうえで、ST_Distance
を実行してみましょう。
db=# SELECT ST_Distance('SRID=4326;POINT(135 35)'::GEOGRAPHY,'SRID=4326;POINT(140 40)'::GEOGRAPHY);
st_distance
------------------
709253.729429939
(1 行)
「お手本」の709253.729[m]
にかなり近い結果となりました。
測地系の違い (2.1以降)
2.0では、測地系の準拠回転楕円体がWGS84でなければなりませんでしたが、2.1以降は、他の準拠回転楕円体に対応するようになりました。
測地系によって距離が若干異なるのを見てみましょう。
db=# SELECT ST_Distance('SRID=4301;POINT(135 35)'::GEOGRAPHY,'SRID=4301;POINT(140 40)'::GEOGRAPHY);
st_distance
------------------
709174.338665577
(1 行)
db=# SELECT ST_Distance('SRID=4612;POINT(135 35)'::GEOGRAPHY,'SRID=4612;POINT(140 40)'::GEOGRAPHY);
st_distance
------------------
709253.729425222
(1 行)
db=# SELECT ST_Distance('SRID=4019;POINT(135 35)'::GEOGRAPHY,'SRID=4019;POINT(140 40)'::GEOGRAPHY);
st_distance
------------------
709253.729425222
(1 行)
EPSG:4301 (Tokyo1918), EPSG:4612 (JGD2000) と EPSG:4326 (WGS84) は相互に違います。EPSG:4301 がかなり違っているのが分かると思います。
また、GRS80地理座標系 (EPSG:4019)とEPSG:4612は同じ結果になりました。準拠回転楕円体が同じであるからです。
ジオグラフィ型かジオメトリ型か
投影座標系(平面に投影した座標系)を使う場合はジオメトリでなければならないので検討の余地もありません。
地理座標系(経度・緯度)はジオメトリ、ジオグラフィのどちらもでかまいません。さて、ジオグラフィ型とジオメトリ型、どちらを選択すれば良いでしょうか。
ジオグラフィ型の利点は、投影座標系(特に投影法)に関する知識がなくてもアプリケーションを組める点にあります。
欠点としては、計測関数の計算コストがジオメトリ型よりも高くなりそうな点と、関数のサポート状況がよくない点が挙げられるでしょう。
以上から、データ量が少ない(インデクスが相当絞り込める場合も含む)か、計算時間が長くなってもいい場合であって、かつ使おうとしている関数がジオグラフィ型をサポートしている場合にはジオグラフィとすると良いかと思います。まずはジオグラフィ。対して、データ量が膨大であったり、頻繁に計算しなければならない場合などにジオメトリを使うことにすると良いように思います。
でも個人の意見ですが、日頃はジオメトリ型を使い、ジオグラフィ型で関数に投げた方が有利な場合(各種計測関数はたぶんジオグラフィ型にした方が楽です)にだけ、ジオグラフィ型にキャストして関数に投げる、というふうに使うのが良いのではないかと思います。
おわりに
ジオグラフィというものがあり、ジオメトリと大差はないけれども、ジオグラフィ型で計測関数等を使うと適切に計算してくれることを紹介しました。