日本の行政区域データから都道府県と市区町村のポリゴンを作ってYellowfinでマップグラフにする

[先日の記事](https://qiita.com/hadatuna/items/7a3cea8a77c882b436c5)では世界の国単位での地図をYellowfinで作成しましたが、そうなると次は国内の地図が欲しくなるところです。さっそくやってみましょう。

なお、手順はどうでもいいからデータがほしいというお急ぎの方は、[こちら](https://qiita.com/hadatuna/items/532d08cd54a7e4ac9c0a#yellowfin%E3%81%A7%E4%BB%8A%E3%81%99%E3%81%90%E6%B4%BB%E7%94%A8%E3%81%A7%E3%81%8D%E3%82%8B%E3%83%87%E3%83%BC%E3%82%BF)へ飛んでください。

# 日本の行政区域データの入手

国土交通省の[国土数値情報ダウンロードサイト](https://nlftp.mlit.go.jp/)で公開されている[行政区域データ](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03-v2_3.html)を利用します。
![kokudo.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/5eaaccd3-b849-8114-2784-713116b5929c.png)

日本全体の地図が作りたいので、都道府県に別れたファイルではなく、[全国](https://nlftp.mlit.go.jp/ksj/gml/datalist/KsjTmplt-N03-v2_3.html#prefecture00)の最新のもの（記事公開時点では平成31年）を選択してダウンロードします。

# QGISでシェープファイルをCSVにエクスポートしてみる

[先日の記事](https://qiita.com/hadatuna/items/7a3cea8a77c882b436c5)の手順に沿って、ダウンロードしたシェープファイルをQGISでCSVエクスポートすれば、簡単にYellowfinで使えるデータが手に入ります。
![qgis_csv.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/5e6a52de-43d5-38b5-45c0-d898205df6c0.png)

ただ、このデータは行政区域単位のポリゴンなので **一般的な業務用途には細かすぎる** というデメリットがあります。
実際にYellowfinでこのデータを使ってみると、以下のように市区町村レベルや都道府県レベルで表示したい場面でも細かく分かれてしまい、期待した仕上がりにならないことがあります。
![yf_s.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/34bfa38e-213d-8359-1dcb-262bf6dc1032.png)

これでは使い勝手が悪いので、行政区域で分かれたポリゴンを結合して、都道府県や市区町村の単位のポリゴンデータを作りたいと思います。

# PostGISの環境構築

ポリゴンを結合する方法はいろいろありますが、今回は[PostGIS](http://postgis.net/)というPostgreSQLの拡張を使います。
既存の環境を汚したくないので、Dockerを使って新規に環境構築します。

## Dockerのインストール

Dockerを使うのがはじめての場合は、とりあえず[Docker Desktop](https://www.docker.com/products/docker-desktop/)をインストールしておけばいいでしょう。

## Dockerイメージの取得

Docker Hubで公開されている[postgis/postgis](https://hub.docker.com/r/postgis/postgis)のイメージを使います。
この記事の公開時点では **15-3.3** が最新だったのですが、後述の `docker compose` での起動がうまくいかなかったので、今回は **14-3.3** を使うことにしました。
以下のコマンドをシェル（Windowsならコマンドプロンプト、Macならターミナル）で実行してイメージを取得します。

```sh:Dockerイメージ取得
$ docker pull postgis/postgis:14-3.3
```

## docker-compose.ymlの作成

どこか任意のディレクトリに、以下のような内容の `docker-compose.yml` ファイルを作成します。

```yaml:docker-compose.yml
version: '3'
services:
  pg:
    container_name: postgis_container
    image: postgis/postgis:14-3.3
    environment:
      POSTGRES_USER: postgres
      POSTGRES_PASSWORD: password
    ports:
      - 15432:5432
    volumes:
        - pg_data:/var/lib/postgresql/data
volumes:
  pg_data:
    driver: local
```

## PostGISコンテナの起動

`docker-compose.yml` を保存したディレクトリに移動して、以下のコマンドを実行します。

```sh:コンテナ起動
$ docker compose up -d
```

正常にコンテナが起動したら、Dockerのログに以下のようなメッセージが出ます。なお、ログは`docker logs --tail postgis_container`等のコマンドで確認できます。

```
2023-07-03 02:52:00.237 UTC [1] LOG:  database system is ready to accept connections
```

PostGISが起動したら、`docker-compose.yml` で指定したユーザー名/パスワード/ポート番号に対して接続すれば普通のPostgreSQLと同じように使えます。
試しにpgAdmin4で接続してみると以下のような感じです。
![pgadmin4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/7205e121-4b64-7a24-3eb3-5d2eff4b1d73.png)
上記の `docker-compose.yml` ではローカルホスト側のポート番号を `15432` に、Dockerコンテナ内のポート番号を `5432` にしていますので、YellowfinやpgAdminなどのツールからは `15432` に対して接続しにいくことになります。

ちなみにコンテナを停止するときは以下のコマンドです。

```sh:コンテナ停止
$ docker stop postgis_container  （←docker-compose.ymlで設定したコンテナ名）
```

# データの結合

構築したPostGISを使って、行政区域データから都道府県単位と市区町村単位に結合したデータをそれぞれ作ります。

## シェープファイルをPostGISへインポート

まずはシェープファイルをPostGISへインポートします。
インポートする方法として`shp2psql`というPostGIS付属のコマンドを使う例がよく紹介されていますが、QGISで画面から簡単に行うこともできますので、今回はその方法を採用します。

### シェープファイルの読み込み
ダウンロードした国土数値情報の`.shp`ファイルをQGISにドラッグ＆ドロップすると、シェープファイルが読み込まれます。
![shp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/96545f1d-4fb1-d265-ee14-721513c33fb2.png)

### PostGISへの接続

左ペインの`ブラウザ`から`PostgreSQL`を右クリックし、`接続の新規作成`を選択します。
![set.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/4749150f-637a-7ad2-a19f-ac784c099188.png)

接続先のPostGISの情報を入力します。
![setp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/79465b07-647f-a875-025b-cd8a15957d8e.png)

### レイヤのインポート
メニューの`データベース`から`DBマネージャー`を起動し、PostGISのデータベースを選択して`レイヤ/ファイルのインポート`をクリックします。
![layer.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/9fc07c40-e46d-d214-0bd2-a7abecc64c08.png)

そして以下の画像の設定でインポートを実行します。
![imp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/e629ee2a-cbb9-9654-3bb4-804468e7d8ea.png)

`OK`をクリックするとしばらくフリーズしたような状態になりますが、私の環境では2分間くらい待つとインポートが完了しました。
![suc.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/b64d06fd-630a-fa3e-1c84-eb49e168678f.png)

## 都道府県・市区町村単位のデータ結合

pgAdminで確認すると、以下のようにきちんとテーブルが作成されています。
![pad.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/ddda0f95-1ec2-6ac7-6e6f-cddbd196b812.png)

今回はPostGISで結合の処理を行うので、pgAdmin等を使って以下のSQLを実行します。

### SQLで都道府県のポリゴンを結合

```sql:都道府県テーブルの作成
CREATE TABLE public."N03_pref" (
    n03_001 character varying(10) PRIMARY KEY,
    geom geometry(MULTIPOLYGON, 3857) NOT NULL
);
```

```sql:都道府県で結合したデータの投入
INSERT INTO public."N03_pref"
SELECT n03_001, ST_Multi(ST_Union(ST_SetSRID(geom, 3857)))
FROM public."N03-19_190101"
WHERE n03_001 IS NOT NULL
GROUP BY n03_001;
```


### SQLで市区町村のポリゴンを結合

```sql:市区町村テーブルの作成
CREATE TABLE public."N03_city" (
    n03_007 character varying(5) PRIMARY KEY,
    geom geometry(MULTIPOLYGON, 3857) NOT NULL
);
```

```sql:市区町村で結合したデータの投入
INSERT INTO public."N03_city"
SELECT n03_007, ST_Multi(ST_Union(ST_SetSRID(geom, 3857)))
FROM public."N03-19_190101"
WHERE n03_007 IS NOT NULL
GROUP BY n03_007;
```

### 結合時の注意点

上記では都道府県は`n03_001（都道府県名）`を、市区町村は`n03_007（行政区域コード）`を集約のキーにしています。
市区町村のキーを`n03_004（市区町村名）`にしなかったのは、重複の可能性があるからです。
また、利便性を考えると、都道府県を表す2桁のコードをここで生成しておくのがいいかもしれません。

## データの簡素化

QGISのメニューから`レイヤ`＞`データソースマネージャ`を選択し、PostgreSQLの中にある先ほど作成した都道府県のテーブルを選択して`追加`をクリックします。
![dbm.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/fc9ff9d1-29dd-12bc-1b12-eab7b9fa4f85.png)

すると、問題なく都道府県単位のポリゴンが表示されました。
![npref.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/a505437d-faf3-6d32-8d93-7e06b76c2db2.png)

### 結合したデータの問題点

Yellowfinからこのテーブルを直接参照することもできますし、[先日の記事](https://qiita.com/hadatuna/items/7a3cea8a77c882b436c5)の内容に沿ってCSVにエクスポートすることも可能です。
しかしこのデータには**詳細すぎて重い**という欠点があります。

たとえば東京湾を拡大してみると、以下のように細部まで形状がわかります。
![nbay.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/98b35e3b-1559-d133-ebe0-e7dd9c56e866.png)

ですが、正直なところ都道府県レベルでここまで詳細な形状は必要ありません。
むしろ描画の処理が重くなり、場合によってはOutOfMemoryなどのシステムエラーの原因になってしまいます。

そこで、**簡素化**というポリゴンの頂点を間引いてデータを軽くする処理を施していきます。

### QGISでジオメトリを簡素化

QGISのメニューから`ベクタ`＞`ジオメトリツール`＞`ジオメトリを簡素化`と進みます。
![gek.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/8eca1701-d49e-f563-d5af-ac6b47f46d7c.png)

簡素化のパラメーターを設定します。
`許容範囲`の値が小さくなると精度が高まり、ポリゴンの頂点が多くなって、データとしては重くなります。今回、都道府県の場合は`0.01`にしました。
![param.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/cf43ce15-d1b7-966d-b877-0c33c6a81250.png)

簡素化されたレイヤを確認すると、きちんと都道府県の形を保って表示されています。
![layerout.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/c4851145-25a5-98a8-914b-c145486013f8.png)

このレイヤをまたPostgreSQLへインポートするか、CSVにエクスポートすればYellowfinで使える都道府県ポリゴンのデータになります。
市区町村についても同様の操作で簡素化すればOKですが、許容範囲は`0.001`くらいに細かくするのがいいでしょう。

ここまでの手順で、Yellowfinの地図で都道府県や市区町村のポリゴンが使えるようになりました。
![yfs.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/4b8c8fe7-fbe2-6224-7427-0e4f3da23cf1.png)

### 簡素化したデータの問題点

上記の手順で簡素化した都道府県のレイヤを拡大してみると、領域間に隙間や重なりが生じています。
![kanso.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/429cf301-43a1-9434-150c-5ad61cfc32d8.png)

ですが、Yellowfinで地図を描画する程度なら支障ないレベルだと思われるので、今回は**この問題を無視**します。
この問題を解消するにはPostGISで**トポロジ**や**トポジオメトリ**というものを駆使する必要があるらしいのですが、この記事ではキーワードを紹介するだけにとどめ、また別の機会に検証したいと思います。

# Yellowfinで今すぐ活用できるデータ

ここまでの手順をそっくりそのまま真似していただければ、Yellowfinで使えるポリゴンデータが手に入るわけですが、**そもそも作業するのが面倒**だという方もいらっしゃるでしょうから、作成したデータをダウンロードできるようにしておきます。
なお、データ内容は記事執筆時点（2023年7月）のものです。

## 都道府県・市区町村ポリゴンのCSVファイル

上記の手順で作成したCSVファイルを以下に置いています。

* 都道府県： https://github.com/hadatuna/yfgis/raw/main/n03_pref.zip
* 市区町村： https://github.com/hadatuna/yfgis/raw/main/n03_city.zip

さらに細かな町丁目レベルのデータが必要な場合は、お手数ですが[この手順](https://qiita.com/hadatuna/items/532d08cd54a7e4ac9c0a#qgis%E3%81%A7%E3%82%B7%E3%82%A7%E3%83%BC%E3%83%97%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%82%92csv%E3%81%AB%E3%82%A8%E3%82%AF%E3%82%B9%E3%83%9D%E3%83%BC%E3%83%88%E3%81%97%E3%81%A6%E3%81%BF%E3%82%8B)に沿ってご自身で作成してください。（ファイルサイズが大きくなりすぎてGitHubには置けませんでした…）

## 行政区域コードのExcelファイル

ポリゴンと紐付けるために必要な都道府県や市区町村の名称及びコードの情報は、国土数値情報のサイトからダウンロード可能です。

* 行政区域コード: https://nlftp.mlit.go.jp/ksj/gml/codelist/AdminiBoundary_CD.xlsx

![行政区域データ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/116818/e40be4af-3f4a-370e-1dd6-79692012131b.png)

# 参考情報

## PostGIS環境構築
* [dockerでPostGISを入れたPostgreSQL環境構築](https://qiita.com/A-Kira/items/3339e902e7a8fca8fdf6)
* [Docker Compose で PostGIS を使った開発環境を構築する](https://blog.takeyuweb.co.jp/entry/2020/07/07/164510)
* [PostGIS の検証環境をさくっと用意する](https://zenn.dev/indigo13love/articles/f4e90115d0895e)

## 行政区域データのインポート
* [日本の行政区域データをPostgreSQLにインポートするメモ](https://qiita.com/kekekekenta/items/9b955b3075fa660e37e2)
* [国土交通省のGISデータをBigQueryにポリゴンとしてデータ投入する](https://qiita.com/mida12251141/items/38087821f86a66cc4193)
* [国土数値情報のデータをQGISで表示する手順を画像で解説：無料でGISを使ってみる](https://lemulus.me/trygis/ksj-introduction)
* [シェープファイルのデータをインポートしてみよう コマンドライン編](https://zenn.dev/boiledorange73/books/b1de0a18073af70946e0/viewer/import-cli)

## 都道府県・市区町村のポリゴンデータ作成
* [国土数値情報のデータから都道府県ポリゴンを作る](https://quzee.hatenablog.com/entry/2016/08/30/224311)
* [国土数値情報から行政区界を作成する](https://qiita.com/kshigeru/items/6fe5a797ebe9bfce57dd)
* [QGIS – 簡素化](http://taustation.com/qgis-geometry-simplification/)
* [トポジオメトリを使うと隙間や重なりの無いポリゴン簡略化が比較的簡単にできる](https://zenn.dev/boiledorange73/articles/0011-pgis-topogeomsimplify)
* [トポジオメトリで属性を持つ](https://zenn.dev/boiledorange73/books/pgis-topology-beginner/viewer/topogeom)
* [トポロジできれいな簡略化をやってみよう](https://zenn.dev/boiledorange73/books/pgis-topology-beginner/viewer/simplify)

## PostGIS全般
* [PostGISを入れるところからやってみよう](https://zenn.dev/boiledorange73/books/b1de0a18073af70946e0)
* [PostGIS入門](https://zenn.dev/boiledorange73/books/caea8d4c77dbba2e23a0)
* [PostGIS ラスタ 入門](https://zenn.dev/boiledorange73/books/pgis-raster-beginner)
* [PostGIS トポロジ 入門](https://zenn.dev/boiledorange73/books/pgis-topology-beginner)