Open Geospatial Consortium (OGC) は、地理空間データのモデルを提供するためにSimple Features Access (SFA) 標準を開発しました。これは、ジオメトリー (Geometry)の基本的な空間タイプを、空間解析処理実行のための操作や変換といった演算に沿って定義します。PostGIS は空間解析タスクを実現するために OGC ジオメトリーモデル値を PostgreSQL データ型のgeometry、geographyとして実装しています。
ジオメトリーは抽象的なタイプです。ジオメトリー値は複数ある具体的なサブタイプの一つに属します。サブタイプは様々な種類の様々な次元のジオメトリーの形状を表現するものです。これらには原子的なタイプであるポイント (Point)、ラインストリング (LineString)、リニアリング (LinearRing)、ポリゴン (Polygon)があります。また、コレクション (collection) (訳注: 「マルチ系」と書いている場合があります) タイプのマルチポイント (MultiPoint)、マルチラインストリング (MultiLineString)、マルチポリゴン (MultiPolygon)、ジオメトリーコレクション (GeometryCollection)があります。 Simple Features Access - Part 1: Common architecture v1.2.1では多面体サーフェス (PolyhedralSurface)、三角形 (Triangle)、TINが追加されています。
ジオメトリーは 2次元デカルト平面上の形状をモデル化しています。多面体サーフェス、三角形、TIN は 3次元空間内の形状を表現することもできます。形状のサイズと位置は座標によって指定されます。それぞれの座標は、平面上で位置を判定する X と Y の座標軸値を持っています。形状はポイントと線分から構築され、ポイントは単一の座標で定められ、線分は二つの座標値から定められます。
座標には Z 座標や M 座標が含まれることがあります。Z 座標はしばしば高さを示します。M 座標は時刻や距離と言った計測された値を持ちます。ジオメトリーに Z 値または M 値がある場合には、ジオメトリーの個々の点において値が設定されなければなりません。ジオメトリーが Z 値または M 値を持つ場合には、座標次元 は 3次元になります。Z 値と M 値の両方を持つ場合には、4次元になります。座標次元は少なくとも X 座標と Y 座標を持つので、少なくとも 2です。
ジオメトリー値は、そのジオメトリーが組み込まれている座標系を示す空間参照系に関連付けられます。空間参照系はジオメトリーの SRID 番号で識別されます。X 軸と Y 軸の単位は空間参照系によって決まります。平面参照系では伝統的に X 座標値と Y 座標値が東、北をそれぞれ示します。地理参照系では、経度と緯度を表現しています。SRID が 0の場合には、軸の単位が無い、無限の直交平面を表します。「空間参照系」を参照して下さい。
ジオメトリーの 次元 はジオメトリータイプの属性です。ポイントタイプの次元は 0、ライン系タイプの次元は 1、ポリゴン系タイプの次元は 2、立体系タイプの次元は 3です。コレクションの次元は含まれる要素の次元の最大値です。
ジオメトリー値はemptyになることがあります。空値とは、非マルチ系ジオメトリーの場合は頂点を持っていないという意味で、コレクションでは要素を持っていないという意味です。
ジオメトリー値の重要なプロパティは範囲 (extent)またはバウンディングボックス (bounding box)です。OGC モデルではエンベロープ (envelope)と呼ばれています。これは、ジオメトリーの座標を囲む 2次元または 3次元のボックスです。ジオメトリーの座標空間内の範囲を表現するための、また、二つのジオメトリーの相互関係をチェックするための、効率の良い方法です。
「次元拡張 9交差モデル」 に書かれている通りジオメトリーモデルによってトポロジー面での空間関係の評価が可能になります。これに対応するために、ジオメトリータイプごとに 内部、境界、外部 が定義されています。ジオメトリーはトポロジー的に閉じていて、常に境界を持っています。境界はジオメトリーの幾何次元数から 1引いた幾何次元になります。たとえば、POLYGON の境界はリング (LINESTRING) で、LINESTRING の境界は端点 (POINT) で、POINT の境界は空です。
OGC ジオメトリーモデルでは、ジオメトリータイプごとに妥当性規則を規定しています。これらの規則によってジオメトリー値が現実的な状況を表現することが保証されます (例えば、穴が外側リングのさらに外にあるポリゴンを生成できますが、ジオメトリーとしては意味をなさないものであり、不正となります)。PostGIS は、また不正なジオメトリー値を格納、処理できます。これにより、必要に応じてジオメトリーの検出、修正が可能です。「ジオメトリー検証」 を参照して下さい。
ポイントは、座標空間内の一つの位置を表現する 0次元ジオメトリーです。
POINT (1 2) POINT Z (1 2 3) POINT ZM (1 2 3 4)
ラインストリングは連続する一連の線分で形成される 1次元のラインです。線分はそれぞれ 2点で定義付けられ、ある線分の終点は次の線分の始点を形成します。OGC 妥当なラインストリングには、0または 2以上のポイントがあります。ただし PostGIS はラインストリングの一つのポイントを許容します。ラインストリングは、自身とクロスする場合があります (自己交差)。始端と終端とが同じ場合にはラインストリングは閉じたことになります。自己交差しない場合には、ラインストリングは単純です。
LINESTRING (1 2, 3 4, 5 6)
リニアリングは閉じていて、かつ単純なラインスリングです。始端と終端は同じでなければなりませんし、ラインは自己交差してはなりません。
LINEARRING (0 0 0, 4 0 0, 4 4 0, 0 4 0, 0 0 0)
ポリゴンは 2次元平面領域です。一つの外側の境界 (殻) と 0個以上の内の境界 (穴) とで区切られています。それぞれの境界はリニアリングです。
POLYGON ((0 0 0, 4 0 0, 4 4 0, 0 4 0, 0 0 0), (1 1 0, 2 1 0, 2 2 0, 1 2 0, 1 1 0))
マルチポイントはポイントのコレクションです。
MULTIPOINT ( (0 0), (1 2) )
マルチラインストリングはラインストリングのコレクションです。各要素が閉じている場合には、そのマルチラインストリングは閉じています。
MULTILINESTRING ( (0 0, 1 1, 1 2), (2 3, 3 2, 5 4) )
MULTIPOLYGON はポリゴンの重なりも隣接も無いコレクションです。コレクション内のポリゴンは有限数のポイントで接触することがあります (二つのポリゴンがエッジを居有する場合には隣接します。境界のポイントまたはエッジで共有されている場合には接触しています)。MULTIPOLGYON の詳細情報については 「ジオメトリー検証」 をご覧下さい。
MULTIPOLYGON (((1 5, 5 5, 5 1, 1 1, 1 5)), ((6 5, 9 1, 6 1, 6 5)))
ジオメトリーコレクションは、ジオメトリーの異種 (混合) のコレクションです。
GEOMETRYCOLLECTION ( POINT(2 3), LINESTRING(2 3, 3 4))
多角形はサーフェスは、パッチまたはエッジを共有する面の隣接するコレクションです。それぞれのパッチは平面ポリゴンです。ポリゴンが Z 値を持つ場合には、サーフェスは 3次元になります。
POLYHEDRALSURFACE Z ( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )
三角形は三つの異なる非共線頂点で定義されるポリゴンです。三角形はポリゴンですので、四つの座標で指定され、一つ目と四つ目は同じです。
TRIANGLE ((0 0, 0 9, 9 0, 0 0))
TIN はTriangulated Irregular Networkを表現する、オーバラップしない三角形のコレクションです。
TIN Z ( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )
ISO/IEC 13249-3 SQL Multimedia - Spatial標準 (SQL/MM) は、OGC SFA を拡張して、曲線ジオメトリーを含むサブタイプを定義しています。SQL/MM タイプは XYM, XYZ, XYZM に対応します。
|
|
|
SQL-MM 実装での全ての浮動小数点数の比較では、所定の丸め誤差があります。現在は1E-8です。 |
曲線ストリングは、基本的な曲線タイプです。線形の世界のラインストリングに似ています。単一の円弧線分は、始点、終点 (1番目と 3番目)、弧の他の点の三つの点で定義されます。閉じた円を指定するには、開始点と終了点を同じにし、中間点を対称点 (円弧の中心) に置きます。連続する円弧では、前の円弧の終端と次の円弧の始端とが同じです。よって曲線ストリングは 1以上の奇数個のポイントを持つことになります。
CIRCULARSTRING(0 0, 1 1, 1 0) CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0)
複合曲線は、曲線区間と直線区間の両方を含むことができる単一の連続した曲線です。このことは、整形された要素を持つことに加えて、全ての要素の最後のポイントは次の要素の最初のポイントでなければならないことを意味します。
COMPOUNDCURVE( CIRCULARSTRING(0 0, 1 1, 1 0), (1 0, 0 1))
曲線ポリゴンは、外側の輪がひとつで 0以上の内側のリングがある点はポリゴンに似ています。違いは、ポリゴンのリングはラインストリングですが曲線ポリゴンのリングは曲線ストリングまたは複合ストリングである点です。
PostGIS 1.4から、PostGIS で曲線ポリゴンで複合曲線をサポートするようになりました。
CURVEPOLYGON( CIRCULARSTRING(0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 3 3, 3 1, 1 1) )
例: CIRCULARSTRING と LINESTRING からなる COMPOUNDCURVE で定義される外側リングを持ち、CIRCULARSTRING で定義される穴を持つ CURVEPOLYGON
CURVEPOLYGON(
COMPOUNDCURVE( CIRCULARSTRING(0 0, 2 0, 2 1, 2 3, 4 3),
(4 3, 4 5, 1 4, 0 0)),
CIRCULARSTRING(1.7 1, 1.4 0.4, 1.6 0.4, 1.6 0.5, 1.7 1) )
マルチ曲線は曲線のコレクションで、ラインストリング、曲線ストリング、複合曲線を含むことができます。
MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4))
マルチサーフェスはサーフェスのコレクションです。サーフェスは (線形) ポリゴンまたは曲線ポリゴンとなることができます。
MULTISURFACE( CURVEPOLYGON( CIRCULARSTRING( 0 0, 4 0, 4 4, 0 4, 0 0), (1 1, 3 3, 3 1, 1 1)), ((10 10, 14 12, 11 10, 10 10), (11 11, 11.5 11, 11 11.5, 11 11)))
OGC SFA 仕様では、ジオメトリー値を外部で使用するための表現として二つの標準書式が定義されています。Well-Known Text (WKT) と Well-Known Binary (WKB) です。WKT とWKB は両方ともそのオブジェクトを定義するタイプと座標に関する情報を含んでいます。
Well-Known Text (WKT) で空間データの標準的な文字表現が可能です。空間オブジェクトの WKT 表現の例を次に挙げます。
POINT(0 0)
POINT Z (0 0 0)
POINT ZM (0 0 0 0)
POINT EMPTY
LINESTRING(0 0,1 1,1 2)
LINESTRING EMPTY
POLYGON((0 0,4 0,4 4,0 4,0 0),(1 1, 2 1, 2 2, 1 2,1 1))
MULTIPOINT((0 0),(1 2))
MULTIPOINT Z ((0 0 0),(1 2 3))
MULTIPOINT EMPTY
MULTILINESTRING((0 0,1 1,1 2),(2 3,3 2,5 4))
MULTIPOLYGON(((0 0,4 0,4 4,0 4,0 0),(1 1,2 1,2 2,1 2,1 1)), ((-1 -1,-1 -2,-2 -2,-2 -1,-1 -1)))
GEOMETRYCOLLECTION(POINT(2 3), LINESTRING(2 3,3 4))
GEOMETRYCOLLECTION EMPTY
WKT の入出力は関数ST_AsTextとST_GeomFromTextによって提供されます。
text WKT = geometry; geometry = ST_GeomFromText(text WKT, SRID);
例えば、SRID 312 で WKT から空間オブジェクトを生成、挿入する手続きは次の通りです:
INSERT INTO geotable ( geom, name )
VALUES ( ST_GeomFromText('POINT(-126.4 45.32)', 312), 'A Place');
Well-Known Binary (WKB) は、空間データのバイナリデータ (バイト列) で、移植可能かつ正確な表現です。空間オブジェクトの WKB 表現を次に挙げます。
WKT: POINT(1 1)
WKB: 0101000000000000000000F03F000000000000F03
WKT: LINESTRING (2 2, 9 9)
WKB: 0102000000020000000000000000000040000000000000004000000000000022400000000000002240
WKB の入出力は関数ST_AsBinaryとST_GeomFromWKBが提供されています。次のように使います。
bytea WKB = ST_AsBinary(geometry); geometry = ST_GeomFromWKB(bytea WKB, SRID);
たとえば、WKB から空間オブジェクトの生成、挿入は次のようにします。
INSERT INTO geotable ( geom, name )
VALUES ( ST_GeomFromWKB('\x0101000000000000000000f03f000000000000f03f', 312), 'A Place');
PostGIS は、geometryという PostgreSQL データ型を定義して、OGC Simple Features model を実装しています。これで、内部タイプコード (GeometryTypeとST_GeometryType参照) で全てのジオメトリーのサブタイプを表現します。これにより、 geometry型のカラムを持つよう定義されたテーブルの行として、空間地物をモデリングすることが可能となります。
geometry データ型は 透過、すなわちジオメトリー値に関する関数の実行で全てのアクセスが可能です。関数でジオメトリーオブジェクトの生成、全ての内部フィールドのアクセスと更新、および新しいジオメトリー値の計算が可能です。PostGIS は OGC Simple feature access - Part 2: SQL option (SFS) 仕様で指定されている全ての関数およびその他の多数の関数に対応しています。関数の完全な一覧については 7章PostGIS リファレンス を参照して下さい。
|
|
|
PostGIS は、空間関数にプリフィクス"ST_"を付けて、SFA 標準に従っています。これは、"Spatial and Temporal (空間と時間)"を示していますが、標準の時間の部分はまだ開発していません。その代わりに"Spatial Type (空間タイプ)"と解釈できます。 |
SFA 標準は、空間オブジェクトは空間参照系識別子 (SRID) を含むと規程しています。SRID は、空間オブジェクトをデータベースに挿入するために生成した時に求められます (デフォルトとして 0になるかも知れません)。ST_SRIDと「空間参照系」をご覧下さい。
ジオメトリーのクエリを効率的にするため、PostGIS では様々な種類の空間インデックスを定義しています。詳細については「空間インデックス」と「空間インデックスを使う」をご覧下さい。
OGC SFA 仕様は、まず 2次元ジオメトリーのみに対応しました。また、入出力表現にジオメトリの SRID は取り入れていまません。OGC SFA 仕様 1.2.1 (ISO 19125標準に準拠) では 3次元 (XYZ) と M 値 (XYM と XYZM) 座標に対応するようになりましたが、SIRD 値の取り込みは依然行われていません。
これらの制限のため、PostGIS では拡張書式である EWKB と EWKT を定義しました。3次元 (XYZ, XYM) と 4次元 (XYZN) 座標系に対応し、SRID 情報を取り込めるようにしました。すべてのジオメトリー情報を含めたので、PostGIS は EWKB を格納用書式 (DUMP ファイル等) として使えるようになりました。
PostGIS データオブジェクトの「カノニカルな形式」のために EWKB と EWKT を使います。入力では、バイナリデータのカノニカルな形式は EWKB、テキストデータについてはEWKB か EWKT が受け付けられます。これにより、HEXEWKB または EWKT のテキスト値から::geometryを使用してキャストを行い、ジオメトリー値が生成できるようになりました。出力では、バイナリのカノニカルな形式は EWKB で、テキストはHEXEWKB (HEX エンコードを施した EWKB) です。
たとえば、この手続きでは、EWKT テキスト値からのキャストでジオメトリーを生成して、HEXWKB のカノニカルな形式を使った出力を行います。
SELECT 'SRID=4;POINT(0 0)'::geometry;
geometry ---------------------------------------------------- 01010000200400000000000000000000000000000000000000
PostGIS EWKT 出力は OGC WKT と次の通り相違点があります。
XYZ ジオメトリーで Z 修飾子が省略されます。
OGC: POINT Z (1 2 3)
EWKT: POINT (1 2 3)
M 値を含む XYM ジオメトリー:
OGC: POINT M (1 2 3)
EWKT: POINTM (1 2 3)
4次元ジオメトリーで ZM 修飾子を省略:
OGC: POINT ZM (1 2 3 4)
EWKT: POINT (1 2 3 4)
EWKT は、次のように OGC/ISO 書式で発生しうる過剰次元と不整合を回避しています。
POINT ZM (1 1)
POINT ZM (1 1 1)
POINT (1 1 1 1)
|
|
|
PostGIS の拡張書式は OGC 書式の上位互換であり、全ての妥当な OGC WKB/WKT は妥当なEWKB/EWKT でもあります。しかし、OGC が PostGIS の定義と衝突する方法で書式を拡張した場合には、将来的に書式を変更する可能性があります。ゆえに、この互換性に*頼るべきではありません*! |
空間オブジェクトの EWKT テキスト表現の例:
POINT(0 0 0) -- XYZ
SRID=32632;POINT(0 0) -- SRID 付き XY
POINTM(0 0 0) -- XYM
POINT(0 0 0 0) -- XYZM
SRID=4326;MULTIPOINTM(0 0 0,1 2 1) -- SRID 付き XYM
MULTILINESTRING((0 0 0,1 1 0,1 2 1),(2 3 1,3 2 1,5 4 1))
POLYGON((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0))
MULTIPOLYGON(((0 0 0,4 0 0,4 4 0,0 4 0,0 0 0),(1 1 0,2 1 0,2 2 0,1 2 0,1 1 0)),((-1 -1 0,-1 -2 0,-2 -2 0,-2 -1 0,-1 -1 0)))
GEOMETRYCOLLECTIONM( POINTM(2 3 9), LINESTRINGM(2 3 4, 3 4 5) )
MULTICURVE( (0 0, 5 5), CIRCULARSTRING(4 0, 4 4, 8 4) )
POLYHEDRALSURFACE( ((0 0 0, 0 0 1, 0 1 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 1 0 0, 0 0 0)), ((0 0 0, 1 0 0, 1 0 1, 0 0 1, 0 0 0)), ((1 1 0, 1 1 1, 1 0 1, 1 0 0, 1 1 0)), ((0 1 0, 0 1 1, 1 1 1, 1 1 0, 0 1 0)), ((0 0 1, 1 0 1, 1 1 1, 0 1 1, 0 0 1)) )
TRIANGLE ((0 0, 0 10, 10 0, 0 0))
TIN( ((0 0 0, 0 0 1, 0 1 0, 0 0 0)), ((0 0 0, 0 1 0, 1 1 0, 0 0 0)) )
これらの書式を使う入出力は次の関数を使うと有効です。
bytea EWKB = ST_AsEWKB(geometry); text EWKT = geometry; geometry = ST_GeomFromEWKB(bytea EWKB); geometry = ST_GeomFromEWKT(text EWKT);
たとえば、EWKT を使って PostGIS の空間オブジェクトを作成し挿入するステートメントは次の通りです。
INSERT INTO geotable ( geom, name )
VALUES ( ST_GeomFromEWKT('SRID=312;POINTM(-126.4 45.32 15)'), 'A Place' )
geographyデータタイプによって地理座標 ("geographic", "geodetic", "lat/lon", "lon/lat"など) 上の空間地物表現にネイティブに対応できます。地理座標系は角度 (度) 単位で表現される球面座標系です。
PostGIS ジオメトリー型の基礎は平面です。平面上の 2点間の最短経路は直線です。ジオメトリーに関する関数 (面積、距離、長さ、インタセクション等) は直線ベクトルとデカルト平面を使って計算しています。これで実装が簡単になり実行速度も上がりますが、地球の球面の上にあるデータについては不正確になります。
PostGIS ジオグラフィというデータ型は球面モデルに基づいています。球面上の 2点の最短経路は大円の弧にあたります。ジオグラフィの関数 (面積、距離、長さ、インタセクション等) は球面上の弧を使います。球面上の世界の形状を考慮に入れるので、より正確な結果が得られます。
基礎となる数学はより複雑になるため、ジオグラフィ型で定義された関数はジオメトリ型で定義された関数よりも少なくなります。時間が経つにつれて新しいアルゴリズムが追加されて、ジオグラフィの機能が拡大していきます。回避策として、ジオメトリ型とジオグラフィ型との相互変換が可能です。
ジオメトリー型のように、ジオグラフィデータは空間参照系識別子 (Spatial Reference System Identifier, SRID) を持っています。 spatial_ref_sysで定義されている地理座標系 (経度/緯度) の空間参照系で利用できます。独自の地理座標系の空間参照系を追加することもできます。「ユーザー定義空間参照系」を参照してください。
計測関数 (例 ST_Distance、ST_Length、ST_Perimeter、ST_Area) によって返されるものの単位と、ST_DWithinの引数で与えられる距離との、空間参照系の単位は、メートルです。
ジオグラフィデータを格納するテーブルは、SQL ステートメントCREATE TABLEにgeography型のカラムを付けることで生成することができます。2次元ラインストリングを WGS84地理座標系 (SRID 4326) で保存するジオグラフィカラムを持つテーブルを生成する例を次に示します。
CREATE TABLE global_points ( id SERIAL PRIMARY KEY, name VARCHAR(64), location geography(POINT, 4326) );
二つの任意の型修飾子に対応するジオグラフィ型:
空間の型修飾子は、カラム内で許される形状の種類や次元を規制します。値によって空間型は POINT、LINESTRING、POLYGON、MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION が可能です。ジオグラフィ型は曲線や三角形、多面体サーフェスに対応していません。型修飾子に後置詞 Z、M、ZM を付けることで、座標次元の制約に対応しています。たとえば、'LINESTRINGM'は、3次元で 3番目の軸は Mであるラインストリングのみを許します。同様に'POINTZM'では 4次元 (XYZM) データが求められます。
SRID 修飾子は空間参照系(SRID) を特定の数値になるよう制約します。省略した場合には、デフォルトは 4326 (WGS84地理座標系) となり、全ての計算は WGS84を使ったものになります。
ジオグラフィカラムを持つテーブルの生成の例を次に挙げます。
SRID がデフォルトの 4326 (WGS84 経度/緯度) である 2次元ポイントジオグラフィを持つテーブルの生成:
CREATE TABLE ptgeogwgs(gid serial PRIMARY KEY, geog geography(POINT));
NAD83緯度/経度の 2次元ポイントジオグラフィを持つテーブルの生成:
CREATE TABLE ptgeognad83(gid serial PRIMARY KEY, geog geography(POINT, 4269));
SRID を 4326で明示した 3次元 (XYZ) ポイントジオグラフィを持つテーブルの生成:
CREATE TABLE ptzgeogwgs84(gid serial PRIMARY KEY, geog geography(POINTZ, 4326));
SRID がデフォルトの 4326である 2次元ラインストリングジオグラフィを持つテーブルの生成:
CREATE TABLE lgeog(gid serial PRIMARY KEY, geog geography(LINESTRING));
SRID がデ 4326 (NAD 1927 経度/緯度) である 2次元ポリゴンジオグラフィを持つテーブルの生成:
CREATE TABLE lgeognad27(gid serial PRIMARY KEY, geog geography(POLYGON, 4267));
ジオグラフィカラムはgeography_columnsシステムビューに登録されます。geography_columnsビューにクエリを出してテーブルを見るには、次の通りにします。
SELECT * FROM geography_columns;
空間インデックスはジオメトリーカラムと同じように機能します。PostGIS は、カラム型がジオグラフィであると通知したうえで、ジオメトリーに使う通常の平面用インデックスでなく、球面を基にした適切なインデックスを生成します。
CREATE INDEX global_points_gix ON global_points USING GIST ( location );
ジオメトリーと同じ方法でジオグラフィテーブルにデータを挿入できます。ジオメトリーデータは、SRID 4326の場合には、ジオグラフィ型に自動キャストされます。EWKT と EWKB書式はジオグラフィ値を指定するために使うことができます。
INSERT INTO global_points (name, location) VALUES ('Town', 'SRID=4326;POINT(-110 30)');
INSERT INTO global_points (name, location) VALUES ('Forest', 'SRID=4326;POINT(-109 29)');
INSERT INTO global_points (name, location) VALUES ('London', 'SRID=4326;POINT(0 49)');
spatial_ref_sysテーブルにある地理 (経度/緯度) 参照系は、ジオグラフィの SRID として指定することができます。非地理座標系を使うとエラーが発生します。
NAD83 経度/緯度 地理座標系値:
SELECT 'SRID=4269;POINT(-123 34)'::geography;
geography ---------------------------------------------------- 0101000020AD1000000000000000C05EC00000000000004140
NAD27 経度/緯度 地理座標系値:
SELECT 'SRID=4267;POINT(-123 34)'::geography;
geography ---------------------------------------------------- 0101000020AB1000000000000000C05EC00000000000004140
NAD83 UTM はメートル単位の投影座標系なので失敗します。
SELECT 'SRID=26910;POINT(-123 34)'::geography;
ERROR: Only lon/lat coordinate systems are supported in geography.
クエリと計測関数はメートル単位となります。そのため距離パラメータはメートル (面積の場合は平方メートル) 単位となります。
1000 km 範囲内の行を検索する:
SELECT name FROM global_points WHERE ST_DWithin(location, 'SRID=4326;POINT(-110 29)'::geography, 1000000);
シアトルからロンドンへの (LINESTRING(-122.33 47.606, 0.0 51.5)) 大円航路を行く航空機がレイキャビク (POINT(-21.96 64.15)) にどれだけ近づくかを計算することで、ジオグラフィの力を見ことができます (航路の地図表示)。
ジオグラフィ型は、レイキャビクとシアトル-ロンドン間の大円航路との距離について、球面上で 122.235 km という本当の最短距離を計算します。
SELECT ST_Distance('LINESTRING(-122.33 47.606,0.0 51.5)'::geography, 'POINT(-21.96 64.15)'::geography);
st_distance ----------------- 122235.23815667
ジオメトリー型では、平面の世界地図上で見て、レイキャビクとシアトル-ロンドン間の直線とのデカルト距離が計算され、意味がありません。計算結果の名目上の単位は「度」ですが、点間の本当の角度差に応じるものではなく、「度」と呼ぶこと自体が不正確です。
SELECT ST_Distance('LINESTRING(-122.33 47.606,0.0 51.5)'::geometry, 'POINT(-21.96 64.15)'::geometry);
st_distance -------------------- 13.342271221453624
ジオグラフィ型によって、経度緯度座標でデータを格納できるようになりましたが、ジオグラフィで定義されている関数が、ジオメトリーより少ないのと、実行に CPU 時間がかかる、というところが犠牲になっています。
選択した型が、期待する領域から出ないことを、ジオメトリー型にして使用する条件とすべきです。使用するデータは地球全体か、大陸か、州か、自治体か?
データが小さいエリア内におさまるなら、適切な投影を選択してジオメトリーを使うのが、効率面でも機能面でも最も良い方法です。
データが地球全体か大陸なら、ジオグラフィで投影法の細かい問題を気にせずにシステムを構築できるでしょう。経度/緯度のデータを保存して、ジオグラフィで定義された関数使います。
投影法を理解していなくて、学習したくもなくて、かつ、ジオグラフィで使える関数が限られていることを受け入れるのなら、ジオグラフィを使った方が簡単です。単純にデータを経度/緯度でロードして、そこから進めて下さい。
ジオグラフィとジオメトリー間のサポート状況の比較については「PostGIS 関数対応マトリクス」をご覧下さい。ジオグラフィ関数の簡潔なリストと説明については「PostGIS ジオグラフィ対応関数」をご覧下さい。
|
4.3.4.1. |
球または回転楕円体のどちらで計算するのでしょうか? |
|
デフォルトでは、全ての距離と面積の計算が回転楕円体面上で行われます。局所域での計算結果が適切な投影法に基づく平面での結果とよく一致します。広い領域に渡る回転楕円体面での計算は、投影座標系の平面上の計算より精度が上がります。回転楕円体面は地球の曲率と非球面形状を考慮に入れているのに対して、平面計算は平らな表面を仮定している点が違います。 全てのジオグラフィ関数には、最後の真偽パラメータを'FALSE'にすると球面を使った計算を行うというオプションがあります。これは、特にジオメトリーが非常に単純である場合に計算を速くするためのものです。 |
|
|
4.3.4.2. |
日付変更線や極に関してはどうなっていますか? |
|
全ての計算に日付変更線や極の概念がありません。座標は球 (経度/緯度) であるので、日付変更線とクロスする形状は、計算の観点からは、他のものと変わりありません。 |
|
|
4.3.4.3. |
処理できる最も長い弧はどうなりますか? |
|
大圏の弧を 2点の「補完線」として使用しています。任意の 2点は、実際には 2方向につながっていて、どちらの方向に行くかに依存します。PostGIS の全てのコードは、大圏コースの 2コースのうち*短い*方でつながっていると仮定しています。結果として、180度以上の弧を持つ形状は正しくモデル化されません。 |
|
|
4.3.4.4. |
なぜヨーロッパやロシアといった大きな範囲の面積計算はとても遅いのですか? |
|
ポリゴンがとんでもなく大きいからです。二つの理由から、大きなエリアは悪いです。一つは、バウンダリボックスが大きいため、どのようなクエリを走らせても、インデックスがフィーチャーを引っ張ってくる傾向にあるためです。もう一つは、頂点数が巨大で、テスト (距離、包含) 関数では、少なくとも 1回、通常は N (N は、もう一方のフィーチャーの頂点数) 回、頂点を横断しなければならないためです。 ジオメトリーでは、大きなポリゴンを持っているけれども小さな範囲のクエリを実行する時、ジオメトリーデータ情報を小片に「非正規化」します。これにより、インデックスが効果的にオブジェクトの一部を問い合わせるようになり、またクエリが常にオブジェクト全体を引っ張りこむようなことがないようになります。ST_Subdivideを参照して下さい。ヨーロッパ全体を一つのポリゴンに*格納できる*からといって、*そうすべき*だというわけではありません。 |
PostGIS は Open Geospatial Consortium (OGC) の Simple Feature Specification に準拠しています。この標準では、単純なジオメトリーと妥当なジオメトリーの概念が定義されています。これらの定義によって、Simple Feature のジオメトリーモデルが一貫性があって、かつ明確な方法で空間オブジェクトを表現することができ、効率的な計算を助けます (OGC Simple Feature とSQL/MM とにおいては、単純性と妥当性について同じ定義です)。
単純なジオメトリーは、自己交差や自己接触といった異常な幾何学上のポイントを持たないジオメトリーです。
POINTは 0次元ジオメトリーオブジェクトとして常に単純です。
MULTIPOINTが単純になる条件は、任意の二つの座標 (POINT) が同じでないことです。
LINESTRINGは、同じポイントを二回通過しないものが単純です。単純なラインストリングの端点が同一の場合には、閉じているとされ、線形リングと呼ばれます。
線ジオメトリーを分類するには ST_IsSimple を使います。下に示すラベル付けした各結果は、関数が示された分類に合致すると判定したときだけ返されます。たとえば (c) は LINESTRING が単純かつ閉じているかの確認をします。
WITH cases(label, geom) AS (VALUES
('a', 'LINESTRING (10 190, 140 130, 70 80, 190 10)'::geometry),
('b', 'LINESTRING (10 190, 130 40, 170 160, 10 10)'::geometry),
('c', 'LINESTRING (90 190, 120 190, 130 140, 190 50, 70 10, 10 70, 10 150, 90 190)'::geometry),
('d', 'LINESTRING (90 190, 120 190, 50 60, 130 10, 190 50, 160 90, 10 150, 90 190)'::geometry)
)
SELECT
(SELECT geom FROM cases WHERE label = 'a' AND ST_IsSimple(geom))
AS "input_(a) simple",
(SELECT geom FROM cases WHERE label = 'b' AND NOT ST_IsSimple(geom))
AS "input_(b) not simple",
(SELECT geom FROM cases
WHERE label = 'c' AND ST_IsSimple(geom) AND ST_IsClosed(geom))
AS "input_(c) simple, closed",
(SELECT geom FROM cases WHERE label = 'd' AND NOT ST_IsSimple(geom))
AS "input_(d) not simple";
MULTILINESTRINGは、要素が全て単純で、かつ、全ての要素同士のインタセクションが要素の境界上でのみ出現する場合には、単純です。
次の例では、三つの MULTILINESTRING に同じ分類を適用します。(f) では、要素のラインが共有している端点だけで繋がり、(g) では内部でクロスします。
WITH cases(label, geom) AS (VALUES
('e', 'MULTILINESTRING ((30 190, 60 60, 170 10), (100 190, 180 150, 160 70))'::geometry),
('f', 'MULTILINESTRING ((30 190, 60 60, 170 10), (30 190, 180 150, 160 70))'::geometry),
('g', 'MULTILINESTRING ((30 190, 60 60, 170 10), (100 190, 180 150, 80 10))'::geometry)
)
SELECT
(SELECT geom FROM cases WHERE label = 'e' AND ST_IsSimple(geom))
AS "input_(e) simple",
(SELECT geom FROM cases WHERE label = 'f' AND ST_IsSimple(geom))
AS "input_(f) simple",
(SELECT geom FROM cases WHERE label = 'g' AND NOT ST_IsSimple(geom))
AS "input_(g) not simple";
POLYGONは線形リングから形成されるので、妥当なポリゴンジオメトリは常に単純です。
一般的に PostGIS 関数は引数ジオメトリーの単純性を求めていません。単純性は主にジオメトリの妥当性を定義するための基礎として用いられます。空間データモデルによっては要件としていることもあります (たとえば、線形ネットワークはしばしばクロスを認めません)。マルチポイントと線形ジオメトリーはST_UnaryUnionを使って単純にできます。
ジオメトリーの妥当性は主に 2次元ジオメトリー (POLYGONとMULTIPOLYGON) に適用されます。妥当性はポリゴンジオメトリーが平面領域を明確にモデル化できる規則によって定義されます。
POLYGONは次の条件では妥当です。
ポリゴン境界リング (外側の殻リングと内側の穴リング) が単純 (交差も自己接触もしていない) であること。これによりポリゴンは切断線、トゲ、循環を持つことができなくなります。これは、ポリゴンの穴を外側のリングの自己接触 (いわゆる "inverted hole" (逆穴)) でなく、内側のリングとして表現されなけれならないことを意味します。
境界リングがクロスしないこと
境界リングは点で接触したとしても接点として接触すること (線上にあってはなりません)
内側リングは外側リング内にあること
ポリゴン内部は単純に接続されていること (リングはポリゴンを複数に分割するように接触してはなりません)
ポリゴン境界リングの連続重複点があるとリングは単純でないと判定されません。SFS 曲線モデルでは、曲線は連続的にたどれる経路になっています。連続で同じ座標になることはトポロジー的に頂点が一つだけの場合と判別がつきません。そのため PostGIS は連続重複点を冗長な頂点として扱い、連続重複点があるだけでは不正と判別しないようにしています。
次の例では ST_IsValid を使って、妥当性規則を確認します。ラベルを付けたジオメトリーは、妥当性計算とラベルとが合っている場合にのみ返されます。
WITH cases(label, geom) AS (VALUES
('h', 'POLYGON ((10 140, 90 190, 130 170, 190 60, 160 10, 50 20, 10 140),
(50 100, 70 80, 110 100, 110 140, 50 100))'::geometry),
('i', 'POLYGON ((10 140, 90 190, 130 170, 190 60, 160 10, 50 20, 10 140),
(190 60, 140 40, 110 60, 120 90, 190 60))'::geometry)
)
SELECT
(SELECT geom FROM cases WHERE label = 'h' AND ST_IsValid(geom))
AS "input_(h) valid",
(SELECT geom FROM cases WHERE label = 'i' AND ST_IsValid(geom))
AS "input_(i) valid, tangent hole";
次の例の (j-m) は不正です。ジオメトリーはそれぞれ、内部での非接続、クロスするリング、ラインによる切断、外側リングの外にある穴です。評価理由は ST_IsValidReason で確認しています。(j) で内部非接続ポリゴンは、妥当な MULTIPOLYGON として表現できます。
WITH cases(label, geom, reason_pattern) AS (VALUES
('(j) disconnected interior',
'POLYGON ((10 140, 90 190, 130 170, 190 60, 160 10, 50 20, 10 140),
(130 170, 10 140, 50 120, 110 110, 130 170))'::geometry,
'Interior is disconnected%'),
('(k) crossing rings',
'POLYGON ((10 140, 90 190, 130 170, 190 60, 160 10, 50 20, 10 140),
(90 189, 10 139, 80 110, 110 130, 90 189))'::geometry,
'Self-intersection%'),
('(l) cut line',
'POLYGON ((10 100, 60 140, 60 190, 60 140, 130 170,
190 60, 160 10, 50 20, 10 100))'::geometry,
'Ring Self-intersection%'),
('(m) hole outside shell',
'POLYGON ((10 40, 10 100, 130 130, 190 80, 140 20, 50 10, 10 40),
(81 143, 103 143, 112 162, 107 175, 89 183, 71 169, 81 143))'::geometry,
'Hole lies outside shell%')
)
SELECT label, geom
FROM cases
WHERE NOT ST_IsValid(geom)
AND ST_IsValidReason(geom) LIKE reason_pattern
ORDER BY label;
label | geom -------------------------+---------------------------------------------------------------------------------------------------------------------------------------- (j) disconnected interior | POLYGON((10 140,90 190,130 170,190 60,160 10,50 20,10 140),(130 170,10 140,50 120,110 110,130 170)) (k) crossing rings | POLYGON((10 140,90 190,130 170,190 60,160 10,50 20,10 140),(90 189,10 139,80 110,110 130,90 189)) (l) cut line | POLYGON((10 100,60 140,60 190,60 140,130 170,190 60,160 10,50 20,10 100)) (m) hole outside shell | POLYGON((10 40,10 100,130 130,190 80,140 20,50 10,10 40),(81 143,103 143,112 162,107 175,89 183,71 169,81 143))
MULTIPOLYGONは次の条件では妥当です。
要素となるPOLYGONが妥当であること
要素がオーバラップしない (内部同士がインタセクトしない) こと
要素同士の接触が点でけである (線に沿って接触しない) こと
次の例では MULTIPOLYGON 規則を確認します。(n) でポリゴンは一点だけで接触しています。(o) でエッジを共有しています。(p) では内部が重なっています。
WITH cases(label, geom, valid, reason_pattern) AS (VALUES
('(n) point touch',
'MULTIPOLYGON (((10 40, 29 118, 100 75, 180 88, 140 30, 70 10, 10 40)),
((50 170, 100 190, 160 180, 180 140, 180 90, 120 120,
30 120, 50 170)))'::geometry,
true, NULL),
('(o) shared edge',
'MULTIPOLYGON (((10 40, 30 120, 120 120, 180 88, 140 30, 70 10, 10 40)),
((50 170, 100 190, 160 180, 180 140, 180 90, 120 120,
30 120, 50 170)))'::geometry,
false, 'Self-intersection%'),
('(p) overlap',
'MULTIPOLYGON (((10 40, 42 93, 130 150, 180 86, 140 30, 70 10, 10 40)),
((50 170, 100 190, 138 182, 149 156, 146 137, 100 100,
58 140, 50 170)))'::geometry,
false, 'Self-intersection%')
)
SELECT label, geom
FROM cases
WHERE ST_IsValid(geom) = valid
AND (valid OR ST_IsValidReason(geom) LIKE reason_pattern)
ORDER BY label;
label | geom ------------------+--------------------------------------------------------------------------------------------------------------------------------------------------------------------- (n) point touch | MULTIPOLYGON(((10 40,29 118,100 75,180 88,140 30,70 10,10 40)),((50 170,100 190,160 180,180 140,180 90,120 120,30 120,50 170))) (o) shared edge | MULTIPOLYGON(((10 40,30 120,120 120,180 88,140 30,70 10,10 40)),((50 170,100 190,160 180,180 140,180 90,120 120,30 120,50 170))) (p) overlap | MULTIPOLYGON(((10 40,42 93,130 150,180 86,140 30,70 10,10 40)),((50 170,100 190,138 182,149 156,146 137,100 100,58 140,50 170)))
これらの規則は妥当なポリゴンジオメトリーも単純であることも示しています。
線ジオメトリーについては、LINESTRINGが少なくとも二つのポイントを持ち、長さが 0でない (少なくとも二つの異なるポイントを持つことと同じ)、というのが唯一の妥当性規則です。単純でない (自己交差がある) ラインは妥当です。
SELECT
ST_IsValid('LINESTRING(0 0,1 1)') AS len_nonzero,
ST_IsValid('LINESTRING(0 0,0 0,0 0)') AS len_zero,
ST_IsValid('LINESTRING(10 10,150 150,180 50,20 130)') AS self_int;
len_nonzero | len_zero | self_int -------------+----------+---------- t | f | t
POINTとMULTIPOINTは妥当性規則を持っていません。
PostGIS は妥当なジオメトリーも不正なジオメトリーも、生成も格納もできます。このため、不正なジオメトリーを検出し、フラグを付け、訂正することができます。OGC 妥当性規則が求める規則 (長さが 0のラインストリングや逆穴を持つポリゴン等) よりも厳格であることもあります。
PostGIS が提供する関数の多くは、引数ジオメトリーが妥当であるとの仮定によっています。たとえば、ポリゴンの外部に穴があるポリゴンの面積を計算しても意味がありませんし、単純でない境界線からポリゴンを形成するのも意味がありません。妥当なジオメトリ入力を仮定することで、トポロジ的に正しいことを確認する必要がなくなるので、関数がより効率的に動作することができます (例外として、長さ 0のラインと反転したポリゴンは一般的に正しく取り扱われます)。また、ほとんどの PostGIS 関数は、入力ジオメトリーが妥当な場合には、妥当なジオメトリー出力を生成します。これにより、PostGIS 関数を安全に連鎖させられます。
PostGIS 関数を呼ぶときに予期しないエラーメッセージ ("GEOS Intersection() threw an error!"等) に遭遇する場合には、まず関数の引数が妥当かどうかを確認します。妥当でないなら、次に示す方法のいずれかによる、処理中のデータの妥当性の確認を検討して下さい。
|
|
|
関数が妥当な入力でエラーを報告する場合には、PostGIS または使用しているライブラリの一つの中にエラーがあるのを発見することがありますが、その際は PostGIS プロジェクトに報告して下さい。PostGIS 関数が妥当な入力から不正なジオメトリーを返す場合も同様です。 |
ジオメトリーが妥当かをテストするにはST_IsValid関数を使います。次のようにします。
SELECT ST_IsValid('POLYGON ((20 180,180 180,180 20,20 20,20 180))');
t
ジオメトリーの不正性の性質と位置に関する情報はST_IsValidDetail関数で得られます。次のようにします。
SELECT valid, reason,location AS location
FROM ST_IsValidDetail('POLYGON ((20 20,120 190,50 190,170 50,20 20))') AS t;
valid | reason | location -------+-------------------+--------------------------------------------- f | Self-intersection | POINT(91.51162790697674 141.56976744186048)
不正なジオメトリーを自動的に訂正することが望ましいような状況があります。その際はST_MakeValid関数を使います (ST_MakeValidは不正な入力を許す特別な関数です)。
複雑なジオメトリーの不正性テストには多大な CPU 時間を取ることになるため、デフォルトでは、ジオメトリーのロード時に PostGIS は妥当性の確認をしません。データソースが信用できない場合には、チェック制約を使って、テーブル上で妥当性を強制的に確認することができます。次のようにします。
ALTER TABLE mytable ADD CONSTRAINT geometry_valid_check CHECK (ST_IsValid(geom));
空間参照系 (Spatial Reference System, SRS) (座標参照系、Coordinate Reference System, CRS とも呼ばれます) は、ジオメトリーが地表上の位置をどのように参照するかを定義しています。SRS には次の通り三種あります。
測地 (geodetic) 空間参照系は、地表に直接対応付けられる極座標系 (経度と緯度) を使います。
投影 (projected) 空間参照系は、回転楕円体面を「平面にする」ための数学的な投影変換を使います。距離、面積、角度といった量を直接計測することが可能な位置座標系です。この座標系はデカルト座標系ですので、原点と二つの直交軸 (通常は来北と東方向) が定義されています。個々の投影座標系は、定まった距離単位 (通常はメートルかフィート) を使います。投影座標系は、歪みを避けて定義された座標範囲に納めるために、適応範囲を制限してもいいことになっています。
局所 (local) 座標系は、地表への参照がないデカルト座標系です。PostGIS では SRID 値を 0に指定します。
使用されている空間参照系には多数の相違点があります。一般的空間参照系は欧州石油調査グループ (European Petroleum Survey Group) のEPSG databaseで標準化されています。利便性向上のためPostGIS (と多くの空間系) は SRID と呼ぶ整数を使って空間参照系の定義を参照します。
ジオメトリーは、SRID 値で空間参照系に関連付けられています。SRID 値の取得にはST_SRIDを使います。ジオメトリーの SRID の設定にはST_SetSRIDを使います。ジオメトリー構築関数の中には、SRID を与えられるものもあります (ST_PointやST_MakeEnvelope等)。EWKT書式はSRID=n;を前置することで SRID に対応できます。
二つのジオメトリーを処理する空間関数 (オーバレイ関数 、関係関数など) の入力ジオメトリーは、同じ空間参照系である (同じ SRID を持つ) 必要があります。ST_TransformやST_TransformPipelineを使うことで、ジオメトリーデータを異なる空間参照系に変換できます。関数から返されるジオメトリーは、入力ジオメトリーと同じ空間参照系になります。
PostGIS が使用するSPATIAL_REF_SYSテーブルは利用可能な空間参照系を定義する OGC 準拠のデータベーステーブルです。このテーブルは、数値で SRID を持ち、文字列で座標系の記述を持っています。
spatial_ref_sysの定義は次の通りです。
CREATE TABLE spatial_ref_sys ( srid INTEGER NOT NULL PRIMARY KEY, auth_name VARCHAR(256), auth_srid INTEGER, srtext VARCHAR(2048), proj4text VARCHAR(2048) )
カラムは次の通りです。
srid
データベース内のSpatial Reference System (SRS, 空間参照系) で一意に識別される整数コードです。
auth_name
この参照系に引用されている標準の名前もしくは標準そのものです。たとえば「EPSG」は妥当なauth_nameです。
auth_srid
空間参照系の ID はauth_nameに引用される機関によって定義されます。ここが EPSG の場合には、これは EPSG コードです。
srtext
空間参照系の Well-Known テキスト表現です。たとえば、WKT SRS の表現は、次のようになります。
PROJCS["NAD83 / UTM Zone 10N",
GEOGCS["NAD83",
DATUM["North_American_Datum_1983",
SPHEROID["GRS 1980", 6378137, 298.257222101]
],
PRIMEM["Greenwich", 0],
UNIT["degree", 0.0174532925199433]
],
PROJECTION["Transverse_Mercator"],
PARAMETER["latitude_of_origin", 0],
PARAMETER["central_meridian", -123],
PARAMETER["scale_factor", 0.9996],
PARAMETER["false_easting", 500000],
PARAMETER["false_northing", 0],
UNIT["metre", 1]
]
SRS WKT の詳細については、OGC 標準のWell-known text representation of coordinate reference systemsをご覧下さい。
proj4text
PostGIS は座標変換機能を提供するために Proj4ライブラリを用いています。 proj4textカラムには、特定の SRID を示す Proj4座標定義文字列が入ります。たとえば次のようになります。
+proj=utm +zone=10 +ellps=clrk66 +datum=NAD27 +units=m
詳細情報についてはPROJ ウェブサイトをご覧下さい。spatial_ref_sys.sqlファイルには、全ての EPSG 投影について、srtextとproj4textの定義があります。
投影変換で空間参照系の定義を使用する場合には、次の戦略を取ります。
auth_nameとauth_sridがある (NULL でない) 場合には、これに基づいて PROJSRS を使います (存在する場合)。
srtextがある場合には、可能ならそれを使用して SRS を生成します。
proj4textがある場合には、可能ならこれを使用して SRS を生成します。
PostGISspatial_ref_sysテーブルにはPROJ投影ライブラリで処理される最も一般的な空間参照系定義 3000件以上があります。しかし、そこに無い多くの座標系があります。空間参照系に関する必要な情報がある場合は、SRS 定義をテーブルに追加できます。PROJ に詳しいなら独自の空間参照系を定義することもできます。ほとんどの空間参照系は地域的なものであり、目的の範囲外で使用する場合は意味を持たない点に注意してください。
PostGIS のコアセットに入っていない空間参照系を探すための素晴らしい資料がhttp://spatialreference.org/にあります。
一般的に使われる空間参照系には 4326 - WGS 84 地理座標系、4269 - NAD 83 地理座標系 (訳注: 日本では使いません)、3395 - WGS 84 メルカトル図法、2163 - 米国ナショナルアトラス正積図法 (訳注: 日本では使いません)、WGS84 UTM 60個のゾーンがあります。UTM ゾーンは計測に最も適していますが、対応領域は経度 6度幅です。WGS84 経度緯度データから UTM ゾーンは least(floor((longitude + 180) / 6) + 1, 60) (訳注: 北欧の一部ではズレがあります) となり、EPSG コードは、 北半球では 32600 + zone となり、南半球では 32700 + zone となります。
米国の州では、州平面空間参照系 (メートルまたはフィート単位) を使用します。この空間参照系は州ごとに一つか二つ存在します。ほとんどのメートル単位のものはコアのセットに存在しますが、フィート単位の多数のものや ESRI が作成したものはspatialreference.orgからロードする必要があります。
地球外の座標系でさえも定義することができます。たとえばMars 2000です。この火星の座標系は非平面 (回転楕円体の度) ですが、geography型で、度でなくメートル単位で長さや近接測定値を取得することができます。
割当外の SRID と PROJ 定義を使って米国中央のランベルト正角円錐図法の独自座標系をロードする例を次に示します。
INSERT INTO spatial_ref_sys (srid, proj4text) VALUES ( 990000, '+proj=lcc +lon_0=-95 +lat_0=25 +lat_1=25 +lat_2=25 +x_0=0 +y_0=0 +datum=WGS84 +units=m +no_defs' );
geometry型のカラムを付けたCREATE TABLE SQL ステートメントでジオメトリーデータを保存するテーブルを生成することができます。次の例では、BC-アルベルス座標系 (SRID 3005) の 2次元 (XY) ラインストリングを保存するジオメトリーカラムを持つテーブルを生成します。
CREATE TABLE roads ( id SERIAL PRIMARY KEY, name VARCHAR(64), geom geometry(LINESTRING, 3005) );
geometry型は、次の通り、二つの任意指定型修飾子に対応しています。
空間タイプ修飾子はカラムで許される形状と次元の種類を制約するものです。値は、対応しているジオメトリタイプ (POINT, LINESTRING, POLYGON, MULTIPOINT, MULTILINESTRING, MULTIPOLYGON, GEOMETRYCOLLECTION 等) なら全て可能です。空間タイプ修飾子は、後置詞 Z, M, ZM を付け加えることで座標次元の制約に対応します。例えば、`LINESTRINGM`修飾子では、3次元で 3番目が M 軸となるラインストリングだけを許します。同様に、'POINTZM'では 4次元 (XYZM) データが求められます。
SRID 修飾子は空間参照系の SRID を特定の数値に制約します。省略した場合には、デフォルトは 0となります。
ジオグラフィカラムを持つテーブルの生成の例を次に挙げます。
デフォルト SRID であらゆる種類のジオメトリーを保持するテーブルの生成:
CREATE TABLE geoms(gid serial PRIMARY KEY, geom geometry);
2次元ポイントで SRID がデフォルトのテーブル作成:
CREATE TABLE pts(gid serial PRIMARY KEY, geom geometry(POINT));
3次元 (XYZ) ポイントで SRID が 3005のテーブル作成:
CREATE TABLE pts(gid serial PRIMARY KEY, geom geometry(POINTZ, 3005));
4次元 (XYZM) ラインストリングで SRID がデフォルトのテーブル作成:
CREATE TABLE lines(gid serial PRIMARY KEY, geom geometry(LINESTRINGZM));
2次元ポリゴンで SRID が 4276 (NAD 1927地理座標系) のテーブル作成:
CREATE TABLE polys(gid serial PRIMARY KEY, geom geometry(POLYGON, 4267));
一つのテーブルが一つ以上のジオメトリーカラムを持つことができます。テーブル生成時に指定するか、ALTER TABLE SQL ステートメントを使って追加するかで実現できます。次に 3次元ラインストリングを格納するカラムを追加する例を示します。
ALTER TABLE roads ADD COLUMN geom2 geometry(LINESTRINGZ, 4326);
OGC Simple Features Specification for SQLは、ジオメトリーテーブル構造を記述するためのGEOMETRY_COLUMNSメタデータテーブルを定義しています。PostGIS ではgeometry_columnsは、データベースのシステムカタログテーブルから読み取るビューです。これによって、空間メタデータ情報が常に現在定義されているテーブルやビューと矛盾しなくなります。
\d geometry_columns
View "public.geometry_columns"
Column | Type | Modifiers
-------------------+------------------------+-----------
f_table_catalog | character varying(256) |
f_table_schema | character varying(256) |
f_table_name | character varying(256) |
f_geometry_column | character varying(256) |
coord_dimension | integer |
srid | integer |
type | character varying(30) |
カラムは次の通りです。
f_table_catalog, f_table_schema, f_table_name
ジオメトリーカラムを持っている地物テーブルの完全修飾名。PostgreSQL には"catalog"の類似カラムが無いので、このカラムは空白のままです。"schema"については PostgreSQL スキーマ名が使われます (デフォルトはpublicです)。
f_geometry_column
フィーチャーテーブル内のジオメトリーカラムの名前。
coord_dimension
カラムの座標次元 (2, 3, 4)。座標次元は、X, Y 座標があるので最低で 2です。
srid
このテーブルのジオメトリーの座標系として使用される座標系空間参照系の ID です。spatial_ref_sysテーブルを参照する外部キーです (「SPATIAL_REF_SYS テーブル」を参照して下さい)。
type
空間オブジェクトの型。空間カラムを単一型に制限するには、POINT、LINESTRING、POLYGON、MULTIPOINT、MULTILINESTRING、MULTIPOLYGON、GEOMETRYCOLLECTION のうちのいずれかを、また、XYM で使う場合には、LINESTRINGM、POLYGONM、MULTIPOINTM、MULTILINESTRINGM、MULTIPOLYGONM、GEOMETRYCOLLECTIONM のうちのいずれかを使います。複数の型が混合するコレクションの場合は"GEOMETRY"を型とすることができます。
これが必要になる事例に、SQL ビューとバルクインサートの二つがあります。バルクインサートの場合には、カラムに制約を与えるか、ALTER TABLE を実行することで、geometry_columns テーブル内の登録を訂正することができます。ビューの場合には、CAST 演算を使用します。カラムが型修飾子に基づく場合には、生成処理によって正しく登録されるので、何も行う必要がありません。ジオメトリーに適用する空間関数を持たないビューも、基礎となるテーブルのジオメトリーカラムと同じように登録されます。
例えば、次のようなビューを作ったとします。
CREATE VIEW public.vwmytablemercator AS SELECT gid, ST_Transform(geom, 3395) As geom, f_name FROM public.mytable;
ジオメトリーカラムを正しく登録するには、ST_Transform の結果ジオメトリーに対してキャストします。
DROP VIEW public.vwmytablemercator;
CREATE VIEW public.vwmytablemercator AS
SELECT gid, ST_Transform(geom, 3395)::geometry(Geometry, 3395) As geom, f_name
FROM public.mytable;
ジオメトリータイプが 2次元ポリゴンになると分かっているならキャストで指定できます。
DROP VIEW public.vwmytablemercator;
CREATE VIEW public.vwmytablemercator AS
SELECT gid, ST_Transform(geom, 3395)::geometry(Polygon, 3395) As geom, f_name
FROM public.mytable;
バルクインサートで派生テーブルを生成する場合には、先にテーブルを生成します。
SELECT poi.gid, poi.geom, citybounds.city_name INTO myschema.my_special_pois FROM poi INNER JOIN citybounds ON ST_Intersects(citybounds.geom, poi.geom);
新しいテーブル上での 2次元空間インデックスの生成。
CREATE INDEX idx_myschema_myspecialpois_geom_gist ON myschema.my_special_pois USING gist(geom);
ポイントが 3次元または M 値を持つ場合には、n 次元インデックスがより適切になりえます。
CREATE INDEX my_special_pois_geom_gist_nd ON my_special_pois USING gist(geom gist_geometry_ops_nd);
新しいテーブルのジオメトリーカラムを geometry_columns に手動登録します。これによって基礎となるテーブルの構造を変更して、カラムで型修飾子が使われるようになります。
SELECT Populate_Geometry_Columns('myschema.my_special_pois'::regclass);
子テーブル間で異なるジオメトリータイプや SRID を持つ継承テーブルのように、制約による定義を維持したい場合には、任意引数 use_typmod を FALSE にします。
SELECT Populate_Geometry_Columns('myschema.my_special_pois'::regclass, false);
Although the old-constraint based method is still supported, a constraint-based geometry column used directly in a view, will not register correctly in geometry_columns, as will a typmod one. In this example we define a column using typmod and another using constraints.
CREATE TABLE pois_ny(gid SERIAL PRIMARY KEY, poi_name text, cat text, geom geometry(POINT, 4326));
SELECT AddGeometryColumn('pois_ny', 'geom_2160', 2160, 'POINT', 2, false);
psql で \d pois_ny を使うと異なる定義になることが分かります。一方は型修飾子方式で、もう一方は制約方式です。
\d pois_ny
Table "public.pois_ny"
Column | Type | Modifiers
-----------+-----------------------+------------------------------------------------------
gid | integer | not null default nextval('pois_ny_gid_seq'::regclass)
poi_name | text |
cat | character varying(20) |
geom | geometry(Point,4326) |
geom_2160 | geometry |
Indexes:
"pois_ny_pkey" PRIMARY KEY, btree (gid)
Check constraints:
"enforce_dims_geom_2160" CHECK (st_ndims(geom_2160) = 2)
"enforce_geotype_geom_2160" CHECK (geometrytype(geom_2160) = 'POINT'::text
OR geom_2160 IS NULL)
"enforce_srid_geom_2160" CHECK (st_srid(geom_2160) = 2160)
geometry_columns では、両方とも正しく登録されています。
SELECT f_table_name, f_geometry_column, srid, type FROM geometry_columns WHERE f_table_name = 'pois_ny';
f_table_name | f_geometry_column | srid | type -------------+-------------------+------+------- pois_ny | geom | 4326 | POINT pois_ny | geom_2160 | 2160 | POINT
しかし、次のようにビューを作ろうとします。
CREATE VIEW vw_pois_ny_parks AS
SELECT *
FROM pois_ny
WHERE cat='park';
SELECT f_table_name, f_geometry_column, srid, type
FROM geometry_columns
WHERE f_table_name = 'vw_pois_ny_parks';
The typmod based geom view column registers correctly, but the constraint based one does not.
f_table_name | f_geometry_column | srid | type ------------------+-------------------+------+---------- vw_pois_ny_parks | geom | 4326 | POINT vw_pois_ny_parks | geom_2160 | 0 | GEOMETRY
This may change in future versions of PostGIS, but for now to force the constraint-based view column to register correctly, you need to do this:
DROP VIEW vw_pois_ny_parks;
CREATE VIEW vw_pois_ny_parks AS
SELECT gid, poi_name, cat,
geom,
geom_2160::geometry(POINT,2160) As geom_2160
FROM pois_ny
WHERE cat = 'park';
SELECT f_table_name, f_geometry_column, srid, type
FROM geometry_columns
WHERE f_table_name = 'vw_pois_ny_parks';
f_table_name | f_geometry_column | srid | type ------------------+-------------------+------+------- vw_pois_ny_parks | geom | 4326 | POINT vw_pois_ny_parks | geom_2160 | 2160 | POINT
空間テーブルを作成したら、これで GIS データをデータベースにアップロードする準備ができたことになります。現在、PostGIS/PostgreSQL データベースにデータをロードするには、SQL ステートメントを使う、またはシェープファイルのローダ/ダンパを使う、という二つの方法があります。
空間データを文字表現 (WKT か WKB) に変換できたら、SQL を使うのが PostGIS にデータを持たせる最も簡単です。SQL ユーティリティのpsqlを使用して、SQL のINSERTステートメントのテキストファイルをロードすると、データをPostGIS/PostgreSQL に一括読み込みできます。
データアップロードファイル (たとえばroads.sql) は次のようになるでしょう。
BEGIN; INSERT INTO roads (road_id, roads_geom, road_name) VALUES (1, 'LINESTRING(191232 243118,191108 243242)', 'Jeff Rd'); INSERT INTO roads (road_id, roads_geom, road_name) VALUES (2, 'LINESTRING(189141 244158,189265 244817)', 'Geordie Rd'); INSERT INTO roads (road_id, roads_geom, road_name) VALUES (3, 'LINESTRING(192783 228138,192612 229814)', 'Paul St'); INSERT INTO roads (road_id, roads_geom, road_name) VALUES (4, 'LINESTRING(189412 252431,189631 259122)', 'Graeme Ave'); INSERT INTO roads (road_id, roads_geom, road_name) VALUES (5, 'LINESTRING(190131 224148,190871 228134)', 'Phil Tce'); INSERT INTO roads (road_id, roads_geom, road_name) VALUES (6, 'LINESTRING(198231 263418,198213 268322)', 'Dave Cres'); COMMIT;
SQL ファイルの PostgreSQL へのロードはpsqlを使います。次のようにします。
psql -d [database] -f roads.sql
shp2pgsqlデータローダは、ESRI シェープファイルを PostGIS/PostgreSQL データベースに、ジオメトリーまたはジオグラフィとして挿入するための適切な SQL に変換します。ローダには、次に示すコマンドラインフラグによって区別される、いくつかの操作モードがあります。
入力は、固めていないシェープファイルのパスでなければなりません。ローダーは .zip アーカイブを直接読めません。ロード前に .shp、.shx、.dbf ファイルを展開します。
グラフィカルユーザーインタフェースを持つshp2pgsql-guiもあります。コマンドラインローダのオプションのほとんどが使えます。これは、スクリプト化されていない 1回限りのロードの場合や、PostGIS 初心者がロードする場合に、簡単に使用できます。PgAdminIII のプラグインとすることもできます。
-c
新しいテーブルの作成とシェープファイルからのデータの読み込みを行います。これがデフォルトモードです。
-a
シェープファイルからデータベーステーブルにデータを追加します。複数のファイルをロードするためにこのオプションを使う場合は、これらのファイルは同じ属性と同じデータ型を持つ必要があります。
-d
シェープファイルにあるデータを持つ新しいテーブルを作成する前にデータベーステーブルを削除します。
-p
テーブル作成の SQL コードを生成するだけで、実際のデータは追加しません。このモードは、テーブル作成とデータロードとを完全に分けたい場合に使用します。
-?
ヘルプ画面を表示します。
-D
出力データに PostgreSQL のダンプ書式を用います。このモードは-a, -c, -d と組み合わせて利用します。デフォルトの"insert"による SQL 書式よりも、大変早くロードできます。大きなデータセットではこちらを使用して下さい。
-f <fid_column>,
--feature-id-column <fid_column>
地物識別子のカラム名を指定します。デフォルトは gid です。値は単純な PostgreSQL 識別子でなければなりません。PostgreSQL システムカラム名は使えません。
-s [<FROM_SRID>:]<SRID>
指定した SRID を持つジオメトリーテーブルの生成や追加を行います。FROM_SRID が与えられた場合には、入力シェープファイルに、これを使います 。この場合には、ジオメトリは変更先 SRID に投影変換します。
-k
識別子 (カラム、スキーマおよび属性) の大文字小文字を保持します。シェープファイルの属性は全て大文字であることに注意して下さい。
-i
全ての整数を標準の 32ビット整数に強制します。DBF ヘッダではそれが正当であったとしても、64ビットの bigint を生成しません。
-I
ジオメトリーカラムに GiST インデックスを生成します。
-u
対象テーブルを UNLOGGED とします。これにより一時的な中間データのロード速度が上がりますが、PostgreSQL は異常終了や正常でないシャットダウンの後に UNLOGGED テーブルの内容を保持しません。
-m
長いカラム名を 10文字の DBF カラム名に対応付けるファイルを指定します。ファイルの内容は 1行以上の空白区切りの 2項目で、行頭行末に空白を入れません。例えば次のようになります:
COLUMNNAME DBFFIELD1
AVERYLONGCOLUMNNAME DBFFIELD2
-S
マルチ系ジオメトリーの替りに単一ジオメトリーを生成します。全てのジオメトリーが実際に単一である (たとえば単一の外環でなる MULTIPOLYGON や単一の頂点でなるMULTIPOINT) 場合にのみ成功します。
-t <dimensionality>
出力ジオメトリーが特定の次元を持つよう強制します。次元は、2D, 3DZ, 3DM, 4D の文字列を使います。
入力の次元が出力より小さい場合には、出力では 0が入ります。入力の次元が大きい場合には、外されます。
-w
出力書式を WKB でなく WKT にします。精度が低下して、座標変動が発生しうることに注意が必要です。
-e
トランザクションを使わずに、ステートメントごとに実行するようにします。エラーの元となる不良なジオメトリーがいくつか含んでいる時に、大半の良好なデータのロードが可能にするものです。ダンプ書式ではトランザクションを常に使うので、-D フラグを指定している場合には使えません。
-W <encoding>
入力データ (dbf ファイル) のエンコーディングを指定します。全ての dbf の属性は指定されたエンコーディングから UTF8に変換されます。SQL 出力結果には SET CLIENT_ENCODING to UTF8が含まれるようになり、バックエンドは UTF-8からデータベースが内部利用のために設定したエンコーディングに再変換できます。
-N <policy>
NULL ジオメトリー操作方針(insert*=挿入, skip=スキップ, abort=強制終了) を選択します。
-n
DBF ファイルのみインポートします。対応するシェープファイルを持っていない場合、 自動的にこのモードになり、DBF ファイルのみロードします。 このフラグは、完全なシェープファイル群を持っていて、属性データだけが欲しくてジオメトリーが欲しくない時のみ使用します。
-G
ジオメトリー型のかわりに、ジオグラフィ型で、WGS84経度緯度 (SRID=4326) を使用します (経度緯度データが必要です)。
-T <tablespace>
新しいテーブルのテーブル空間を指定します。 -X パラメータが使われない場合には、インデックスはデフォルトのテーブル空間を使用します。PostgreSQL 文書には、テーブル空間を用いるべき時に関する良い文書があります。
-X <tablespace>
新しいテーブルのインデックスで使われるテーブル空間を指定します。 主キーインデックスに適用され、-I が合わせて使われている場合には GiST 空間インデックスにも適用されます。
-Z
このフラグをこれを使う時、ANALYZE 手続きの生成を防ぎます。-Z フラグが無い (デフォルトの振舞い) 場合には、ANALYZE 手続きが生成されます。
ローダを使って入力ファイルを生成してアップロードするセッション例は次の通りです。
shp2pgsql -c -D -s 4269 -i -I shaperoads.shp myschema.roadstable > roads.sql psql -d roadsdb -f roads.sql
変換とアップロードは UNIX のパイプを使うと一回で実行できます。
shp2pgsql shaperoads.shp myschema.roadstable | psql -d roadsdb
シェープファイルのディレクトリーをロードするのに、ローダーをファイルごとに実行して、ファイル名からテーブル名を導きます。Windows では次のようにします。 cmd.exe 上ではパーセント記号は一つで、バッチファイルでは二つです:
REM In a .bat file, use %%F. At an interactive cmd.exe prompt, use %F.
for %%F in ("C:\data\shapefiles\*.shp") do (
shp2pgsql -d -D -s 4269 -I -W UTF-8 "%%~fF" "myschema.%%~nF" | psql -d roadsdb
)
全てのシェープファイルが同じ属性とデータ型を持っていて、これらを一つのテーブルに追加したいとき、最初のファイルを作り、残りのファイルは -a を使います。
空間データは SQL かシェープファイルダンパを使うと抽出できます。SQL の節では空間テーブルで比較とクエリに使用できる関数を示します。
データベース外へのデータ抽出の最も簡単な方法は、抽出するデータセットを定義し、SELECT問い合わせを使って、結果カラムを解析可能なテキストファイルにダンプすることです。
SELECT road_id,road_geom AS geom, road_name FROM roads;
road_id | geom | road_name
--------+-----------------------------------------+-----------
1 | LINESTRING(191232 243118,191108 243242) | Jeff Rd
2 | LINESTRING(189141 244158,189265 244817) | Geordie Rd
3 | LINESTRING(192783 228138,192612 229814) | Paul St
4 | LINESTRING(189412 252431,189631 259122) | Graeme Ave
5 | LINESTRING(190131 224148,190871 228134) | Phil Tce
6 | LINESTRING(198231 263418,198213 268322) | Dave Cres
7 | LINESTRING(218421 284121,224123 241231) | Chris Way
(6 rows)
返されるレコードの数を減らすためにある種の制限が必要になる場合があります。属性ベースで制限をかける場合には、非空間テーブルで使うのと同じ SQL 文を使います。空間に制限をかけるには次の関数を使います。
ST_Intersects
この関数は、二つのジオメトリーが空間を共有しているかどうかをテストします。
=
この関数で、二つのジオメトリーが幾何的に同一であるかを見ることができます。たとえば、'POLYGON((0 0,1 1,1 0,0 0))' は 'POLYGON((0 0,1 1,1 0,0 0))' と同じかを見ることができます (これは同じとなります)。
次に、これらの演算子をクエリで使うことができます。SQL コマンドラインからジオメトリとボックスの指定を行うときは、明示的に文字列表現をジオメトリーに変換しなければならないことに注意して下さい。たとえば、次のようになります。ただし 312は架空の空間参照系番号で、ここでのデータに合致しています。
SELECT road_id, road_name FROM roads WHERE roads_geom='SRID=312;LINESTRING(191232 243118,191108 243242)'::geometry;
上のクエリは"ROADS_GEOM"テーブルから、その値と等価である単一のレコードを返します。
道路がポリゴンで定義した面を通過するかどうかをチェックするには次のようにします。
SELECT road_id, road_name FROM roads WHERE ST_Intersects(roads_geom, 'SRID=312;POLYGON((...))');
最も一般的な空間クエリは「フレームベース」のクエリでしょう。これは、表示するためのデータの価値のある「マップフレーム」を取得するために、データブラウザやウェブマッパのようなクライアントソフトウェアに使われます。
"&&"演算子を使うとき、比較フィーチャーを BOX3D か GEOMETRY かに指定することができます。ただし、GEOMETRY を指定すると、それのバウンディングボックスが比較に使われます。
次に示すクエリのように、フレームに BOX3D オブジェクトを使います。
SELECT roads_geom AS geom FROM roads WHERE roads_geom && ST_MakeEnvelope(191232, 243117,191232, 243119,312);
エンベロープの投影を指定するために SRID 312を使っていることに注意して下さい。
pgsql2shpテーブルダンパは、データベースに直接接続して、テーブル (あるいはクエリによって定義されたもの) をシェープファイルに変換するものです。基本的な文は次の通りです。
pgsql2shp [<options >] <database > [<schema >.]<table>
pgsql2shp [<options >] <database > <query>
コマンドラインオプションは次の通りです。
-f <filename>
特定のファイル名に出力を書きこみます。
-h <host>
接続先データベースのホスト名。
-p <port>
接続先データベースのポート。
-P <password>
データベースに接続するためのパスワード。
-u <user>
データベースに接続する際のユーザー名。
-g <geometry column>
複数のジオメトリーカラムを持つテーブルの場合の、シェープファイルの出力に使用するジオメトリーカラム。
-b
バイナリカーソルを使います。これは、実行時間を短くしますが、テーブルの非ジオメトリ属性がテキストへのキャストを持っていない場合には、動作しません。
-r
Raw モード。gidフィールドを落としたり、カラム名をエスケープしてはいけません。
-m filename
識別名を 10文字名に再割り当てします。 ファイルの中身は、一つの空白で区切られ、前と後に空白が無い二つのシンボルの行からなります。VERYLONGSYMBOL SHORTONE ANOTHERVERYLONGSYMBOL SHORTER 等となります。
インデックスによって巨大データセットの空間データベースの使用が可能となります。インデックス無しでは、地物の検索を行う際に、データベースの全てのレコードに対するシーケンシャルスキャンが必要となります。インデックスによって、レコード探索のために早く移動できる構造を構築するので、検索速度が向上します。
一般的に属性データに使われるインデックス手法であり B 木は、空間データではあまり有用ではありません。1次元データの格納とクエリにだけしか対応していないためです。ジオメトリーのような 2次元以上の次元を持つデータでは、全ての次元の範囲を指定できるインデックス手法が求められます。PostgreSQL の空間データ処理に関する主要な利点の一つに、多次元データで上手く動作する GiST、BRIN、SP-GiST の複数のインデックス手法を提供していることです。
GiST (Generalized Search Tree)インデックスは、データを「一方にあるもの」「オーバラップするもの」「内部にあるもの」に分解するもので、GIS データを含む幅広い範囲で使えます。PostGIS は GiST インデックス空間データを R 木インデックス実装のベースにています。GiST は最も一般的に使われ、多目的なインデックス手法で、非常に良好な問い合わせ効率を提供しています。
BRIN (Block Range Index)インデックスは、空間範囲を集計することで動作します。探索は範囲のスキャンを通して行われます。BRIN は一部の種類 (空間的にソートされ、更新がほぼ無いか全く無い) のデータだけに適切です。しかし、インデックス生成時間は非所に早く、インデックスサイズは非常に小さくなります。
SP-GiST (Space-Partitioned Generalized Search Tree)は 4分木、kd 木、基数木 (トライ木) のような部分木探索に対応する一般的なインデックス手法です。
空間インデックスはジオメトリーのバウンディングボックスだけを格納します。空間クエリはインデックスは初期フィルタとして使用して、クエリ条件に一致する可能性のあるジオメトリーを早く求めます。ほとんどの空間クエリでは、空間述語関数を使って特定の空間条件をテストする二次フィルタが必要です。空間述語関数を使ったクエリの詳細情報については「空間インデックスを使う」をご覧下さい。
また、PostGIS Workshop section on spatial indexesとPostgreSQL manualもご覧下さい。
GiST は「汎用検索木 (Generalized Search Tree)」の意味で、多次元データのインデックスの一般化された形式です。PostGIS は GiST 上で実装している R 木インデックスをを空間データのインデックスに使用しています。GiST は最も一般的に使われ、多目的なインデックス手法で、クエリ能率を非常に良くします。他の GiST の実装は、通常の B 木インデックスに従わない全ての種類の不規則なデータ構造 (整数配列, スペクトラルデータ等) の検索速度を向上させるために使います。詳細情報についてはPostgreSQL manualをご覧ください。
GIS データテーブルが数千行を超えたら、空間検索の速度向上のためインデックスを構築したくなるでしょう (これは属性検索でない場合です。属性でしたら通常のインデックスを属性フィールドに追加します)。
GiST インデックスをジオメトリーカラムに追加するための文は次の通りです。
CREATE INDEX [indexname] ON [tablename] USING GIST ( [geometryfield]);
上の文では常に 2次元インデックスを構築します。n 次元インデックスをジオメトリー型で使うには、次の文でインデックスを生成できます。
CREATE INDEX [indexname] ON [tablename] USING GIST ([geometryfield] gist_geometry_ops_nd);
空間インデックスの構築は、計算量を集中させて行われます。また、この時には、テーブルへの書き込みアクセスがブロックされます。そのため、本番システムではより遅い CONCURRENTLY を選択するかも知れません。次のようにします。
CREATE INDEX CONCURRENTLY [indexname] ON [tablename] USING GIST ( [geometryfield]);
インデックス構築後に、時々 PostgreSQL にテーブルの統計情報を集めさせると助かります。クエリプランの最適化に使われます。
VACUUM ANALYZE [table_name] [(column_name)];
BRIN は"Block Range Index"の意味です。PostgreSQL が提供する汎用インデックスです。BRIN は不可逆インデックスですので、レコードが検索条件に合致するか確認する二次チェックが必要です (これは提供されているすべての空間インデックスと同じです)。BRIN は、インデックス生成が非常に高速で、インデックスサイズが非常に小さく、読込み効率がそこそこ良いものです。これの主目的は、巨大テーブルに対してテーブル内の物理位置と相関するカラムの上にインデックスを生成することです。BRIN は各種属性データ構造 (整数、配列など) の高速検索が可能です。詳細情報についてはPostgreSQL manualを参照してください。
空間テーブルが、ひとたび数千行を超えると、データの空間検索の速度向上にインデックスが必要と感じることになります。GiST インデックスは、サイズがデータベースで使える RAM 容量を超えず、インデックスのストレージサイズに余裕があり、書き込み時のインデックス更新コストにも余裕があるなら、非常に高いパフォーマンスを発揮します。そうでない場合には、非常に大きなテーブルにおいては、BRIN インデックスを代替に考えることができます。
BRIN インデックスは、連続するテーブルブロックの集合 (ブロック範囲と言います) の全てのジオメトリーを囲むバウンディングボックスを格納します。インデックスを使用した問い合わせを実行する時に、問い合わせ範囲とインタセクトするブロック範囲を見つけるためにスキャンします。これは、データが物理的に整列していて、ブロック範囲のバウンディングボックスのオーバラップが最小である (理想的には相互に排他的である) 場合に限って効率的です。結果インデックスは非常に小さいサイズですが、通常、読み込み効率は、同じデータにおける GiST インデックスより悪くなります。
BRIN インデックスの構築は、は GiST インデックスと比べて、CPU 集中を非常に減らします。BRIN インデックスは GiST インデックスよりも、同じデータに対して 10倍速く構築するのが普通です。BRIN インデックスはテーブルブロックの範囲ごとに一つのバウンディングボックスしか格納しないので、GiST インデックスと比べて、ディスクスペースを 1000倍少なくできます。
レンジ内で要約するブロック数を選択できます。この数字を減らすと、インデックスは大きくなりますが、効率向上の助けになる可能性があります。
BRIN を効果的にするには、テーブルデータをブロック範囲のオーバラップの量を最小にするような物理的オーダーで格納します。データが既に適切に並び替えられているかも知れません (たとえば、既に空間オーダーで並び替えられているデータセットを他のデータベースからロードする場合)。そうでない場合には、一つの空間キーによるデータの並べ替えで実現できます。一つの方法として、ジオメトリー値で並べ替えた新しいテーブルを生成することです (最近の PostGIS のバージョンで効果的なヒルベルト曲線オーダーが使われています)。
CREATE TABLE table_sorted AS SELECT * FROM table ORDER BY geom;
もしくは、データは、ジオハッシュを (一時的な) インデックスに使い、そのインデックスでクラスタリングを行うことによって適切に並べ替えることができます。
CREATE INDEX idx_temp_geohash ON table
USING btree (ST_GeoHash(ST_Transform(geom, 4326 ), 20));
CLUSTER table USING idx_temp_geohash;
BRIN インデックスをジオメトリーカラムに追加するための文は次の通りです。
CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geome_col]);
上の文で 2次元インデックスを構築します。3次元インデックスをビルドするには、この文を使います。
CREATE INDEX [indexname] ON [tablename] USING BRIN ([geome_col] brin_geometry_inclusion_ops_3d);
また、4次元演算子クラスを使う 4次元インデックスを使うこともできます。
CREATE INDEX [indexname] ON [tablename] USING BRIN ([geome_col] brin_geometry_inclusion_ops_4d);
上記のコマンドでは、範囲のブロック数はデフォルトの 128を使用しています。集計で範囲のブロック数を指定するには、この文を使います。
CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geome_col] ) WITH (pages_per_range = [number]);
また、BRIN インデックスは、多数の行で一つのインデックス値を格納することを心に留めておいて下さい。テーブルに違う次元のジオメトリーを格納する場合には、インデックスの効率が悪くなります。この効率欠落を回避するには、格納したジオメトリーの次元数の最小値となる演算子クラスを選択します。
geography型も BRIN インデックスに対応しています。BRIN インデックスをジオグラフィカラムに構築するための文は次の通りです:
CREATE INDEX [indexname] ON [tablename] USING BRIN ( [geog_col]);
上の文では常に回転楕円体面上の地理空間オブジェクトの 2次元インデックスを構築します。
現在のところは「包括対応」だけをここで考えています。これは、&&, ~, @の演算子だけが 2次元で使われることを意味します (ジオメトリーとジオグラフィの両方)。 &&&演算子は 3次元ジオメトリで使えます。しばらくは KNN 検索に対応しません。
BRIN と他のインデックスとの重要な違いは、データベースがインデックスを動的に保守しないことです。テーブルの空間データを変更すると、単純にインデックスの末尾に追加しています。このためインデックス探索の能率が時間とともに低下します。インデックスはVACUUMか空間関数brin_summarize_new_values(regclass)を実行することで更新できます。このため、BRIN は読み込み専用か、書き込みがほとんど発生しないよなデータでの利用では最も適切になりえます。詳細情報については、manualをご覧下さい。
空間データに BRIN を使用して集計するには:
インデックス構築時間は非常に速く、インデックスサイズは非常に小さいです。
インデックスのクエリ時間は GiST より遅いですが、十分許容できます。
テーブルデータを空間順序で並べ替える必要があります。
手動でインデックスの保守をする必要があります。
巨大なテーブルであって、オーバラップが少ないか無く (ポイントなど)、かつ静的か頻繁には変更しないようなものに、最も適しています。
比較的多数のデータレコードを返すクエリでの使用が、より効果的です。
SP-GiST は、「空間分割された一般探索木」を表します。四分木、k 次元木、基数木 (トライ木) のような分割探索木に対応するインデックスの総称的な形式です。このデータ構造の一般的な機能は、検索空間を反復して分割することですが、分割は等しいサイズである必要はありません。SP-GiST は、GIS インデックスだけでなく、電話回線のルーティングや、IP ルーティング、部分文字列検索等といった、様々な種類のデータを探索する速度の向上に使われます。詳細情報についてはPostgreSQL manualをご覧下さい。
GiST インデックスを利用しているので、空間オブジェクトを覆うバウンディングボックスを保存するという意味で、SP-GiST インデックスは不可逆です。SP-GiST インデックスは、GiST インデックスの代替と考えることができます。
一度 GIS データテーブルが数千行を超えると、データの空間探索の速度向上に SP-GiSTインデックスを使うと良いかも知れません。「ジオメトリー」カラムに SP-GiST インデックスを構築するための文は次の通りです。
CREATE INDEX [indexname] ON [tablename] USING SPGIST ( [geometryfield]);
上の文では、2次元インデックスを構築します。ジオメトリー型の 3次元インデックスは、次のように、3次元演算子クラスを使用して生成します。
CREATE INDEX [indexname] ON [tablename] USING SPGIST ([geometryfield] spgist_geometry_ops_3d);
空間インデックスの構築は、計算量を集中させて行われます。また、この時には、テーブルへの書き込みアクセスがブロックされます。そのため、本番システムでは、より遅い CONCURRENTLY を選択するかも知れません。次のようにします。
CREATE INDEX CONCURRENTLY [indexname] ON [tablename] USING SPGIST ( [geometryfield]);
インデックス構築後に、時々 PostgreSQL にテーブルの統計情報を集めさせると助かります。クエリプランの最適化に使われます。
VACUUM ANALYZE [table_name] [(column_name)];
SP-GiST インデックスは次の演算子を含むクエリの実行速度を向上させられます。
2次元インデックスについては <<, &<, &>, >>, <<|, &<|, |&>, |>>, &&, @>, <@, ~=です。
3次元インデックスについては &/&, ~==, @>>, and <<@ 。
現時点では kNN 探索に対応していません。
通常、インデックスは知らないうちにデータアクセスの速度を向上します。ひとたびインデックスを構築すれば、PostgreSQL クエリプランナは自動的にクエリの能率を向上させるために使うべきかどうかを決定します。しかし、プランナが既存のインデックスを選択せず、遅いシーケンシャルスキャンを使い続ける場合があります。
空間インデックスが使われていないのが分かった場合には、少しの行えることがあります。
クエリプランの試験とクエリの確認で、必要なものを計算できます。誤った JOIN や忘れ去られたテーブルや間違ったテーブルでは、予期しないテーブルレコード検索が複数回行われることがありえます。クエリプランを得るにはクエリの先頭にEXPLAINを付けて実行します。
テーブル内の値の数量と分布に関する統計情報を収集するとともに、クエリプランナにインデックス使用にかかる意思決定のための、より良い情報を与えるようにします。VACUUM ANALYZEは両方を計算します。
データベースに対する定期的な vacuum は常に実行するべきです。多くの PostgreSQLデータベースエージェントは、閑散時の cron ジョブとして定期的にVACUUMを実行します。
VACUUM が役に立たない場合には、SET ENABLE_SEQSCAN TO OFF;コマンドを使用して、一時的にプランナにインデックス情報の使用を強制することができます。この方法で、プランナがインデックス使用を多くしたクエリプランを生成できるかどうかを確認できます。このコマンドはデバッグにのみ使用してください。一般的に言えば、プランナはインデックスを使用するタイミングをよく知っています。クエリを実行したらSET ENABLE_SEQSCAN TO ON;を実行して、他のクエリでは通常操作にすることを忘れないでください。
SET ENABLE_SEQSCAN TO OFF;でクエリ速度が向上する場合には、PostgreSQL のハードウェア関連のチューンが行われていないのかも知れません。プランナがシーケンシャル対インデックスのコストが誤っている場合には、RANDOM_PAGE_COST (postgresql.conf内にあります) の値を変更してみて下さい。またはSET RANDOM_PAGE_COST TO 1.1;とします。RANDOM_PAGE_COSTのデフォルト値は 4.0です。1.1 (SSD の場合) または 2.0 (高速磁気ディスクの場合) を試してみて下さい。値を小さくするほど、プランナがインデックススキャンをしやすくなります。
SET ENABLE_SEQSCAN TO OFF;がクエリの助けにならないなら、クエリは PostgreSQL プランナがまだ最適化できない SQL 構成なのかも知れません。プランナが処理できるようにクエリを再記述できるかもしれません。例えば、インラインSELECT を持つ副問い合わせがあると、効果的なプランを作らないことがあり、LATERAL JOIN を使うように書き換えることができます。
詳細情報については PostgreSQL マニュアルの問い合わせ計画節をご覧下さい。