20. ジオメトリ構築関数

これまでに見た関数は、全てジオメトリを「そのまま」動作させ、次のように返していました

  • オブジェクトの分析 (ST_Length(geometry), ST_Area(geometry)) や、

  • オブジェクトの直列化 (ST_AsText(geometry), ST_AsGML(geometry)) や、

  • オブジェクトの一部 (ST_RingN(geometry,n)) または

  • TRUE / FALSE のテスト (ST_Contains(geometry,geometry), ST_Intersects(geometry,geometry)) です。

「ジオメトリ構築関数」はジオメトリを入力に取り新しい形状を出力にします。

20.1. ST_Centroid / ST_PointOnSurface

ポリゴン地物を地物のポイント表現に入れ替える空間クエリを作成するとき一般に必要となります。これは空間結合 (ポリゴン/ポリゴン結合 で触れます) に使うといいです。二つのポリゴンレイヤでの ST_Intersects(geometry,geometry) は、ポリゴンが複数オブジェクト境界上にあると、両方とインタセクトしたりして、しばしば数え上げが二重になることがあるためです。変換したポイントがどちらか一方について、両方にはつかないように強制します。

  • ST_Centroid(geometry) は入力引数の中心におおむね来るポイントを返します。この単純計算は非常に速いですが、返されるポイントが地物の中にあるとは限らず、望ましいものではない場合があります。入力地物が凸でない場合 (欧字の 'C' を想像して下さい) には、重心は地物の内部にない場合があります。

  • ST_PointOnSurface(geometry) は入力引数の内側にあることを保証するポイントを返します。これは、空間結合で「代理ポイント」を計算するのに、もっとベンチです。

_images/centroid.jpg
-- Compare the location of centroid and point-on-surface for a concave geometry

SELECT ST_Intersects(geom, ST_Centroid(geom)) AS centroid_inside,
       ST_Intersects(geom, ST_PointOnSurface(geom)) AS pos_inside
FROM (VALUES
    ('POLYGON ((30 0, 30 10, 10 10, 10 40, 30 40, 30 50, 0 50, 0 0, 0 0, 30 0))'::geometry)
  ) AS t(geom);
 centroid_inside | pos_inside
-----------------+------------
 f               | t

20.2. ST_Buffer

バッファ処理は GIS の作業手順では一般的に行われていて、PostGIS でも使用できます。 ST_Buffer(geometry,distance) はバッファ距離とジオメトリタイプを受け取り、入力ジオメトリからバッファ距離だけ離れた境界を持つポリゴンを出力します。

_images/st_buffer.png

たとえば、米国国立公園局は、リバティ島の周りの海上交通ゾーンを設定したいとすると、島の周囲500mのバッファとなるポリゴンを構築できます。リバティ島は、このワークショップで使用している nyc_census_blocks テーブル内の単一街区ですので、簡単に抽出してバッファ処理を実行できます。

-- Make a new table with a Liberty Island 500m buffer zone
CREATE TABLE liberty_island_zone AS
SELECT ST_Buffer(geom,500)::geometry(Polygon,26918) AS geom
FROM nyc_census_blocks
WHERE blkid = '360610001001001';
_images/liberty_positive.jpg

ST_Buffer 関数も、負の距離を受け付け、ポリゴンの入力から内側に入り込んだポリゴンを構築します。ラインとポイントでは、空ポリゴンが返されます。

_images/liberty_negative.jpg

20.3. ST_Intersection

他の伝統的な GIS 処理である「オーバレイ」によって、二つの重なったポリゴンのインタセクションを計算して新しいカバレッジが生成されます。結果には、結果の中にあるポリゴンをマージすることで、いずれかの親の中にある任意のポリゴンを構築できるという特性があります。

ST_Intersection(geometry A, geometry B) 関数は、引数の両方が共有する空間の面 (または線または点) を返します。引数が接続していない場合には、関数は空ジオメトリを返します。

-- What is the area these two circles have in common?
-- Using ST_Buffer to make the circles!

SELECT ST_AsText(ST_Intersection(
  ST_Buffer('POINT(0 0)', 2),
  ST_Buffer('POINT(3 0)', 2)
));
_images/intersection.jpg

20.4. ST_Union

前の例では、ジオメトリをインタセクトさせて、両方の入力からのラインを持つ新しいジオメトリを生成しました。ST_Union 関数は反転せず、入力を取り共有ラインを削除します。ST_Union には二つの形式があります:

  • ST_Union(geometry, geometry): 2引数版では二つのジオメトリを取り、マージされた結合を返します。たとえば、前の節で出てきた二つの円の例は、インタセクションを結合に入れ替えたら、これと同じように見えます。

    -- What is the total area these two circles cover?
    -- Using ST_Buffer to make the circles!
    
    SELECT ST_AsText(ST_Union(
      ST_Buffer('POINT(0 0)', 2),
      ST_Buffer('POINT(3 0)', 2)
    ));
    
    _images/union.jpg
  • ST_Union([geometry]): 集約関数版は、ジオメトリの集合を引数に取り、グループ全体についてマージしたジオメトリを返します。集約関数版の ST_Union は、基本ジオメトリの部分集合を慎重に生成するために、"GROUP BY" SQL 句を併用できます。非常に強力です。

ST_Union 集約関数の例として、nyc_census_blocks テーブルを考えます。国勢調査のジオグラフィは、小さいジオグラフィから大きいジオグラフィを作成できるよう慎重に構築されています。そのため、国勢調査区を構成する街区をマージして国勢調査区を生成することができます (後の 国勢統計区テーブルの作成 で行います)。また、各郡に属する街区のマージで郡の生成ができます。

マージの実行には、一意のキーである blkid が実際にはより高いレベルのジオグラフィに関する情報が埋め込まれていることに注意して下さい。以前に使用したリバティ島についてのキーの一部は次の通りです:

360610001001001 = 36 061 000100 1 001

36     = State of New York
061    = New York County (Manhattan)
000100 = Census Tract
1      = Census Block Group
001    = Census Block

blkid の先頭5桁が同じジオメトリの全てをマージして郡地図を作ることができます。我慢して下さい。これは計算コストが高く、1、2分かかりえます。

-- Create a nyc_census_counties table by merging census blocks
CREATE TABLE nyc_census_counties AS
SELECT
  ST_Union(geom)::Geometry(MultiPolygon,26918) AS geom,
  SubStr(blkid,1,5) AS countyid
FROM nyc_census_blocks
GROUP BY countyid;
_images/union_counties.png

面積テストによって、結合処理でジオメトリが一切失われないことを確認することができます。まず、個々の街区の面積を計算し、郡IDによるグループ化して、グループごとに面積を合計します。

SELECT SubStr(blkid,1,5) AS countyid, Sum(ST_Area(geom)) AS area
FROM nyc_census_blocks
GROUP BY countyid
ORDER BY countyid;
 countyid |       area
----------+------------------
 36005    | 110196022.906506
 36047    | 181927497.678368
 36061    | 59091860.6261323
 36081    | 283194473.613692
 36085    | 150758328.111199

郡テーブルから個々の新しい郡ポリゴンの面積を計算します:

SELECT countyid, ST_Area(geom) AS area
FROM nyc_census_counties
ORDER BY countyid;
 countyid |       area
----------+------------------
 36005    | 110196022.906507
 36047    | 181927497.678367
 36061    | 59091860.6261324
 36081    | 283194473.593646
 36085    | 150758328.111199

同じ結果になりました! 街区データからニューヨーク市の郡テーブルの構築に成功しました。

20.5. 関数リスト

ST_Centroid(geometry): 入力ジオメトリの重心を表現するポイントジオメトリを返します。

ST_PointOnSurface(geometry): 入力ジオメトリの内部にあることが保証されたポイントジオメトリを返します。

ST_Buffer(geometry, distance): ジオメトリ版: 指定したジオメトリからの距離が指定した距離以下である点の全てを表現するジオメトリを返します。ジオメトリの計算は、指定したジオメトリの空間参照系で行われます。ジオグラフィ版: 投影座標系に変換する薄いラッパです。

ST_Intersection(geometry A, geometry B): geomA と geomB との共有部を表現するジオメトリを返します。ジオグラフィ実装では、ジオメトリに変換して、インタセクションを得て、 WGS84 に戻します。

ST_Union(): ジオメトリ群の和集合を表現するジオメトリを返します。

ST_AsText(text): SRIDメタデータを持たないジオメトリ/ジオグラフィの Well-Known Text (WKT) 表現を返します。

substring(string [from int] [for int]): SQL 正規表現に合致する部分文字列を抽出する PostgreSQL 文字列関数です。

sum(expression): レコード集合のレコードの合計を返すPostgreSQL 集約関数です。