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以降)。