こんにちは。ロコガイドのバックエンドエンジニアの箕輪です。会社ではフットサルサークルにも参加しています。 ずっと延期されていましたが先日ついに活動再開しました。約3年振りです。 まったく体は動かず終わった後も1週間近く筋肉痛になりましたが、それでもすごく楽しかったです。少しずついろんな活動ができるようになってきましたね。
タイトルにあるリバースジオコーディングとは緯度経度といった座標から住所や地域名などのテキスト情報を得ることを指します。ジオコーディングと逆方向の変換(住所から緯度経度)になるため逆ジオコーディングとも呼ばれています。今回はその環境を自前で構築した内容をご紹介します。
背景
自前で用意するに至った背景は2つあります。
1つ目は金額的コスト観点です。
リバースジオコーディングはGoogle Map Geocoding ServiceやAmazon Location Serviceを使うことで実現することもできましたが
- 分析での利用を主目的としており、いろんな示唆を得るために繰り返し利用したい
- トクバイ利用者数はMAU1600万を超え、1日分のアクセスログの解析でもそれなりのデータ量に
- 特定の人だけでなく社内の誰もがいつでも簡単に使えるようにしたい
ということで、試算するとなかなかのコストになってしまうことがわかりました。 他にもAPIベースでリバースジオコーディングするには1回の分析に時間がかかりすぎてしまうということで、別の仕組みを考える必要がありました。
2つ目がリバースジオコーディング環境をつくる過程で得られる町丁目レベルのエリア情報です。 トクバイで取り扱っているチラシは元は紙の媒体であり、新聞に折り込まれるなどして各家庭に届きます。 そして新聞折込チラシの世界では市区町村よりもっと細かな町丁目レベルでのコントロールが可能となっています。
現在のトクバイでは市区町村や駅、スポットなどからお店やお特な情報を探すことができますが、町丁目レベルまではまだ実現できていません。 今後より便利な形でユーザーに情報を届けていくには、折込チラシと同等もしくはもっと細かな単位で情報配信をコントロールできるようにする必要があります。サービスへの実践導入はまだ先になりますが、最初の段階としてまずは町丁目エリアを整備していく必要がありました。
前提
手順の前にいくつか前提についてご説明します。
ベースとなるデータベース環境はすでにトクバイの分析基盤として利用しているAmazon Redshiftを利用しました。 新しく一から環境を用意する時間と手間が省けるだけでなく、アクセスログをはじめサービスで利用しているマスタデータもすでに参照できるため、リバースジオコーディングを行った後の分析においても活用シーンが広がります。 リバースジオコーディングに必要なGEOMETRY型のデータ型や様々な空間関数はAmazon Redshiftでは2019年よりサポートされています。
またデータベースの他に座標から地理的なテキストに変換するための境界データマスタが必要です。背景にあった町丁目レベルのデータです。 こちらは政府統計ポータルサイトe-Statからダウンロードすることができます。 e-Stat上で公開されているデータは利用ルールの範囲内であれば誰でも自由に利用することができます。 他にも数多くの統計情報が公開されていますので、一通り眺めてみると面白いデータや新たな発見があるかもしれません。
利用できるデータ形式は複数あり、シェープファイル、GML、KML形式でダウンロードが可能です。 今回はAmazon Redshiftで直接取り込めるシェープファイルを使いました。 その他取り込むための一時的なデータ置き場としてAmazon S3を利用しています。
作業手順
いよいよ実際にリバースジオコーディング環境をつくっていきます。 作業ステップは以下になります。
- e-StatからシェープファイルのダウンロードとAmazon S3へのアップロード
- 取り込み先テーブルの作成
- データ取り込み
順に説明していきます。
1. e-StatからシェープファイルのダウンロードとAmazon S3へのアップロード
多くのデータが公開されていますので迷わないよう、今回利用するデータの辿り方を記載しておきます。
地図で見る統計 > 境界データダウンロード > 小地域 > 国勢調査 > 2020年 小地域(町丁・字等別)(JGD2000) > 世界測地系緯度経度・Shapefile
こちらの境界データは都道府県単位で用意されています。 ブラウザから一つ一つダウンロードすることもできますが、ここはエンジニアらしくシェルスクリプトで楽をします。 ダウンロードに加えZIPファイルの解凍、Amazon S3へのアップロードまでを同時に行います。
スクリプトの注意事項
- スクリプト内のAmazon S3のバケット名やシェープファイルの場所、クレデンシャルはご利用の環境に合わせて適宜変更してください
- S3へのアップロードには別途AWS CLI(コマンドラインインターフェース)が必要になります
- AWS CLIの具体的な使い方については公式サイトをご覧ください
シェルスクリプトファイルの作成
まずはじめに作業用ディレクトリを用意します。
$ mkdir work_dir $ cd work_dir
中身のスクリプトは下記のコマンドで生成していきます。
$ echo '#!/bin/sh' > download.sh # 各都道府県のダウンロードURLを組み立ててwgetコマンドに渡します # アクセス間隔をあけるためにsleepを間に入れています $ for n in `seq -w 1 47`; do echo "wget -O A002005212020DDSWC$n.zip 'https://www.e-stat.go.jp/gis/statmap-search/data?dlserveyId=A002005212020&code=$n&coordSys=1&format=shape&downloadType=5&datum=2000'"; echo "unzip -d r2ka$n A002005212020DDSWC$n.zip"; echo "Amazon S3 cp --recursive r2ka$n/ s3://sample_buckets/town_boundaries/shape/"; echo "sleep 3" done >> download.sh
最後にスクリプトを実行し、ダウンロードとS3へのアップロードを行います。 完了まで数分かかります。
$ chmod +x download.sh $ ./download.sh
2. 取り込み先テーブルの作成
次にAmazon Redshift上に取り込み先のテーブルを用意します。以下例ではテーブル名をtown_boundaries
としています。
またテーブルの各項目の詳細については、e-Statにデータベース定義書がありますのでそちらを参照してください。
境界データの中には人口や世帯数も含まれているのでそれらを利用した分析も可能です。
テーブル作成クエリサンプル
CREATE TABLE town_boundaries ( wkb_geometry GEOMETRY ,KEY_CODE BIGINT ,PREF BIGINT ,CITY BIGINT ,S_AREA VARCHAR ,PREF_NAME VARCHAR ,CITY_NAME VARCHAR ,S_NAME VARCHAR ,KIGO_E VARCHAR ,HCODE VARCHAR ,AREA FLOAT ,PERIMETER FLOAT ,R2KAxx_ BIGINT ,R2KAxx_ID BIGINT ,KIHON1 VARCHAR ,DUMMY1 VARCHAR ,KIHON2 VARCHAR ,KEYCODE1 VARCHAR ,KEYCODE2 VARCHAR ,AREA_MAX_F VARCHAR ,KIGO_D VARCHAR ,N_KEN VARCHAR ,N_CITY VARCHAR ,KIGO_I VARCHAR ,KBSUM BIGINT ,JINKO BIGINT ,SETAI BIGINT ,X_CODE FLOAT ,Y_CODE FLOAT ,KCODE1 VARCHAR );
RedshiftではGEOMETRY型にはソートキーや分散キーは指定できません。 GEOMETRY型に対してORDER BY、GROUP BY、DISTINCT句なども使用できません。 その他公式サイトに制約事項の記載がありますので参考ください。
3. データ取り込み
最後のステップになります。COPYコマンドを利用して作成したテーブルにデータを取り込みます。
COPY town_boundaries FROM 's3://{アップロードしたバケット名}/town_boundaris/shape/h27ka01.shp' FORMAT SHAPEFILE CREDENTIALS 'aws_iam_role=arn:aws:iam::xxxxxxxxxxxx:role/redshift-role' xxxxxxxxxxxx
上記は北海道のシェープファイルを読み込む例です。他都道府県についてもファイル名を変更して同様にCOPYコマンドを実行します。また、Amazon S3のデータを参照するにはバケットへのLISTアクセスとバケットオブジェクトへのGETアクセス権限が必要になりますので、事前に設定してください。もしクエリ実行時にエラーが発生した場合は、STL_LOAD_ERRORSテーブルにエラー内容が出力されますので、そちらで内容を確認してみてください。
以上で作業は終了です。簡単ですね。
リバースジオコーディングしてみる
では取り込んだデータを元に実際にリバースジオコーディングしてみます。 サンプルとしてロコガイドオフィスのある三田国際ビルを使いました、緯度経度はGoogleマップから取得しています。
ST_POINT関数で緯度経度文字列(経度が先、緯度が後)をGEOMETRY型に変える必要があります。 しかし実際にクエリを投げてみるとエラーが出てしまいました。
Arguments to within must have the same SRID value: 0, 4612`
与えられた引数と内部の値とが同じSRIDでなければならないと言われています。
ここで少し用語の整理をします。
- SRID(Spatial Reference System Identifier)
- 空間座標系(測地系、座標系、地図投影法などの組み合わせ)を識別するために割り振られている固有なID
- 下記EPSGで定義されたものを始めとして、さまざまな標準SRIDが認められている
- EPSG(European Petroleum Survey Group)
- 例) EPSG:4612
- 本団体によって策定、規格化されたコード体系
- 現在はIOGP(International Association of Oil & Gas Producers)に活動が引き継がれた
- 例) EPSG:4612
今回e-Statから取り込みを行ったデータは JGD2000(日本測地系2000) → EPSG:4612 → SRID値も同じく4612 となります。
一方、Googleマップ内部ではWebメルカトル(Pseudo-Mercator)を採用しており、EPSG:3857として定義されています。 ただしこれはGoogleが独自に編み出した(現在はいろんなサービスが追従)空間座標系であり、米国が構築・維持している代表的な世界測地系であるWGS84(EPSG:4326)とは異なります。ですがそこはちゃんと配慮されており、マップ上で指定した地点の緯度経度を取得する場合には、Google側で自動でWGS84に補正してくれています。
わかりやすく内容をまとめてくださっている方がいますので興味のある方はぜひこちらを読んでみてください。 「Web メルカトル(ウェブメルカトル)」の由来と真相 - WINGFIELD since1981 https://www.wingfield.gr.jp/archives/4294
ということでSRIDを揃えて、改めてリバースジオコーディングをおこなってみます。
無事、緯度経度から町丁レベルの地域名を取得することができました。
補足
- SQLの確認ではDBeaverという無料のDBクライアントツールを利用しています
図のようにGEOMETRY型を選択するとOpenStreetMapを同時表示してくれるため、実際にエリアを確認しながら分析作業ができるのでおすすめです。
- 境界の取り扱いについて
SQL例ではST_COVERS関数を用いましたが、これはPOINTが境界線上にあった場合でも結果に含めるといった指定になります。つまり、ちょうど2つの町丁エリアの境界線上にPOINTが存在した場合は2つともヒットすることになります。 こういった場合にどちらのエリアとするのかは分析目的にあわせて決める必要があります。また境界線上はヒットさせたくない場合はST_CONTAINS関数を用います。他にもST_INTERSECTSという似たような関数もありますので分析内容に応じて使い分けるてください。
おわりに
いかがでしたでしょうか?もしAmazon Redshiftを使われていれば1日でリバースジオコーディング環境をつくることができます。なおMySQL8.0でも空間分析関数がサポートされていますので同じような環境をつくることができます。参考にしてみてください。 ロコガイドでは分析が好きな方、プロダクト開発が好きな方、プログラムを書くのが好きな方、一緒に働く仲間を募集しています。ご応募お待ちしています。