はじめに
シェープファイルのデータをインポートしてみよう コマンドライン編とシェープファイルのデータをインポートしてみよう GUI編で、国土数値情報(行政区域)のシェープファイルをPostGISにインポートしました。
コマンドライン編の後半で、シェープファイルにはマルチポリゴンはあって、単一ポリゴン(ジオメトリタイプ: POLYGON)が無いことを紹介しました。
国土数値情報(行政区域)については、シェープファイルの仕様で仕方なくマルチポリゴンとしていますが、実質的には単一ポリゴンです。
マルチポリゴンを単一ポリゴンに変換した方が良い場合と、市区町村ごとにマルチポリゴンにした方が良い場合とがあると思いますが、いずれの場合も、なんらかの加工が必要となります。
今回は、マルチポリゴンを単一ポリゴンに分解する方法を示します。
使用する関数
ST_Dump
, ST_NumGeometries
マルチポリゴンを単一ポリゴンに分解する
マルチ系ジオメトリを単一ジオメトリにするには、ST_Dump
を使います。
ST_Dump
は、geometry_dump型を返します。この型は、整数配列のpathとジオメトリ型のgeomとからできています。
db=# SELECT name, ST_Dump(geom) AS dump FROM (
SELECT
'g1' AS name,
'MULTIPOLYGON(((0 0, 1 0, 1 1, 0 0)),((2 0, 3 0, 3 1, 2 0)))'::GEOMETRY AS geom
UNION SELECT
'g2' AS name,
'MULTIPOLYGON(((0 2, 1 2, 1 3, 0 2)),((2 2, 3 2, 3 3, 2 2)))'::GEOMETRY AS geom
) AS Q1
name | dump
------+------------------------------------------------------------------------------------------------------------------------------------------------------------------
g1 | ({1},0103000000010000000400000000000000000000000000000000000000000000000000F03F0000000000000000000000000000F03F000000000000F03F00000000000000000000000000000000)
g1 | ({2},0103000000010000000400000000000000000000400000000000000000000000000000084000000000000000000000000000000840000000000000F03F00000000000000400000000000000000)
g2 | ({1},0103000000010000000400000000000000000000000000000000000040000000000000F03F0000000000000040000000000000F03F000000000000084000000000000000000000000000000040)
g2 | ({2},0103000000010000000400000000000000000000400000000000000040000000000000084000000000000000400000000000000840000000000000084000000000000000400000000000000040)
(4 行)
dumpが、整数配列とEWKBとの組み合わせになっているのが分かると思います。
pathとgeomとにバラして、geomはST_AsTextで見やすくして表示してみます。
db=# SELECT name, (dump).path, ST_AsText((dump).geom) FROM (
SELECT name, ST_Dump(geom) AS dump FROM (
SELECT
'g1' AS name,
'MULTIPOLYGON(((0 0, 1 0, 1 1, 0 0)),((2 0, 3 0, 3 1, 2 0)))'::GEOMETRY AS geom
UNION SELECT
'g2' AS name,
'MULTIPOLYGON(((0 2, 1 2, 1 3, 0 2)),((2 2, 3 2, 3 3, 2 2)))'::GEOMETRY AS geom
) AS Q1
) AS Q2;
name | path | st_astext
------+------+----------------------------
g1 | {1} | POLYGON((0 0,1 0,1 1,0 0))
g1 | {2} | POLYGON((2 0,3 0,3 1,2 0))
g2 | {1} | POLYGON((0 2,1 2,1 3,0 2))
g2 | {2} | POLYGON((2 2,3 2,3 3,2 2))
ST_Dump以外のカラム
ST_Dump以外のカラムは、マルチ系ジオメトリごとに同じ値となります。上の結果を見ると、nameカラムで'g1'となっている行と'g2'となっている行が複数あることが確認できると思います。
pathフィールド
pathにはマルチ系ジオメトリ内にあったときの順序が1はじまりで入っています。順序はマルチ系ジオメトリごとにユニークになりますが、複数のマルチ系ジオメトリがある場合にはユニークではありません。順序が逆転すると困る場合にはpathは重要になると思いますが、通常は無視してかまいません。
マルチ系ジオメトリ内の単一ジオメトリの数を見る
上の例では、Q1が吐くタプルはふたつだったけれども、最終的には4タプルが出てきました。つまりタプル数が増加する可能性があります。どれぐらい増加するかを事前に見るには、マルチ系ジオメトリごとに、内部に持つ単一ジオメトリを見ます。
db=# SELECT ST_NumGeometries(geom) FROM t1;
st_numgeometries
------------------
1
1
1
1
1
1
1
1
...
t1のマルチポリゴンは、全て単一ポリゴンをひとつずつ持っていることが分かります。
テーブル t1d を作成して t1 から分解したポリゴンをたたき込む
(以前はt1uとしていましたがt1dに変更します)
db=# CREATE TABLE t1d (
gid SERIAL PRIMARY KEY,
n03_001 TEXT,
n03_002 TEXT,
n03_003 TEXT,
n03_004 TEXT,
n03_007 TEXT,
geom GEOMETRY(POLYGON, 4612)
);
NOTICE: CREATE TABLE will create implicit sequence "t1d_gid_seq" for serial column "t1d.gid"
NOTICE: CREATE TABLE / PRIMARY KEY will create implicit index "t1d_pkey" for table "t1d"
CREATE TABLE
db=# CREATE INDEX ix_t1d_geom ON t1d USING GiST (geom);
CREATE INDEX
ふつうにINSERT INTOでOKです。
db=# INSERT INTO t1d(n03_001,n03_002,n03_003,n03_004,n03_007,geom)
SELECT n03_001,n03_002,n03_003,n03_004,n03_007,(ST_Dump(geom)).geom AS geom
FROM t1;
おわりに
今回は、ST_Dump
を使ってマルチポリゴンを単一ポリゴンに変換しました。今回はマルチポリゴンのダンプでしたが、他のマルチ系ジオメトリでもダンプできます。ST_Dump
は、データをインポートした直後によく使いますので、覚えておいて損はありません。
注意すべき点として、ST_Dump
を含むクエリの結果では、ダンプしているカラム以外のカラムでは、元テーブルのタプルごとに値が同じになることが上げられます。ユニーク制限をかけているカラムで値が重複すると困ったことになるので、テーブル設計の段階で、これをアタマに入れておく必要があります。
また、ST_NumGeometries
を使って、マルチ系ジオメトリが持つ単一ジオメトリの数を見て、国土数値情報(行政区域)はマルチポリゴンとしてインポートされているけれども、実質的には単一ポリゴンであることを確認しました。
冒頭、「市区町村ごとにマルチポリゴンにした方が良い場合」があると書きましたが、今回はマルチポリゴンを作っていません。またの機会にでも紹介できれば良いかなと思っています。