PostGIS トポロジ型と関数は、フェイス、エッジ、ノード等のトロポジオブジェクトを管理するために使います。
PostGIS Day Paris 2011における Sandro Santilli さんの講演が、PostGIS トポロジの概略説明として良いです。Topology with PostGIS 2.0 slide deckにあります。
Vincent Picavet さんはトポロジとは何か、どのように使われるか、および、対応するFOSS4G ツールに関する良い概略説明を PostGIS Topology PGConf EU 2012で出しています。
トポロジーベースの GIS データベースの例にUS Census Topologically Integrated Geographic Encoding and Referencing System (TIGER)データベースがあります。PostGIS トポロジーの実験でデータが必要となる場合に使って下さい。
PostGIS トポロジーモジュールは長い間存在していましたが、公式ドキュメントには常に存在しているわけではありませんでした。広範なクリーンアップで非推奨関数を削除し、既知の使い勝手に関する問題の修正、機能と関数の文書化、新機能追加、SQL-MM 適合性の改善がなされました。
本章では、リミティブテーブル、TopoGeometry タイプ、トポロジー管理関数、関数の編集、関数の評価、変換補助などのトポロジーモジュールの参照を維持しています。
このモジュールに関する全ての関数とテーブルは、topologyスキーマにインストールされます。
SQL/MM 標準で定義される関数は ST_ プリフィクスを持ち、PostGIS 特有の関数はこのプリフィクスを持ちません。
トポロジー機能はデフォルトでビルドされます。2章PostGIS インストールで説明されている通り、ビルド時のコンフィギュアオプション --without-topology を指定することで、無効にできます。
この例では、ノードを与えたラインからトポロジーのフェイスを構築し、結果フェイスにポイント属性を関連付けます。トポロジーに投入するために ST_CreateTopoGeo を使い、結果の確認に ValidateTopology を使い、各属性が指すフェイスの特定に GetFaceByPoint を使い、通常のポリゴンジオメトリーを作るのに ST_GetFaceGeometry を使います。
からトポロジーを生成し、二つのフェイスをラインを取り囲むラインを投入します:
CREATE EXTENSION IF NOT EXISTS postgis_topology;
SELECT topology.CreateTopology('topo_example', 3857);
WITH lines(geom) AS (VALUES
('SRID=3857;LINESTRING(0 0, 2 0)'::geometry),
('SRID=3857;LINESTRING(0 1, 2 1)'::geometry),
('SRID=3857;LINESTRING(0 0, 0 1)'::geometry),
('SRID=3857;LINESTRING(1 0, 1 1)'::geometry),
('SRID=3857;LINESTRING(2 0, 2 1)'::geometry)
)
SELECT topology.ST_CreateTopoGeo(
'topo_example',
ST_Collect(geom)
)
FROM lines;
SELECT * FROM topology.ValidateTopology('topo_example');
ValidateTopology からの返り値が空の場合には、トポロジーのエラーがありません。属性のポイントはフェイスに合致させることができます。ID が 0 のフェイスはユニバーサルフェイスで、ポイントが全てのフェイスの外側にあります。
WITH labels(name, geom) AS (VALUES
('west', 'SRID=3857;POINT(0.5 0.5)'::geometry),
('east', 'SRID=3857;POINT(1.5 0.5)'::geometry),
('outside', 'SRID=3857;POINT(3 0.5)'::geometry)
),
located AS (
SELECT name,
topology.GetFaceByPoint('topo_example', geom, 0) AS face_id
FROM labels
)
SELECT name,
face_id,
CASE WHEN face_id = 0 THEN NULL
ELSE topology.ST_GetFaceGeometry('topo_example', face_id)
END AS geom
FROM located
ORDER BY name;
属性をフェイスのテーブルに複製する前に、ポイントがフェイスの外側に無いかを確認し、同じフェイスに複数のポイントが割り当てられないかを確認します。
WITH labels(name, geom) AS (VALUES
('west', 'SRID=3857;POINT(0.5 0.5)'::geometry),
('east', 'SRID=3857;POINT(1.5 0.5)'::geometry),
('outside', 'SRID=3857;POINT(3 0.5)'::geometry)
),
located AS (
SELECT name,
topology.GetFaceByPoint('topo_example', geom, 0) AS face_id
FROM labels
)
SELECT 'outside face' AS problem,
array_agg(name ORDER BY name) AS labels
FROM located
WHERE face_id = 0
HAVING count(*) > 0
UNION ALL
SELECT 'duplicate face ' || face_id,
array_agg(name ORDER BY name)
FROM located
WHERE face_id != 0
GROUP BY face_id
HAVING count(*) > 1;
例で使ったトポロジーが不要であって削除するには DropTopology を使います:
SELECT topology.DropTopology('topo_example');
あらゆるトポロジーの中核的なプリミティブはedge_data、 node、faceテーブルに格納されます。これらのテーブルはCreateTopologyで生成されたスキーマ内にあります。edge_dataの各タプルは有向エッジを表現しています。start_nodeからend_nodeへの有向曲線と、その曲線の進行方向左側にあるフェイスの識別番号 (left_face) と右側のもの (right_face) とを併せて記録しています。このため、ジオメトリー線分が 2個のフェイスに属するとき、同じ線分が 2度 (方向ごとに 1度ずつ) 出現することがあります。
next_left_edgeカラムとnext_right_edgeカラムはフェイスを辿る方法をエンコードしてこの方向情報を完全にしています。正負符号付整数を格納していて、絶対値は進行方向を向いた時の次のエッジを示し、正負符号で次のエッジが順方向化逆方向かを決定しています。正式には、次の規則があります (エッジを eとします):
abs(next_left_edge)はeの左にあるフェイスを辿る場合に次に到達するエッジの識別番号です。値が正数の場合にはeの終端ノードから次のエッジの格納されている通りの方向に進みます。負数の場合には次のエッジは逆向きに辿ることになるので、共有するフェイスは辿っている者から見たら左側にあります。
abs(next_right_edge)はeの右にあるフェイスを辿る場合に次に到達するエッジの識別番号です。値が正数の場合には次のエッジがeの終端ノードから始まり次のエッジの格納されている通りの方向に進みます。負数の場合には次のエッジは逆向きにたどることになるので、共有するフェイスは辿っている者から見たら左側にあります。負数の場合には次のエッジを逆方向で辿る、すなわちエッジの終端から始めることになり、右側のフェイスが保存されます。
ゼロは対応する側でエッジがぶら下がっていることを示します (たとえば接続するフェイスが 0のユニバーサルフェイスになっている孤立エッジ)。 edgeビューにあるabs_next_left_edgeカラムとabs_next_right_edgeカラムは絶対値にして扱いすくしたものです。
この表現は二重連結辺リストの異種で、多数のトポロジールーチンで利用されます。 GetRingEdges や ValidateTopology といった関数によるフェイス境界の再構築や矛盾の診断に使われます。したがって評価時に「不正な next_left_edge」と「不正な next_right_edge」診断が報告されます。 AddEdge のようなコンストラクターは next_* 属性を普通の自己参照で初期化します。ST_AddEdgeModFace と ST_RemEdgeModFace を含む編集ルーチンは、エッジが挿入されるか削除されると同時にリンクを更新します。他の大量処理 (たとえば Polygonize) は意図的にカラムに値を設定しないことがあり、文書では振舞いに関する注意を明示的に出しています。
本節では、PostGIS トポロジでインストールされる PostgreSQL データ型の一覧を挙げます。独自に関数をデザインする際に特に重要となる、キャストでの挙動を記述していることにご注意ください。
ValidateTopologyが返す型です。
本節では、PostGIS トポロジでインストールされる PostgreSQL ドメインの一覧を挙げます。ドメインは、オブジェクト型のように扱え、関数やテーブルカラムのオブジェクトを返します。ドメインは存在するチェック制約を持つ既存の型である点で、型とは違います。
本節では、新しいトポロジスキーマの構築、トポロジの評価、TopoGeometry カラムの管理のためのトポロジ関数の一覧を挙げます。
schema_nameで指定されたスキーマ内にあるtable_nameで指定されたテーブルから TopoGeometry カラムを削除し、topology.layer テーブルにある登録を解除します。
本節では、トポロジ構築時のデータベース統計の管理について説明します。
トポロジに要素を追加すると、そのトリガとして、分割されることになる既存のエッジを探索し、ノードを追加し、新しいラインでノードを作成するエッジを更新するために多数のデータベースクエリが発生します。このため、トポロジテーブル内のデータに関する統計情報が最新の状態になっているなら、統計情報を使うと便利です。
PostGIS トポロジーの追加や編集の関数は、自動的に統計情報を更新することはありません。トポロジにおいて逐次変更していては、統計情報の更新が過剰になるためです。処理は呼び出し元の義務となっています。
|
|
|
autovacuum で更新された統計情報は、autovacuum プロセス完了前に始まったトランザクションからは見えないので、更新した統計情報を使うには、実行時間の長いトランザクションでは ANALYZE 自体を実行する必要があります。 |
本節では、新しいトポロジを生成するトポロジ関数を挙げます。
本節では、エッジ、フェイス、ノードの追加、移動、削除、分割に関する関数を挙げます。本節の関数はすべて ISO SQL/MM で定義されています。
anodeとanothernodeで指定される二つの既存孤立ノードを接続するトポロジに、ジオメトリーalinestringで定義される孤立エッジを追加し、新しいエッジの識別番号を返します。
apointジオメトリーがノードとして存在しているなら、エラーが投げられます。移動に関する説明を返します。
afaceの境界となる、整列したエッジの集合を返します。
本節では、非標準の手法でのトポロジ処理の関数を挙げます。
本節では、新しい TopoGeometry を生成するトポロジ関数を挙げます。
topoelementarrayを返します。
本節では、既存の TopoGeometry を編集する関数を挙げます。
topoelementarray (topoelement の配列) を返します。
topoelementオブジェクトの集合を返します。
本節では TopoGeometry とトポロジプリミティブとの間の関係を見るトポロジ関数の一覧を挙げます。
トポロジを生成したり、場合によってはトポロジレイヤに関連付けると、バックアップや他のデータベースへの転送のために、ファイルに出力するフォーマットでエクスポートした方がいいでしょう。
トポロジはテーブルの集合 (プリミティブで 4テーブル、レイヤで任意数) と、メタデータテーブルのレコード (topology.topology と topology.layer) になっているため、PostgreSQL の標準的なダンプ/レストアのツールでは問題があります。さらに、トポロジの識別子はデータベース間で同じではないため、トポロジのパラメータはレストア時に変更する必要があります。
トポロジのエクスポート/レストアを簡単化するために、二つの実行可能ファイルが提供されています。pgtopo_exportとpgtopo_importです。使用例は次の通りです。
pgtopo_export dev_db topo1 | pgtopo_import topo1 | psql staging_db
pgtopo_exportスクリプトはデータベース名とトポロジ名を取り、トポロジ (と関連するレイヤ) を新しいデータベースにインポートするために使うことができるダンプファイルを出力します。
デフォルトではpgtopo_exportはダンプファイルを標準出力に書くので、pgtopo_importにパイプで繋げたり、ファイルにリダイレクトする (端末への出力を止める) ことができます。出力ファイル名をコマンドラインスイッチ-fで指定することもできます。
デフォルトではpgtopo_exportは、指定されたトポロジに対して定義されたレイヤの全てのダンプが含まれます。これは、必要なデータより多い場合もありますし、動作しない場合もあり (レイヤテーブルが複雑な依存関係を持っている場合)、この場合には--skip-layersスイッチでレイヤをスキップし、別々に処理することができます。
pgtopo_exportを--help (または短縮の-h) スイッチで実行すると、常に短い使用法文字列を印字します。
ダンプファイルフォーマットは、少なくともフォーマットバージョン情報を持つpgtopo_dump_versionファイルを含むpgtopo_exportディレクトリの圧縮された tar アーカイブです。バージョン1では、ディレクトリにはトポロジのプリミティブテーブル (node, edge_data, face, relation) と、プリミティブと関連付けられたトポロジとレイヤとを持つタブ区切り CSV ファイルが含まれ、(--skip-layersが与えられない場合) 与えられたトポロジのレイヤとして報告されたテーブルの PostgrfeSQLダンプのカスタムフォーマットも含まれます。
pgtopo_importスクリプトはpgtopo_export書式のトポロジのダンプと、生成するトポロジに与える名前とを取り、そこからトポロジと関連するレイヤを再構築する SQL スクリプトを出力します。
生成された SQL には、与えられた名前でトポロジを生成し、プリミティブなデータをロードし、格納して、TopoGeometry 値を確実に正しいトポロジに関連付けることで全てのトポロジレイヤを登録する手続きが含まれます。
デフォルトではpgtopo_importは標準入力からダンプを読むので、pgtopo_exportをパイプで繋げて併用することができます。入力ファイル名をコマンドラインスイッチ-fで指定することもできます。
デフォルトではpgtopo_importの SQL ファイル出力には、ダンプで発見された全てのレイヤを格納するためのコードが含まれます。
これらは、ダンプ内にあるテーブル名が既に対象データベース内のテーブル名で使われている場合には、望ましくないようになるか動作しない可能性があります。この場合には、レイヤをスキップする--skip-layersスイッチを使い、別に (または後で) 処理することができます。
ロードして、レイヤを名前付きトポロジにリンクする SQL は --only-layers スイッチで生成できます。これは、名前の競合を解決した*後に*レイヤをロードしたり、異なるトポロジのレイヤ (開始トポロジで空間的に簡略化されたもの等が挙げられます) にリンクするのに使えます。
対象トポロジが既に存在していて事前に削除したいなら --drop-topology スイッチを渡すことができます (PostGIS 3.6.0以降)。