2014/11/18

PostGIS のジオメトリを直接 SVG 出力 & ブラウザで表示

[PostGIS][SVG]
PostGIS ラスタから画像埋め込み HTML を出力(2014/10/23)のジオメトリ編のような内容です。PostGIS には古くから ST_AsSVG という関数がありますが、戻り値が SVG そのものではなく(この点ラスタの ST_AsPNG とは違う)、SVG を構成する path 要素の d 属性だけを返すため、少し手間がかかります。それでもクエリ一つで ↓ の SVG を PostGIS から直接出力してブラウザに表示できました。以下、手順を段階的に振り返ったメモです。

■ 20141118_postgis_to_svg.html(下と同じもの 190 KB)


上の例は現在のさいたま市の行政界で、元データは国土数値情報の行政区域のシェープファイルです。実行環境は Windows 7 32bit + PostgreSQL 9.3.5 + PostGIS 2.1.3。SVG の確認に使うブラウザは Chrome 38。↓ がジオメトリのテーブル。埼玉県全体の行政界ですが、今回は最初のテストとして、さいたま市だけ出力しました。


↓ とりあえずジオメトリを ST_AsSVG に渡した結果。path 要素ではおなじみの M コマンドで始まる d 属性が返ります。一つの d 属性に複数の図形を持てるので、マルチジオメトリもそのまま変換できます。M コマンドの後は全て L です(ポリゴンの構成線は全て直線)。Y 座標は軸の向きが逆なので全て負数になります。

SELECT jcode, jname, ST_AsSVG(geom)
FROM "201411"."18_geom_example"
WHERE jcode LIKE '111%' ;


元データの経緯度は小数点以下6桁ですが、今回 ST_AsSVG で出力された値には少し端数が出ました。この桁数は第三引数で調整することができ(デフォルトは15桁)、↓ のように10桁に落としたら端数が消えて元データと同じになりました。第二引数は絶対/相対座標の切り替えです。詳細は PostGIS 2.2.0devマニュアル日本語訳 : ST_AsSVG を参照。

SELECT jcode, jname, ST_AsSVG(geom, 0, 10)
FROM "201411"."18_geom_exaample"
WHERE jcode LIKE '111%' ;


これでひとまずジオメトリを ST_AsSVG で変換できることが分かったので、さらに path 要素の色・線、注記(今回は行政区)の位置や大きさなどを SVG の仕様に即して追加していきます。以下では次のウェブサイトを参考にしました。

■ svg要素の基本的な使い方まとめ
■ SVG Textのtext-anchor属性をつかってみた。

↓ ジオメトリの path 要素を作るクエリ。ストロークの幅は座標値に合わせる必要があり、今回は経緯度座標のままなのですごく小さい値になります。

WITH settings AS (
SELECT 'wheat' :: text AS fill_poly
, 'white' :: text AS stroke_poly
, 5e-4 AS stroke_width
), a AS (
-- // 元データ、さいたま市のみ
SELECT * FROM "201411"."18_geom_example"
WHERE jcode LIKE '111%'
)
SELECT concat('<path fill="', fill_poly
, '" stroke="', stroke_poly
, '" stroke-width="', stroke_width
, '" stroke-linejoin="round"' -- // 頂点を丸く
, ' d="', ST_AsSVG(geom, 0, 10), '"/>')
FROM a, settings ;


↓ 注記の text 要素を作るクエリ。各ポリゴンの重心に合わせて中央揃えします。この文字サイズも、上のストローク幅と同様に経緯座標値に合わせ、すごく小さい値にします。Y 座標の反転を忘れがちなので注意。

WITH settings AS (
SELECT 'gray' :: text AS fill_text
, 5e-3 AS font_size
), a AS (
-- // 元データ、さいたま市のみ
SELECT concat(jcode, ' ', replace(jname, 'さいたま市', '')) lab
, ST_Centroid(geom) poi
FROM "201411"."18_geom_example"
WHERE jcode LIKE '111%'
)
SELECT concat('<text x="'
, ST_X(poi)
, '" y="', ST_Y(poi) * -1 -- // 縦軸反転
, '" fill="', fill_text
, '" font-size="', font_size
, '" text-anchor="middle">' -- // 中央揃え
, lab, '</text>')
FROM a, settings ;


↓ viewBox を設定し SVG の先頭となるタグを作るクエリ。ここでは全体のバウンディングボックス を viewBox にしましたが、適宜変えることで余白を付けたり、逆にクリップできます。上と同様 Y 座標の反転を忘れがちなので注意。

WITH a AS (
-- // 元データ、さいたま市のみ
SELECT * FROM "201411"."18_geom_example"
WHERE jcode LIKE '111%'
), d AS (
-- // 準備
SELECT ST_Envelope(ST_Collect(geom)) env FROM a
)
SELECT concat('<svg viewBox="'
, concat_ws(' ', ST_XMin(env)
, ST_YMax(env) * -1 -- // 縦軸反転
, ST_XMax(env) - ST_XMin(env)
, ST_YMax(env) - ST_YMin(env))
, '">')
FROM d ;


これで全ての要素が揃いました。あとは一つのクエリにまとめて COPY 文で出力します。全体は次のようになります。出力先に PostgreSQL ユーザの書き込み権限が必要です。

COPY (
WITH settings AS (
SELECT 'wheat' :: text AS fill_poly
, 'white' :: text AS stroke_poly
, 'gray' :: text AS fill_text
, 5e-4 AS stroke_width
, 5e-3 AS font_size
), a AS (
-- // 元データ さいたま市のみ
SELECT concat(jcode, ' ', replace(jname, 'さいたま市', '')) lab
, geom, ST_Centroid(geom) poi
FROM "201411"."18_geom_example"
WHERE jcode LIKE '111%'
), b AS (
-- // ジオメトリ
SELECT concat('<path d="'
, ST_AsSVG(geom, 0, 10)
, '" fill="', fill_poly
, '" stroke="', stroke_poly
, '" stroke-width="', stroke_width
, '" stroke-linejoin="round" />') -- // 頂点を丸く
FROM a, settings
), c AS (
-- // テキスト
SELECT concat('<text x="'
, ST_X(poi)
, '" y="', ST_Y(poi) * -1 -- // 縦軸反転
, '" fill="', fill_text
, '" font-size="', font_size
, '" text-anchor="middle">' -- // 中央揃え
, lab, '</text>')
FROM a, settings
), d AS (
-- // viewBox の準備
SELECT ST_Envelope(ST_Collect(geom)) env FROM a
)
SELECT concat('<html><svg viewBox="'
, concat_ws(' ', ST_XMin(env)
, ST_YMax(env) * -1 -- // 縦軸反転
, ST_XMax(env) - ST_XMin(env)
, ST_YMax(env) - ST_YMin(env))
, '">')
FROM d
UNION ALL SELECT * FROM b
UNION ALL SELECT * FROM c -- // 下に重ねるほど、上に描画される
UNION ALL SELECT '</svg></html>'
) TO 'R:/20141118_postgis_to_svg.html' ;


これで出力したのが冒頭の表示です。下がその再掲。ブラウザでなく Illustrator などで SVG だけ必要なら、上のクエリから HTML タグを除いて出力すれば SVG になります。


↓ 出力結果をエディタで開いたところ。COPY 文で実行したクエリの結果がそのまま一行ずつ書き込まれ、各行がそのまま SVG の一要素になります。


今回なかった穴あきポリゴンは、きちんと描画できるか今後確認します。また今回ジオメトリと注記一つ一つに色や線を入力しましたが、g 要素でまとめて設定する手もあります。その場合、ジオメトリと注記ごとにグループ化してタグを連結するクエリになります。その例は機会があればいずれ。
×

この広告は1年以上新しい記事の投稿がないブログに表示されております。