2014年1月21日

AWS で PostGIS を使ってみる

AWS で PostGIS を使ってみます。 PostGIS を使うことで、場所の情報、移動の情報、地域の情報などを扱いやすくなります。 PostGIS に保存された情報は、 QGIS などのデスクトップGISからも参照できますし、 GeoDjango などを利用したWebアプリケーションのバックエンドとして利用できます。

なお、自分でEC2インスタンスを管理しなくても「Amazon RDS for PostgreSQL」として提供されています。費用面を考えると、基本的な使い方は EC2 で学習して、きちんと使うときは RDS を使うと良いでしょう。

EC2 インスタンスを起動

まずは EC2 のインスタンスを起動します。 初めはテスト用なので micro にしておき、大きなデータを扱いたくなった場合は、ストレージを割り当ててインスタンスを作り直すのが良いでしょう。 EBS の割当方法は How to Install PostgreSQL with PostGIS on Amazon EC2 Linux の記事が参考になります。

  • リージョン:東京 (ap-northeast-1)
  • SSH鍵名:aws-tokyo-sandbox(事前に作成済であると仮定)
  • インスタンスタイプ:t1.micro
  • セキュリティグループ:SSHEnabledGroup (22番ポートでSSH接続を許可したグループ)

Webコンソールから起動できますが、APIを使うこともできます。 APIを使う場合は、Python で実装された awscli が便利です。これは pip でインストールできます。

$ pip install awscli
$ aws --version

環境変数は AWS_ACCESS_KEY_IDAWS_SECRET_ACCESS_KEY を設定します。 リージョンは AWS_DEFAULT_REGION で設定します。

$ export AWS_ACCESS_KEY_ID=<access_key>
$ export AWS_SECRET_ACCESS_KEY=<secret_key>
$ export AWS_DEFAULT_REGION=ap-northeast-1

これらは設定ファイルに記述しておくことも可能です。 ~/.aws/config か、環境変数 AWS_CONFIG_FILE で指定したファイルを読み込みます。 設定ファイルの書き方は awscli のページを参照してください。

それでは aws コマンドを使ってEC2インスタンスを起動します。 インスタンスのイメージIDは Amazon Linux AMI に記載されています。

$ aws ec2 run-instances \
  --image-id ami-3561fe34 --count 1 --instance-type t1.micro \
  --key-name aws-tokyo-sandbox --security-groups SSHEnabledGroup

しばらく待つとインスタンスが起動しますので、接続用のアドレスを確認します。

$ aws ec2 describe-instances \
  --filters "Name=instance-type,Values=t1.micro" | grep PublicDnsName
                  "PublicDnsName": "ec2-XXX-XXX-XXX-XXX.ap-northeast-1.compute.amazonaws.com",

SSHでログインできることを確認します。 XXX-XXX-XXX-XXX の部分は実際の値で読み替えてください。SSH鍵ファイルの置き場所も各自の設定で読み替えてください。

$ ssh -i ~/.ssh/aws-tokyo-sandbox.pem ec2-user@ec2-XXX-XXX-XXX-XXX.ap-northeast-1.compute.amazonaws.com

ログインしたら、 yum コマンドを使ってパッケージを最新版に更新しましょう。

$ sudo yum update -y

以下では、このインスタンスに PostGIS をインストールします。 この段階でも yum で PostgreSQL はインストールできますが、 マイナーバージョンがひとつ古いのと、PostGIS のインストールはできないので、勢いでインストールしないように気をつけましょう。

PostGIS をインストール

インスタンスに PostGIS/GEOS/Proj をインストールします。

  • PostgreSQL 9.3
  • PostGIS 2.1
  • GEOS 3.4.2 - PostGIS が依存
  • Proj 4.8.0 - PostGIS が依存

GEOS と Proj は GDAL (Geospatial Data Abstraction Library) をインストールすることでも利用可能になります。

手順概要:

  1. 標準 YUM レポジトリから postgresql を除外する。
  2. PostgreSQL の YUM レポジトリを使えるようにする。
  3. EPEL から依存モジュールを yum でインストールする。
  4. yum で PostgreSQL と PostGIS をインストールする。
  5. 起動設定、データディレクトリの初期化、接続テストを実施する。
  6. テスト用データベースを作成して拡張機能を有効にする。

Amazon Linux のデフォルトのレポジトリにも postgresql-server パッケージがあります。 依存関係がややこしくなりますので、標準のものからは postgresql を除外します。 /etc/yum.repos.d/amzn-main.repo の "[amzn-main]" ブロックと、 /etc/yum.repos.d/amzn-updates.repo の "[amzn-updates]" ブロックに以下の行を追記します。

exclude=postgresql*

管理者権限でファイルを編集します。

$ sudo vi /etc/yum.repos.d/amzn-main.repo
$ sudo vi /etc/yum.repos.d/amzn-updates.repo

次に PostgreSQL の yum レポジトリを使えるようにします。 パッケージが見えることも確認しておきましょう。

$ curl -O http://yum.postgresql.org/9.3/redhat/rhel-6-x86_64/pgdg-redhat93-9.3-1.noarch.rpm
$ sudo rpm -ivh pgdg-redhat93-9.3-1.noarch.rpm
$ yum list postgresql93*

Amazon Linux のデフォルトでは EPEL を読み込まない設定になっています。 yum--enablerepo オプションで EPEL も参照するようにして、 "geos" と "proj" パッケージをインストールしましょう。 ("gdal-libs" パッケージをインストールすると両方ともインストールされます。 PostGIS パッケージが "gdal-libs" に依存していますので、後でインストールされます。 この段階で "gdal" と "gdal-libs" をインストールしても良いでしょう。 "gdal" をインストールすると、 ogr2ogr などのコマンドが使えます。)

$ sudo yum --enablerepo epel install geos geos-devel -y
$ sudo yum --enablerepo epel install proj proj-devel proj-epsg -y

PostgreSQL 公式の YUM レポジトリでは、パッケージ名にバージョン番号が付きます。 PostgreSQL 9.3 は postgresql93-server、PostGIS は postgis21_93 になっています。 後で必要になるので、 postgresql93-contrib のインストールも忘れないでください。

$ sudo yum install postgresql93-server postgresql93-contrib -y
$ sudo yum install --enablerepo epel postgis2_93 postgis2_93_devel postgis2_93_utils -y

インストールしたコマンドが確認できることを確認します。

$ proj
$ psql --version

PostgreSQL サーバーを起動します。 データディレクトリの初期化、接続テストを実施しておきましょう。

$ sudo service postgresql-9.3 initdb
$ sudo ls -l /var/lib/pgsql/9.3    # ディレクトリが存在することを確認
$ sudo service postgresql-9.3 start
$ sudo service postgresql-9.3 status
$ sudo su - postgres -c "psql -c \"SELECT version();\""

テスト用データベースを作成して拡張機能を有効にします。 拡張機能はデータベースごとに有効にする必要がありますので、テンプレート化しておくと良いかもしれません。 なお、上記の手順で postgresql93-contrib をインストールしていないと fuzzystrmatch 拡張をインストールできません。 postgis_tiger_geocoderfuzzystrmatch に依存しています。 fuzzystrmatch は文字列比較の拡張で、レーベンシュタイン距離なども計算できます。

ここからは postgres ユーザーで作業します。 su コマンドでユーザーを切り替えましょう。

$ sudo su - postgres
$ createdb test
$ psql -d test
test=# CREATE EXTENSION postgis;
test=# CREATE EXTENSION postgis_topology;
test=# CREATE EXTENSION fuzzystrmatch;
test=# CREATE EXTENSION postgis_tiger_geocoder;
test=# SELECT postgis_full_version();
test=# \q

次に、作業用のデータベースユーザーを sampleuser という名前で作成します。 createuser コマンドを実行するとパスワードを入力するプロンプトが表示されますので、 任意のパスワードを設定しておきましょう。 (ユーザーは postgres のままであると想定しています。)

$ createuser -P -E -S sampleuser
Enter password for new role:
Enter it again:

ユーザーを追加したら pg_roles テーブルを確認しましょう。

$ psql -tx -c "SELECT * FROM pg_roles WHERE rolname = 'sampleuser';"
(出力は省略)

忘れずに test データベースへのアクセスを有効にします。

$ psql -c "GRANT ALL ON DATABASE test to sampleuser;"
$ psql -l
                                   List of databases
   Name    |  Owner   | Encoding |   Collate   |    Ctype    |    Access privileges
-----------+----------+----------+-------------+-------------+-------------------------
 postgres  | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 |
 template0 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres            +
           |          |          |             |             | postgres=CTc/postgres
 template1 | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =c/postgres            +
           |          |          |             |             | postgres=CTc/postgres
 test      | postgres | UTF8     | en_US.UTF-8 | en_US.UTF-8 | =Tc/postgres           +
           |          |          |             |             | postgres=CTc/postgres  +
           |          |          |             |             | sampleuser=CTc/postgres
(4 rows)

最後に、認証方式をOSに依存しないパスワード形式に変更します。 pg_hba.conf というファイルの以下の行を書き換えます。

# BEFORE
host    all             all             127.0.0.1/32            ident

# AFTER
host    all             all             127.0.0.1/32            password

管理者権限で編集し、変更を反映させます。

$ sudo vi /var/lib/pgsql/9.3/data/pg_hba.conf
$ sudo service postgresql-9.3 reload

これで EC2 の Amazon Linux に PostGIS をインストールできました。 postgres ユーザーから ec2-user に戻り、TCP/IP で "test" データベースに接続できることを確認しましょう。

$ psql -h localhost -d test -U sampleuser -W
Password for user sampleuser:
psql (9.3.2)
Type "help" for help.

test=> \d
               List of relations
 Schema |       Name        | Type  |  Owner
--------+-------------------+-------+----------
 public | geography_columns | view  | postgres
 public | geometry_columns  | view  | postgres
 public | raster_columns    | view  | postgres
 public | raster_overviews  | view  | postgres
 public | spatial_ref_sys   | table | postgres
(5 rows)

実用的に利用するには、設定ファイル (postgresql.conf) を編集してメモリ割当などを変更しておきましょう。 Tuning Your PostgreSQL Server/ja に目を通しておきましょう。

参考:

データの登録

適当な位置情報として、 Jリーグベストピッチ賞 を受賞したスタジアムの位置を登録してみましょう。 Wikipedia のページには、2001年度から2013年度までの受賞スタジアムのページへのリンクがあります。 そして、それぞれのページの右上には地理座標のリンクがあります。 (経年変化があるため、スタジアム名称は正式名称だけにしています)

たとえば、「横浜国際総合競技場」の緯度経度は (35.510281,139.606164) のように表現できます。 これをすべてのページについて見ていくと、以下の表を構成できます。 (手作業なので地味に大変。。。) これをタブ区切りテキストで stadiums_awarded.txt として保存します。 ヘッダー行を含まず、エンコーディングは UTF-8 とします。 また、受賞年度の列は配列として表現するために "{" と "}" で囲みます。

スタジアム 緯度 経度 受賞年度
横浜国際総合競技場 35.510281 139.606164 2001, 2010, 2011, 2012
札幌ドーム 43.015128 141.409772 2002
茨城県立カシマサッカースタジアム 35.991736 140.640533 2003
静岡市清水日本平運動公園球技場 34.984703 138.481386 2004, 2008, 2009, 2010, 2011, 2012, 2013
埼玉スタジアム2002 35.903108 139.717603 2005, 2009, 2013
平塚競技場 35.343539 139.341219 2006
新潟スタジアム 37.882614 139.059169 2007, 2009, 2012, 2013
さいたま市大宮公園サッカー場 35.916161 139.633383 2011
等々力陸上競技場 35.585536 139.652697 2011
大阪市長居球技場 34.615383 135.516656 2012, 2013

次に、PostgreSQL にテーブルを定義します。 先ほど作成した "test" データベースに以下の DDL を発行します。 テスト用に少量のデータなので、正規化せず配列型を使います。

test=> CREATE TABLE stadiums_awarded (
  name character varying(100) NOT NULL UNIQUE,
  latitude float NOT NULL,
  longitude float NOT NULL,
  years integer ARRAY NOT NULL
);

試しに1レコードだけ登録してみます。

test=> INSERT INTO stadiums_awarded VALUES ('横浜国際総合競技場', 35.510281, 139.606164, '{2001, 2010, 2011, 2012}');

問題なく登録できることを確認したら TRUNCATE 文で内容を破棄し、 COPYstadiums_awarded.txt の内容を登録します。 PostgreSQL の場合、 COPY の前にバックスラッシュを付けることでローカルのファイルを転送できます。 バックスラッシュを付けない場合は、EC2 インスタンスでの絶対パスを指定しましょう。 (バックスラッシュのルールは COPY -- TO でファイルに書き出す場合も同様です。)

test=> \COPY stadiums_awarded FROM 'stadiums_awarded.txt' WITH encoding 'UTF8';

続いて表示用のテーブルを作成します。 ここが PostGIS を使う部分で、フィールドの型に geometry を指定します。 "Point" は点を表すデータで、"4326" は測地系の EPSG コードです。GPS データにも使われる世界測地系 WGS84 のことです。 一覧などは EPSG Geodetic Parameter Registry のサイトで検索できます。

test=> CREATE TABLE stadiums (
  name character varying(100) NOT NULL UNIQUE,
  coord geometry(Point, 4326) NOT NULL,
  years integer ARRAY NOT NULL
);

最後に、 stadiums_awarded のデータを stadiums に変換します。 "ST_Point" 関数で緯度経度から地点情報を生成し、 "ST_SetSRID" 関数で測地系を設定します。

test=> INSERT INTO stadiums
  SELECT name, ST_SetSRID(ST_Point(longitude, latitude), 4326), years
  FROM stadiums_awarded;

試しに2013年に表彰されたスタジアムを検索し、西から順番に表示してみます。 "ST_X" 関数を使うことで、ジオメトリ型の点の経度を抽出できます。 緯度を抽出する場合は "ST_Y" を使います。 (配列の値から検索するので ANY を使います。記述順番に注意しましょう。)

test=> SELECT name FROM stadiums WHERE 2013 = ANY (years) ORDER BY ST_X(coord);
              name
--------------------------------
 大阪市長居球技場
 静岡市清水日本平運動公園球技場
 新潟スタジアム
 埼玉スタジアム2002
(4 rows)

今回は タブ区切りテキスト ⇒ 中間テーブル ⇒ 表示用テーブル という手順を取りましたが、 中間テーブルを経由せず、タブ区切りテキストから直接登録する方法もあります。 この場合、 Well-known text (WKT) を拡張した EWKT を使います。 詳しくは 4章. PostGISを使う のページを参照してください。 SRID 情報(測地系に関する情報)を付与する必要がありますので、 外部データソースから書き出されたファイルを扱う場合は注意しましょう。

QGISで表示

QGIS から PostGIS に接続してみます。 QGIS はオープンソースのデスクトップ型GISソフトウェアで、複数のプラットフォームに対応しています。 動作がもっさりしている感はありますが、多様なデータソースに対応しており、PostGIS コネクタも標準で備えています。

まずは本家の ダウンロードページ からプラットフォームに合ったアーカイブをダウンロードし、 インストールしておきましょう。 Windows の場合はインストーラーで簡単ですが、Mac の場合はやや複雑です。 KyngChaos のページから GDAL と FreeType をダウンロードしてインストールし、 ターミナルから pip を使って matplotlib をインストールします。 matplotlib をインストールするときに freetype-config を参照しますので、 /usr/X11/bin を環境変数 PATH に追加しておきましょう。 GDAL と matplotlib を準備できたら同じく KyngChaos のページから QGIS をダウンロードしてインストールします。

QGIS を起動すると、真っ白なページが表示されます。 まずは OpenLayers プラグインをインストールし、Google, Bing, Yahao, OpenStreetMap などのタイル地図を表示してみましょう。

メニューバーの「プラグイン」>「プラグインの管理とインストール」を選ぶとプラグインマネージャーが起動します。 「さらに検索する」を選び、「検索」ボックスに "OpenLayers" と打ち込むと OpenLayers プラグインが出てくるはずなので、これをインストールしてみましょう。 インストール後にメニューバーの「プラグイン」を選ぶと、今度はタイル地図を選べるようになっているはずです。 ライセンス要件を確認して、用途に合った地図を使いましょう。

次に PostGIS レイヤーを追加します。 メニューバーの「レイヤ」>「PostGISレイヤの追加」を選ぶと PostGIS テーブルを追加するウィザードが起動します。 「新規」ボタンを押して PostGIS 接続の情報を登録します。 ホスト、ポート、データベース、ユーザ名、パスワードが必要な情報です。

しかし、今回は PostGIS が AWS 上にあります。 イレギュラーな方法ではありますが、SSHのポートフォワーディングを使って、 localhost の 54321 番ポートと先ほど設定した PostgreSQL の 5432 番ポートを繋ぎましょう。 これでローカルの QGIS から AWS 上の PostGIS に接続できます。

$ ssh -L 54321:localhost:5432 ec2-user@${EC2_PUBLIC_DNS_NAME}

PostGIS レイヤーを重ねてスタイルを調整すると、以下のように表示できます。

QGIS には OpenLayers プラグイン以外にも様々なプラグインが用意されています。 以下のページにたくさんまとめられていますので、用途に合ったものを探してみると良いでしょう。

終わりに

AWS の EC2 に PostGIS をインストールし、簡単なデータを登録して、 デスクトップGIS である QGIS から参照してみました。

GIS ソフトウェアには依存関係が複雑なものが多いため、 ローカルマシンにインストールするとアップデートが大変なことも多々あります。 それどころか、バイナリが用意されていないプラットフォームではコンパイルできないことも多々あります。 AWS を使うと一時的に環境を用意して、用が済んだら気楽に廃棄できる点が嬉しいと思います。 テストデータをちょっと地図上で確認してみたい、そんな用途には便利な構成ではないでしょうか。

この記事では PostGIS の中でも geometry 型、"ST_Point" 関数、"ST_SetSRID" 関数、"ST_X" 関数しか利用していませんが、 PostGIS にはもっとたくさんの関数が用意されています。 こうした関数を利用することで、ある地点が任意の範囲に含まれているかどうか、という処理も簡単に実現できます。

今後、行政から位置情報に関するデータが公開されることも増えるでしょうから、 PostGIS や QGIS などを利用する場面が増えるかもしれませんね。

コメントを投稿