第9章 トポロジ

目次

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 を指定することで、無効にできます。

9.1. ラインからフェイスを構築する

この例では、ノードを与えたラインからトポロジーのフェイスを構築し、結果フェイスにポイント属性を関連付けます。トポロジーに投入するために 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');

9.2. トポロジープリミティブテーブル

あらゆるトポロジーの中核的なプリミティブはedge_datanodefaceテーブルに格納されます。これらのテーブルは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カラムは絶対値にして扱いすくしたものです。

この表現は二重連結辺リストの異種で、多数のトポロジールーチンで利用されます。 GetRingEdgesValidateTopology といった関数によるフェイス境界の再構築や矛盾の診断に使われます。したがって評価時に「不正な next_left_edge」と「不正な next_right_edge」診断が報告されます。 AddEdge のようなコンストラクターは next_* 属性を普通の自己参照で初期化します。ST_AddEdgeModFaceST_RemEdgeModFace を含む編集ルーチンは、エッジが挿入されるか削除されると同時にリンクを更新します。他の大量処理 (たとえば Polygonize) は意図的にカラムに値を設定しないことがあり、文書では振舞いに関する注意を明示的に出しています。

9.3. トポロジ型

概要

本節では、PostGIS トポロジでインストールされる PostgreSQL データ型の一覧を挙げます。独自に関数をデザインする際に特に重要となる、キャストでの挙動を記述していることにご注意ください。

  • getfaceedges_returntype — 順序番号とエッジ番号で構成される複合型。
  • TopoGeometry — トポロジとして定義されたジオメトリーを表現する型です。
  • validatetopology_returntype — エラーメッセージとエラーの場所を示す id1と id2からなる複合型です。これはValidateTopologyが返す型です。

9.4. トポロジドメイン

概要

本節では、PostGIS トポロジでインストールされる PostgreSQL ドメインの一覧を挙げます。ドメインは、オブジェクト型のように扱え、関数やテーブルカラムのオブジェクトを返します。ドメインは存在するチェック制約を持つ既存の型である点で、型とは違います。

  • TopoElement — 二つの整数の配列で、通常 TopoGeometry 要素を識別するために使われます。
  • TopoElementArray — TopoElement オブジェクトの配列。

9.5. トポロジ管理と TopoGeometry 管理

概要

本節では、新しいトポロジスキーマの構築、トポロジの評価、TopoGeometry カラムの管理のためのトポロジ関数の一覧を挙げます。

  • AddTopoGeometryColumn — 既存のテーブルに TopoGeometry カラムを追加し、topology.layer 内に新しいレイヤとして新しいカラムを登録して、新しい layer_id を返します。
  • RenameTopoGeometryColumn — TopoGeometry カラムの名前を変更します
  • DropTopology — 使用上の注意: この関数によって、トポロジスキーマが削除され、topology.topologyテーブルからの参照が削除され、geometry_columns テーブルから削除対象スキーマ内のテーブルへの参照が削除されます。
  • RenameTopology — トポロジ名を変更します
  • DropTopoGeometryColumn schema_nameで指定されたスキーマ内にあるtable_nameで指定されたテーブルから TopoGeometry カラムを削除し、topology.layer テーブルにある登録を解除します。
  • FixCorruptTopoGeometryColumn — postgis_topology を 3.6.0以上にアップグレードする際に発生するポジオメトリーの間違いを訂正します。
  • Populate_Topology_Layer — テーブルからメタデータを読み、topology.layer テーブルに不足しているものを追加します。
  • TopologySummary — トポロジ名を取り、トポロジ内のオブジェクトの型に関する概要の全体を提供します。
  • ValidateTopology — トポロジの問題についての詳細を示す validatetopology_returntype の集合を返します。
  • ValidateTopologyRelation — 不正なトポロジ関係の行に関する情報を返します。
  • ValidateTopologyPrecision — トポロジ内の低精度の頂点を返します。
  • MakeTopologyPrecise — トポロジの頂点を精度グリッドにスナップします。
  • FindTopology — 異なる方法でトポロジ行を返します。
  • FindLayer — 様々な方法で topology.layer 行を返します。
  • TotalTopologySize — 指定したトポロジが使用するディスク容量の総量を得ます。インデックスや TOAST データも含みます。
  • UpgradeTopology — 指定したトポロジーに対して、トポロジー ID とプリミティブ ID が大きな数 (int8) に対応するようアップグレードします。

9.6. トポロジ統計管理

概要

本節では、トポロジ構築時のデータベース統計の管理について説明します。

トポロジに要素を追加すると、そのトリガとして、分割されることになる既存のエッジを探索し、ノードを追加し、新しいラインでノードを作成するエッジを更新するために多数のデータベースクエリが発生します。このため、トポロジテーブル内のデータに関する統計情報が最新の状態になっているなら、統計情報を使うと便利です。

PostGIS トポロジーの追加や編集の関数は、自動的に統計情報を更新することはありません。トポロジにおいて逐次変更していては、統計情報の更新が過剰になるためです。処理は呼び出し元の義務となっています。

[注記]

autovacuum で更新された統計情報は、autovacuum プロセス完了前に始まったトランザクションからは見えないので、更新した統計情報を使うには、実行時間の長いトランザクションでは ANALYZE 自体を実行する必要があります。

9.7. トポロジコンストラクタ

概要

本節では、新しいトポロジを生成するトポロジ関数を挙げます。

  • CreateTopology — 新しいトポロジスキーマを生成し、topology.topology テーブルに登録します。
  • CopyTopology — トポロジ (ノード、エッジ、フェイス、レイヤと TopoGeometry) の新しいスキーマに複写します。
  • ST_InitTopoGeo — 新しいトポロジスキーマを生成し、topology.topology テーブルに登録します。
  • ST_CreateTopoGeo — 空のトポロジにジオメトリーのコレクションを追加し、成否を示すメッセージを返します。
  • TopoGeo_AddPoint — 許容差を使って既存のトポロジにポイントを追加し、可能ならエッジを分割します。
  • TopoGeo_AddLineString — 許容差を使って既存のトポロジにラインストリングを追加し、可能ならエッジ/フェイスを分割します。
  • TopoGeo_AddPolygon — 許容差を使って既存のトポロジにラインストリングを追加し、可能ならエッジ/フェイスを分割します。エッジ識別番号を返します。
  • TopoGeo_LoadGeometry — ジオメトリーを既存のトポロジにロードし、必要に応じてスナップと分割を実行します。

9.8. トポロジエディタ

概要

本節では、エッジ、フェイス、ノードの追加、移動、削除、分割に関する関数を挙げます。本節の関数はすべて ISO SQL/MM で定義されています。

  • ST_AddIsoNode — フェイスに孤立ノードを追加し、新しいノードの識別番号を返します。フェイスがNULL の場合でもノードは生成されます。
  • ST_AddIsoEdge anodeanothernodeで指定される二つの既存孤立ノードを接続するトポロジに、ジオメトリーalinestringで定義される孤立エッジを追加し、新しいエッジの識別番号を返します。
  • ST_AddEdgeNewFaces — 新しいエッジを追加します。新しいエッジがフェイスを分割する場合には、もとのフェイスを削除して、分割した二つのフェイスに置き換えます。
  • ST_AddEdgeModFace — 新しいエッジを追加します。新しいエッジがフェイスを分割する場合には、もとのフェイスを編集し、一つのフェイスを追加します。
  • ST_RemEdgeNewFace — エッジを削除し、消去対象エッジでフェイスが二つに分割されているなら元の二つのフェイスを削除し、一つの新しいフェイスに置き換えます。
  • ST_RemEdgeModFace — エッジを削除します。削除されるエッジが二つのフェイスを分割していたなら、一方のフェイスを削除し、もう一方のフェイスを両方の空間を覆うように編集します。
  • ST_ChangeEdgeGeom — トポロジ構造に影響を与えることなくエッジの形状を変更します。
  • ST_ModEdgeSplit — 既存のエッジに沿って新しいノードを生成してエッジを分割します。もとのエッジは変更され、新しいエッジが一つ追加されます。
  • ST_ModEdgeHeal — 二つのエッジについて、接続しているノードを削除して修復します。1番目のエッジを編集して、2番目のエッジを削除します。削除されたノードの識別番号を返します。
  • ST_NewEdgeHeal — 二つのエッジについて、接続しているノードを削除して修復します。両方のエッジを削除し、1番目のエッジと同じ方向のエッジに置き換えます。
  • ST_MoveIsoNode — トポロジ内の孤立ノードを別の位置に移動させます。新しいapointジオメトリーがノードとして存在しているなら、エラーが投げられます。移動に関する説明を返します。
  • ST_NewEdgesSplit — 新しいノードを既存のエッジに沿って作成して、エッジを分割します。もとのエッジは削除され、二つのエッジに置き換えられます。二つの新しいエッジに接続する新しいノードの識別番号を返します。
  • ST_RemoveIsoNode — 孤立ノードを削除し、実行結果が返されます。ノードが孤立していない (エッジの始端または終端である) 場合には、例外が投げられます。
  • ST_RemoveIsoEdge — 孤立エッジを削除し、実行結果の記述を返します。エッジが孤立していない場合には、例外が投げられます。

9.9. トポロジアクセサ

  • GetEdgeByPoint — 与えられたポイントにインターセクトするエッジの識別番号を探索します。
  • GetFaceByPoint — 与えたポイントとインタセクトするフェイスを見つけます。
  • GetFaceContainingPoint — ポイントを含むフェイスを見つけます。
  • GetNodeByPoint — ポイント位置にあるノードの識別番号を探索します。
  • GetTopologyID — トポロジ名から topology.topology テーブル内にあるトポロジの識別番号を返します。
  • GetTopologySRID — トポロジ名から topology.topology テーブル内にあるトポロジの SRID を返します。
  • GetTopologyName — 識別番号からトポロジ (スキーマ) の名前を返します。
  • ST_GetFaceEdges — 順序番号を含む、afaceの境界となる、整列したエッジの集合を返します。
  • ST_GetFaceGeometry — 指定されたトポロジの中の、フェイス識別番号で指定されたポリゴンを返します。
  • GetRingEdges — 与えられた側を歩いて得られた、正負符号付きエッジ識別番号の集合を、順序通りに返します。
  • GetNodeEdges — 与えられたノードに付随するエッジの集合を整列して返します。
  • FindVertexSegmentPairsBelowDistance — 許容距離内にあるトポロジーの頂点/線分の組を検索します。

9.10. トポロジ処理

概要

本節では、非標準の手法でのトポロジ処理の関数を挙げます。

  • Polygonize — トポロジエッジで定義される全てのフェイスを探索し、追加します。
  • AddNode — 指定したトポロジスキーマのノードテーブルにポイントノードを追加し、新しいノードの識別番号を返します。指定したポイントに既にノードがある場合は既存のノード識別番号を返します。
  • AddEdge — 指定したラインストリングジオメトリーを使って、ラインストリングエッジをエッジテーブルに追加し、指定したトポロジスキーマの始点終点をポイントノードテーブルに追加し、新しい (または既存の) エッジの識別番号を返します。
  • AddFace — フェイスプリミティブをトポロジに登録し、その識別番号を得ます。
  • ST_Simplify — 与えた TopoGeometry を「シンプル化した」ジオメトリーを返します。ダグラス-ポーカーのアルゴリズムを使います。
  • RemoveUnusedPrimitives — 存在する TopoGeometry オブジェクトを定義するのに必要でないトポロジプリミティブを削除します。

9.11. TopoGeometry コンストラクタ

概要

本節では、新しい TopoGeometry を生成するトポロジ関数を挙げます。

  • CreateTopoGeom — 新しい TopoGeometry オブジェクトを topo エレメント配列から生成します - tg_type: 1:[multi]point, 2:[multi]line, 3:[multi]poly, 4:collection
  • toTopoGeom — 単純なジオメトリーから TopoGeometry を生成します。
  • TopoElementArray_Agg — element_id とタイプの配列 (topoelements) からなるtopoelementarrayを返します。
  • TopoElement — TopoGeometry を TopoElement に変換します。

9.12. TopoGeometry エディタ

概要

本節では、既存の TopoGeometry を編集する関数を挙げます。

  • clearTopoGeom — TopoGeometry の中身を消去します。
  • TopoGeom_addElement — TopoGeometry の定義に要素を追加します。
  • TopoGeom_remElement — TopoGeometry の定義から要素を削除します。
  • TopoGeom_addTopoGeom — TopoGeometry の要素を他の TopoGeometry の定義に追加します。
  • toTopoGeom — ジオメトリーの形状を既存の TopoGeometry に追加します。

9.13. TopoGeometry アクセサ

  • GetTopoGeomElementArray — 与えられた TopoGeometry (プリミティブ要素) のトポロジ要素とタイプを含むtopoelementarray (topoelement の配列) を返します。
  • GetTopoGeomElements — 与えられた TopoGeometry (プリミティブ要素) の、トポロジの element_id とelement_type を含むtopoelementオブジェクトの集合を返します。
  • ST_SRID — TopoGeometry の空間参照識別子を返します。

9.14. TopoGeometry 出力

  • AsGML — TopoGeometry の GML 表現を返します。
  • AsTopoJSON — opoGeometry の TopoJSON 表現を返します。

9.15. トポロジ空間関係関数

概要

本節では TopoGeometry とトポロジプリミティブとの間の関係を見るトポロジ関数の一覧を挙げます。

  • Equals — 二つの TopoGeometry が同じトポロジプリミティブで成っている場合に true を返します。
  • Intersects — 二つの TopoGeometry からのプリミティブの組がインタセクトする場合にtrue を返します。

9.16. トポロジのインポートとエクスポート

トポロジを生成したり、場合によってはトポロジレイヤに関連付けると、バックアップや他のデータベースへの転送のために、ファイルに出力するフォーマットでエクスポートした方がいいでしょう。

トポロジはテーブルの集合 (プリミティブで 4テーブル、レイヤで任意数) と、メタデータテーブルのレコード (topology.topology と topology.layer) になっているため、PostgreSQL の標準的なダンプ/レストアのツールでは問題があります。さらに、トポロジの識別子はデータベース間で同じではないため、トポロジのパラメータはレストア時に変更する必要があります。

トポロジのエクスポート/レストアを簡単化するために、二つの実行可能ファイルが提供されています。pgtopo_exportpgtopo_importです。使用例は次の通りです。

コード
pgtopo_export dev_db topo1 | pgtopo_import topo1 | psql staging_db

9.16.1. トポロジエクスポータの使用

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ダンプのカスタムフォーマットも含まれます。

9.16.2. トポロジインポータの使用

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