古事連記帖

趣味のこと、技術的なこと、適当につらつら書きます。

オレオレ逆ジオコーディングサービスを作る その1

逆ジオコーディング、ザックリ言うと緯度経度といった座標から住所を割り出す仕組みを作ります。

もともと(ほぼ)自分のために作っている、くるまに乗るときに使うアプリで住所情報を割り出して表示する仕組みを用意していましたが、これまでYahoo! Open Local Platformのリバースジオコーダを使っていました。

developer.yahoo.co.jp

このAPIは住所の他、いくつかの道路の路線名や愛称が返ってきます。道路名はあまり他のサービスにない便利な機能でしたが、最近できた高速道路*1を取得できないなど、情報が古かったりしました。
また、外部サービスであるがゆえにリクエストリミットの存在、商用利用ができないこともあり、ちゃんと作ってストアに公開するなど第三者に利用してもらうといったことが難しそうだなあと思いつついた中でしたが、改めて自分で作ってみようと思い立ちました。


YOLPで使ってきたものをできるだけ踏襲するために、必要な情報は以下の通り。

  • 住所情報
  • 道路情報

YOLPではそれぞれに読み仮名も用意されていましたし、作っていたアプリでは音声合成で住所を読み上げる際に使っていましたが、最近登場したVOICEPEAKがすごくよく、事前収録して使う方向にしたことで読み仮名の重要性が落ちたのでなくても問題なくなりました。

www.ah-soft.com


そして使うフレームワークASP.NET Coreで、住所や道路の情報を持つデータベースはMariaDBにしました。MSSQLも考えましたが汎用性とかを考えてMySQL(とその互換のMariaDB)にしました。
あとは今後のお勉強のためにDockerコンテナを作って運用することにしました。

住所情報

座標から住所を取り出すためにはそのためのデータが必要です。行政が提供しているもののなかで大きく2つ存在します。

国土交通省の位置参照情報
nlftp.mlit.go.jp

政府統計の境界データ
www.e-stat.go.jp

このうち国交省のデータは、住所大字・町丁単位でその地点の代表が点で存在します。政府統計のデータは大字・町丁単位で、その地点がポリゴンとして存在しています。
ちょっとわかりにくいかもしれませんが、図にするとこんな感じ。

これの違いとしては、今の座標が「どれだけその点が近いか」と「その点が接触しているか」のどちらで情報を得るかとなります。その点から一定の距離に含まれるものを表示するのでよければ前者を、取りたい情報が図形として存在するので確実に取りたい場合は後者を選ぶことになりますが、今回は後者を選びました。

ただ、政府統計の方は5年に一度の国勢調査で得られたものをもとに調査年の3年後に更新されるのに対し、国交省の方は1年に一回更新されるといった違いがあります。従って、例えば最近東京都の中央防波堤の一部が区に割り当てられて誕生した「大田区令和島」「江東区海の森」は国交省データにはありますが政府統計データには含まれていません*2

使うデータは決まりましたが、政府統計データに含まれるデータはGISで使う前提のため、シェープファイルとして存在しています。そのままデータベースに突っ込むわけにもいかないので、変換ツールを使いました。
github.com
使い方については特に言及しませんが、シェープファイルを読ませてあげれば良い感じにしてくれます。

MySQLに流す分にはこれで終わりでもいいんですが、MariaDBに流す場合ちょっと加工が必要です。
作られるテーブル名がファイル名で返されるので、それを置き換えたい場合は置き換えます。

CREATE TABLE `h27ka23` (gid serial,
を
CREATE TABLE `Aichi` (gid serial,
に置き換えるとか

MariaDBでは流すとエラーになるものを消したり、追加したりします。

ALTER TABLE `h27ka23` ADD COLUMN geom MULTIPOLYGON SRID 0 ;
を
ALTER TABLE `h27ka23` ADD COLUMN geom MULTIPOLYGON REF_SYSTEM_ID=0;
または、テーブル名を変えている場合は
ALTER TABLE `Aichi` ADD COLUMN geom MULTIPOLYGON REF_SYSTEM_ID=0;
に置き換えたり
各レコードの末尾
, 0, 'axis-order=long-lat'));
を
, 0));
にしたり('axis-order=long-lat'を認識してくれませんでした)

クエリファイル結構大きいので耐えられるエディター*3を使って置換するのがいいと思います

こうしてできあがったクエリデータですが、まあまあ大きなデータになります。これだけでも充分いけるんですが、日本全国の町丁までを総当たりさせるとめちゃくちゃ重たくなるので、都道府県単位(または市区町村単位)のポリゴンを用意するなどして、ある程度のしぼり込みをするといいかもしれないなあと思って、自分のではそうしています。もうちょっといいしぼり込みの仕方とか、もう全部ひっくるめた方がいいとかあれば教えて欲しいです。

まあまあデカい。01は北海道です

政府統計データにはもう一つ難点があり、住所以外にも河川や海などにも情報が存在します。シェープファイルQGISなどのビューアーで見ると「羽田沖水面」「多摩川河川敷」などといった名前が含まれています。

赤丸で囲ったのが本来住所として存在しない(はず)のデータ

QGISに読ませて加工してもいいんですが、まあまあ量があったりするので今回はめんどくさくてそのままにしました。

住所取得の準備はここまでです

道路情報

道路情報は国交省の位置参照情報に2つのものが存在します。ひとつは高速道路時系列データと、もうひとつは道路データです。

nlftp.mlit.go.jp
nlftp.mlit.go.jp

どちらもライン情報として提供されています。従って、点と扱いは同じく座標から近いかどうかで判断する必要があります。また、扱える情報にも差があり、前者は毎年更新されていますが高速道路のみ、後者は都道府県道や国道、高速道路がありますが、平成7年の情報でかなり古いです。
また、お世辞にもいい情報であるとは言えず、地図と重ねてみるとジャンクション構造が再現されていなかったりと、使いたい用途としては不適格な部分が多くあります。

茶色いのが高速道路時系列データ。わかりやすく太くしてある。地図は国土地理院地図。
紫色のが道路データ。わかりやすく太くしてある。地図は国土地理院地図。

持っている文字情報も政令路線名で書かれていたりするため、「第一東海自動車道」が「東名高速道路」であるとマッピングデータを作るか修正するかしないといけませんでした。

その他にもいくつか探してみたりしましたが、自分が思うほしい情報が取れるものがありませんでした*4


そこで、今回は自力で手作業して道路情報を作ることにしました。地図や航空写真を見ながらQGISでポリゴンデータを書いていく地道な作業です。
全国の都道府県道や国道をすべて描くのは非常に時間が掛かり、毎年何かしら変化が起きる可能性があることから作成せず、変化の起きにくい(ほぼ新規開通のみである)高速道路に絞って作成することにしました。

QGISのインストール方法や、国土地理院地図・航空写真を取り込む方法は国交省がまとめてpdfとして公開しているものを参考にしてください
https://nlftp.mlit.go.jp/ksj/other/QGIS_manual.pdf

地図、航空写真を「レイヤ」に取り込んだら準備完了です。「新規シェープファイルレイヤ」をクリックしてシェープファイルを作る準備をします。

表示されたウインドウに必要な情報を入れていきます。ファイル名は好きなものを、文字コードはとりあえずUTF-8でも。ジオメトリタイプは「ポリゴン」を選びます。追加次元や座標系については今回何も触れないでおきます。
フィールドはポリゴンに付加する文字情報です。最低限必要になるのは、「道路名」くらいでしょうか。必要に応じて「ID」や、例えば高速道路ナンバリングを表示したければそのためのフィールドを用意したりするといいかもしれません。
僕が用意したフィールドは「ID」、「道路名」「道路の愛称(名所となっている橋の名前とか、補足してつけておきたい名前など)」「ナンバリング」「道路のある都道府県名」です。

ここから実際にポリゴンを作っていきます。まず「編集モード切替」で編集モードにしてから「ポリゴン地物を追加」を押して描画できるようにします。

そうするとキャンバスに表示されるカーソルが変わります。表示されている地図にある道路の境界をクリックしていきながらポリゴンを作っていきます。

クリックして点を打っていくと赤く面ができていきます。地図は国土地理院の航空写真と地図を重ねたもの。

うまくできたら、右クリックしてポリゴンデータに情報を付加します。ウインドウが表示されるので、そこに先ほど用意したフィールドに情報を入れていきます。ここでOKを押すとデータができます。キャンセルを押すと作ったポリゴンも含めて削除されてしまうので、間違えて押さないようにしてください。

ほしい情報を入れていく感じ

OKを押した後、キャンバスに赤く表示されていたものが確定してオレンジ色になります。「レイヤスタイル」で好きな色に変更ができるので、例えば不透明度を下げて地図が見えるようにするとよいと思います。

不透明度を下げた感じ

これをどんどん繰り返していき、道路情報を作っていきます。
まだまだ作りかけですが、僕が作った分としては以下の画像の通りです。

まず首都高をほぼ作り上げるところから始め、自分が通る頻度の高い(高そうな)道路を中心に郊外へ延ばすように作りました。GPSで取れる精度のことも考えて、中央分離帯がかなり広くない限り上下線の区別はせず、一方でジャンクションは細かく、ランプウェイは接続先の路線名を名乗るようにするなどしてみました。

伊勢原ジャンクション

フィールド情報は以下の通りにしていて、ポリゴンにナンバリングをつけられるので、例えば同じ道路名でも区間でナンバリングが違うところ*5も割と正確に出せるようになります。

東名の右ルート・左ルートとかは愛称として入れました

かなり地味すぎてマウスだとしんどくなってきたので、ペンタブレットを買いました。Intuos ProのSサイズです。ペンのボタンを全部無効化して、タブレット本体のボタンにキャンバスの拡大縮小や移動、右クリックのショートカットを割り当ててあげるとすごく効率よく描くことができます。

ある程度書き終えたら、「レイヤ」にある作成したシェープファイルのレイヤーを右クリックし、「エクスポート」から「新規ファイルに地物を保存」を選びます。

表示されたウインドウで「形式」は扱いやすいものを選びます。僕はMariaDBへ流し込むクエリに変換するコードを別途組んでいるので、扱いやすい「GeoJSON」を選んでいます。ファイル名に名前を入れ、下にある「保存されたファイルを地図に追加する」のチェックを外し「OK」を押します。

GeoJSONで出力したデータをクエリファイルに変換します。中身はザックリ言えばJSONなので、構造さえわかればあとは住所のクエリを参考にして変換ツールを組めるかと思います。
僕が用意したフィールド情報で作るとこんなデータができあがります。

gist.github.com
中身はView Rawで見れます

データは"features"配列の中に入ってくるので、その中に含まれるものをかいつまんで変換する感じです。

できあがったクエリは以下の感じです。住所データと似せて作ってあります。
gist.github.com

これでひとまず準備ができました。
準備だけですごく時間が掛かってしまったので、サーバーの実装については以下の別記事をご参照ください。

ayano.hateblo.jp

*1:東名高速道路はちらっと追加されたりはしましたが

*2:誕生は2020年のため、次に更新されるであろう2023年にも含まれるか怪しいところ

*3:Visual Studio Codeが割と頑張れる

*4:たぶん地図会社の法人向けAPIなどには存在していると思います。趣味で使うには非常にしんどい

*5:中央道や圏央道など