PostGISを扱う練習として、HerokuでRails 7アプリからPostGISを使えるように設定し、緯度経度を与えたときに、その場所を含む県名を取得して表示するようなサービスを作ってみました。
DockerでRails 7アプリを動かす
第一歩として、ローカル環境でRails 7のアプリを動かします。Docker DesktopなどをインストールしていてDockerのセットアップが済んでいることが前提です。
にあるコードをgit cloneし、READMEにある通り、セットアップをおこなって動かします。
http://localhost:3000 にアクセスすれば、以下のようにトップ画面が表示されます。
PostGISを有効にする
以下の3箇所を修正してローカルのDocker環境でPostGISを有効にします。
Gemfileに以下を追加します。
gem 'activerecord-postgis-adapter'
config/database.ymlのadapterをpostgisに変更します。
default: &default
adapter: postgis
docker-compose.ymlでdbホストのイメージを変更します。
db:
image: mdillon/postgis
またPostGISを有効にするためのマイグレーションファイルを作成します。
docker-compose exec web rails g migration AddPostgisExtensionToDatabase
で生成したマイグレーションファイルの中身は
class AddPostgisExtensionToDatabase < ActiveRecord::Migration[7.0]
def change
enable_extension 'postgis'
end
end
とします。
これらの変更をおこなった上で、再度docker-compose buildから環境の再構築をおこなうのですが、その前にdocker volume rmで、最初に作ってしまったデータベース用のdockerボリュームを削除しておきます。
docker volume rm rails7-on-docker_pg_data
これを行った上で、docker-compose buildから環境の再構築をおこないます。
再構築が終わり、再びdocker-compose upでコンテナを起動したら、
docker-compose exec db bash
の後、
psql -U postgres railsondocker_development
でpostgresに接続後、SELECT postgis_version(); を実行して、以下のようにpostgisのバージョン情報が出力されれば、PostGISの設定が有効となっています。
postgres=# SELECT postgis_version();
postgis_version
---------------------------------------
2.5 USE_GEOS=1 USE_PROJ=1 USE_STATS=1
(1 row)
Herokuで動かす
ここまでのコード変更は自分のGitHubリポジトリにコミット済みであることが前提で、Herokuで動かす手順は以下の通り。
GitHubリポジトリに接続し、
Automatic Deploysを有効にします。
config/database.ymlのproductionの部分を以下の通り書き換え、
production:
<<: *default
url: <%= ENV.fetch('DATABASE_URL', '').sub(/^postgres/, "postgis") %>
git pushすると、自動的にHerokuにデプロイされます。
最初、db:migrateが必要なので、
heroku run rails db:migrate -a <アプリ名>
を実行します。
heroku pg:psql -a <アプリ名>
でHerokuのDB(postgres)に接続し、ローカル環境のときと同様、SELECT postgis_version(); を実行して、PostGISの設定が有効となっていることを確認します。
緯度経度情報を保存するPointモデルを作成
緯度と経度、そして県名を保存できるPointモデルを操作するscaffoldを作成します。
docker-compose exec web rails g scaffold Point 'lat:decimal{10,2}' 'lng:decimal{10,2}' prefecture:string
migrationを実行します。
docker-compose exec web rails db:migrate
県のポリゴンデータ
県のポリゴンデータを作成するための準備として市区町村のポリゴンデータをPostGISに取り込みます。
国土交通省の国土数値情報ダウンロードのページから「全国」のデータをダウンロードし解凍します。(令和3年のデータファイルはN03-20210101_GML.zip)
解凍したフォルダN03-20210101_GMLはアプリ直下のdbフォルダの下に置いておきます。
docker-compose.ymlを以下のように編集し、docker-compose buildで、コンテナを再構築し、上記フォルダにコンテナからアクセスできるようにしておきます。
db:
image: mdillon/postgis
env_file:
- .env/development/database
volumes:
- .:/usr/src/app <-- この行を追加
- db_data:/var/lib/postgresql/data
docker-compose exec db bash
にdbホストに接続し、シェープファイルからSQLファイルを生成するためのコマンドshp2pgsqlを使って
shp2pgsql -c N03-20210101_GML\\N03-21_210101.shp shapes > shapes.sql
で全国の市区町村のポリゴンデータをPostGISに取り込むためのSQLファイルを作成します。
次に
psql -U postgres railsondocker_development < shapes.sql
を実行して、ポリゴンデータをshapesテーブルに取り込みます。
次に
psql -U postgres railsondocker_development
でpostgres接続後、
CREATE TABLE prefectures AS
SELECT
n03_001 AS pref_name,
ST_Multi(ST_Union(geom)) AS geom
FROM shapes
GROUP BY n03_001;
を実行して、各都道府県に属する市区町村のポリゴンデータを結合して、都道府県ごとのポリゴンデータをprefecturesテーブルに抽出します。
できあがったprefecturesテーブルをsqlファイルにエクスポートします。
pg_dump -U postgres --table prefectures railsondocker_development > prefectures.sql
Heroku 上では、このprefectures.sqlだけを読み込みます。
heroku pg:psql -a <アプリ名> < prefectures.sql
ポリゴンデータから緯度経度が所属する都道府県を導く
緯度、経度を入力し、Pointのデータを保存したときに、どの都道府県のポリゴンデータにその緯度・経度が含まれるかを調べ、その結果をprefectureにセットします。
以下の通り、Pointモデルのbefore_saveにその処理を定義します。
class Point < ApplicationRecord
before_save :set_prefecture
private
def set_prefecture
# 県名を取得
res = ActiveRecord::Base.connection.select_all("SELECT pref_name FROM prefectures WHERE ST_Within(ST_GeomFromText('POINT(#{lng} #{lat})'), geom);")
self.prefecture = res.first["pref_name"]
end
end
Pointの入力フォームでprefectureを入力する必要はないので、その部分を削除します。
http://localhost:3000/points/new に削除し、たとえば以下のように緯度、経度を入力します。
Create Pointボタンをクリックすると、以下のようにその緯度、経度が属する都道府県が保存され、表示されます。
このとき、Geocodingなどの外部APIにアクセスしていないので、高速に検索できています。
参考リンク
2022/03/01 11:39:00