PostGISで三角形を描画する

#PostGISで3角形を描画する

##忙しい人用：自作関数による3角描画（経度、緯度、外接円の半径）
第1引数の経度、第2引数の緯度で指定された中心点を持つ、第3引数ｒで指定した半径の円の中にある正三角形を出力します。


```sql:create_function_st_triangle.sql
/**
 * Create triangle geometry.
 * @param lon double precision center point WGS84 SRID:4326
 * @param lat double precision WGS84 SRID:4326
 * @param r double precision circumradius by the meter
 * @return geometry
 **/
CREATE OR REPLACE FUNCTION st_triangle( lon double precision, lat double precision , r double precision )
RETURNS geometry
AS 'with t_plist(id,n)as (
    values (1, 25 ) , (2, 4 ) , (3, 14 ) ,(4, 25 ) )
SELECT
 ST_SetSRID( ST_MakePolygon(ST_MakeLine( array( SELECT geom 
FROM
(
	SELECT
	  id, geom
	FROM
	 ST_dumppoints( ST_ExteriorRing( ST_Buffer( ST_POINT( $1 , $2)::geography , $3)::geometry  ) )  AS c
	 , t_plist
	WHERE 
	 t_plist.n = path[1]
	ORDER BY t_plist.id
) AS a ) ) ) , 4326 );'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;

```

###デモ：東京駅を中心とした、三角形の描画

```sql:test_st_triangle.sql
SELECT ST_AsText( st_triangle( 139.76719 , 35.6811 ,50) ) 
UNION
SELECT ST_AsText( st_triangle( 139.76719 , 35.6811 ,100) ) 
UNION
SELECT ST_AsText( st_triangle( 139.76719 , 35.6811 ,150) )  
UNION
SELECT ST_AsText( st_triangle( 139.76719 , 35.6811 ,200) )  

```
![3.3.png](https://qiita-image-store.s3.amazonaws.com/0/53918/58ea7c68-ec2c-c0e5-b96a-2bef33124cd1.png)


#PostGISで三角形を作画するには
##任意の大きさの作図を実現するST_Bufferの利用
以前の投稿[PostGISのST_Bufferで円を描画する](http://qiita.com/takahi/items/37c0a096d2073646ba62)で、ST＿Bufferで作成した円は、半径に関わらず32角である事が分かりました。そこで、３２角の特定の角をピックアップすれば、三角形が作成出来ると考えました。

##ST_Bufferの補間点の出力の観察
補間点をQGISで表示して観察してみると、最も東の点から時計回りで出力されいます。
![3.2.png](https://qiita-image-store.s3.amazonaws.com/0/53918/0bc62755-5dfa-93a8-275f-d3840a83f30f.png)

真北に頂点を持つ正三角形を作成するには、「２５→４→１４→２５」の順番で点を結ぶとポリゴンが作成できます。

<img width="307" alt="18.09.29.png" src="https://qiita-image-store.s3.amazonaws.com/0/53918/fcd62d1b-203c-6d8f-8df4-e7fd033c5703.png">



- 32角から、２５と４と１４と２５を列挙する


```sql
with t_plist(id,n)as (
    values (1, 25 ) , (2, 4 ) , (3, 14 ) ,(4, 25 ) )
```

- 円から補間点を抽出し、`t_plist`のn列で指定した番号のポイントを抽出

```sql
    SELECT
      id, geom
    FROM
     ST_dumppoints( ST_ExteriorRing( ST_Buffer( ST_POINT( $1 , $2)::geography , $3)::geometry  ) )  AS c
     , t_plist
    WHERE 
     t_plist.n = path[1]
```
PostGISの関数の簡単な解説
`ST_POINT` : 緯度経度からポイント型の地物を作成する
`ST_Buffer` : 地物の周りに面を付与する
`ST_ExteriorRing` : ポリゴン形状の外枠を線オブジェクトとして出力する
`ST_dumppoints` : 地物の補間点を列挙する

# 関数化

SQLによるストアドファンクション（自作関数）を作成してみます。
PostgreSQLの場合、関数名が同じでも引数と戻り値が異なると、同一名称でも複数個の登録が可能です。

```sql:
CREATE OR REPLACE FUNCTION 関数名( 引数名 型 )
RETURNS 戻り値の型
AS 'ここにSQLを記述する。 引数は$1で呼び出し可能'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;
```

折角なので、３タイプ程関数を作ってみます。
同じ名称ですが、１つのデータベースに同居可能です。

##関数1：緯度、経度、外接円の半径を指定する関数

```sql:create_function_st_triangle_1.sql
/**
 * Create triangle geometry.
 * @param lon double precision center point WGS84 SRID:4326
 * @param lat double precision WGS84 SRID:4326
 * @param r double precision circumradius by the meter
 * @return geometry
 **/
CREATE OR REPLACE FUNCTION st_triangle( lon double precision, lat double precision , r double precision )
RETURNS geometry
AS 'with t_plist(id,n)as (
    values (1, 25 ) , (2, 4 ) , (3, 14 ) ,(4, 25 ) )
SELECT
 ST_SetSRID( ST_MakePolygon(ST_MakeLine( array( SELECT geom 
FROM
(
	SELECT
	  id, geom
	FROM
	 ST_dumppoints( ST_ExteriorRing( ST_Buffer( ST_POINT( $1 , $2)::geography , $3)::geometry  ) )  AS c
	 , t_plist
	WHERE 
	 t_plist.n = path[1]
	ORDER BY t_plist.id
) AS a ) ) ) , 4326 );'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;

```


##関数２　：GEOMETRY型と外接円の半径を指定するタイプ

```sql:create_function_st_triangle_2.sql
/**
 * Create triangle geometry.
 * @param center_point geometry 
 * @param r double precision circumradius by the meter
 * @return geometry
 **/
CREATE OR REPLACE FUNCTION st_triangle( center_point geometry , r double precision )
RETURNS geometry
AS 'with t_plist(id,n)as (
    values (1, 25 ) , (2, 4 ) , (3, 14 ) ,(4, 25 ) )
SELECT
 ST_SetSRID( ST_MakePolygon(ST_MakeLine( array( SELECT geom 
FROM
(
	SELECT
	  id, geom
	FROM
	 ST_dumppoints( ST_ExteriorRing( ST_Buffer( ST_Transform( $1 , 4326)::geography , $2 )::geometry  ) )  AS c
	 , t_plist
	WHERE 
	 t_plist.n = path[1]
	ORDER BY t_plist.id
) AS a ) ) ) , 4326 );'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;

```

##関数3　：GEOGRAPHY型と外接円の半径を指定するタイプ

```sql:create_function_st_triangle_3.sql
/**
 * Create triangle geometry.
 * @param center_point geography 
 * @param r double precision circumradius by the meter
 * @return geography
 **/
CREATE OR REPLACE FUNCTION st_triangle( center_point geography , r double precision )
RETURNS geography
AS 'with t_plist(id,n)as (
    values (1, 25 ) , (2, 4 ) , (3, 14 ) ,(4, 25 ) )
SELECT
 ST_SetSRID( ST_MakePolygon(ST_MakeLine( array( SELECT geom 
FROM
(
	SELECT
	  id, geom
	FROM
	 ST_dumppoints( ST_ExteriorRing( ST_Buffer($1 , $2 )::geometry  ) )  AS c
	 , t_plist
	WHERE 
	 t_plist.n = path[1]
	ORDER BY t_plist.id
) AS a ) ) ) , 4326 )::geography ;'
LANGUAGE SQL
IMMUTABLE
RETURNS NULL ON NULL INPUT;

```