OpenRefineでOSMデータを扱ってみた

地理データには幾何形状に位置を付与して表現する部分があるため、GISソフトウェア以外での取り扱いは厳しいものがあります。しかしながらこの部分を切り離して考えると、残りは地理オブジェクトのIDや名前とそのプロパティという構造であり、表形式で取り扱えるようになります。OpenRefineを使ってOSMデータのプロパティ、即ちタグの登録状況をあれこれ見てみましょう。

# 1 OSMデータの抽出
[overpass turboを利用して松戸市のamenityを抽出](https://overpass-turbo.eu/s/TEg)します。
![](https://i.imgur.com/d3yrcD4.png)
抽出結果は「エクスポート」からGeoJSON形式でPCローカルに出力もしくはクリップボードにコピーします。
![](https://i.imgur.com/BASauXV.png)
# 2 OpenRefineでの利用
## 2.1 OpenRefineのインストールと言語設定
インストールは非常に簡単です。やり方は[こちらの記事](https://qiita.com/keita69sawada/items/5f47d1a8ff078e67ee3f)などを参照してください。実行ファイルを起動するとブラウザ上でOpenRefineが立ち上がります。
![](https://i.imgur.com/MPDjirX.png)
言語は初期値として英語が設定されているので、これ(Language settings)を日本語にすると使いやすくなります。

## 2.2 データのロード
上記1のGeoJSONファイルを読み込むか、クリップボードから貼り付けます。
![](https://i.imgur.com/Cr2jCl1.png)
「次へ」でデータ形式は自動的に解析されますが、違っていたら「JSON files」に切り換えます。
![](https://i.imgur.com/1PKn4PK.png)
OpenRefineのプロジェクトとして出力する範囲を、マウスカーソルを動かしながら色付きの矩形が"properties"の部分を囲むようにし、クリックで確定します。（元データの"geometry"の部分は外します）
![](https://i.imgur.com/eXcGE4Q.png)
カラム名に「properties - OSMキー」が自動的に設定され、松戸市の「amenity」一覧が表形式で表示され、人間の目で見やすくなりました。

表の構造として問題なさそうであれば、必要に応じて後から検索しやすくするためのタグを適当に付加して「プロジェクトを作成」。
![](https://i.imgur.com/jXJhFxe.png)
一覧しやすくするために1画面あたりの表示件数を50(これが最大)に変更します。データ件数が多い場合はややもっさり感があるかもしれませんが、何度もクリックしたりしないよう気をつけてください。
![](https://i.imgur.com/EpjM2ph.png)
## 2.3 データのチェック
OSMデータをCSV形式などに落とし込むことは他のツールなどを組み合わせることでも可能ですが、OpenRefineの特徴はデータのクレンジングツールである点です。雑多なデータを整形して統計などに使えるきれいなデータにするための機能が豊富に備わっているため、いったん取り込んでしまえば自分用のツールボックスとして自在に使えます。以下、主にOSMデータをチェックしながらその品質を改善するような使い方の例を挙げてみます。
### 2.3.1 OSMへのリンク
OSM上のオブジェクトを即座に参照できるようにリンクを張ります。
先頭列「properties - ＠id」にまさにOSMのオブジェクトIDがあり、その前に"https://www.openstreetmap.org/" をつけるだけでリンクできます。

カラム名左端の▼をクリックしてセル編集＞変換　と選びます。
![](https://i.imgur.com/KpkwYN2.png)
ここで[GREL](https://github.com/OpenRefine/OpenRefine/wiki/General-Refine-Expression-Language)というスクリプト言語を使って、そのセルの値(value変数で参照できます)に対する操作を式で記述します。(GREL以外にもPython/JythonやClojureも使えます)
![](https://i.imgur.com/GBuq1kz.png)
`"https://www.openstreetmap.org/"+value`

「+」は結合を表し、「value」はそのセルに元々あった値を指します。
プレビュー欄で操作前後の値の変化が確認できるので予期した結果にならない場合は式を修正します。問題なければ「OK」をクリックします。操作は現在表示されている行全てに対して縦方向に反映されます。

少し待つと結果が表示され、青くリンクが張られています。
![](https://i.imgur.com/VakX0cZ.png)
クリックしてそのOSMオブジェクトを参照できるようになりました。

### 2.3.2 amenityの値ごとの参照
OpenRefineにはデータの切り口を表すファセットという、高度なフィルタリング機能があります。ファセットで対象データ（行）を絞り込んで、セルやカラムに対する検索や整形操作を行うのが基本です。ファセットはいくつでも追加でき、AND条件として適用されます。今見えている一覧に対してだけ操作が反映されるので安心して使える反面、全体に対して操作したかった内容がファセットの解除を忘れて部分的にしか反映されないこともあるので注意が必要です。とはいえ間違って操作してもステップ単位で記録されており、「取り消す/やり直す」でいつでもどの時点へでも戻ったり進んだりできるので気楽に操作できます。

「properties - amenity」のカラム名左端▼をクリックしてファセット＞テキストファセット　と選びます。
![](https://i.imgur.com/4gaXvTe.png)
すると左サイドに「ファセット/フィルター」欄が表示され、amenityの値によるファセットが作成されます。ここで一覧に表示する対象を選ぶことができます。デフォルトは名前順で、件数が少ないとあまり比較する意味がないので件数順に並べるために「カウント」をクリックします。
![](https://i.imgur.com/wPzSZ90.png)
するといちばん多いのはparking(駐車場)であることが分かります。駐車場に特化したマッピングをする方もおられますが、個人的にはそこはあまり追いかけていないので（婉曲表現）その次のrestaurant(レストラン)を選んでみます。各文字テキストをクリックすることで選択（赤色）／解除（青色）できます。
![](https://i.imgur.com/FpdOMQY.png)
松戸市でマッピングされているレストランの一覧が表示されました。
横方向に多数のカラムがあるので、カラム名を短くしたり、不要なカラムを非表示にしたり（削除もできます）、順番を入れ替えてチェックしたいカラムを左側に寄せてみます。
![](https://i.imgur.com/xVrlurY.png)
![](https://i.imgur.com/tEucS6N.png)
![](https://i.imgur.com/5Mc7jHV.png)
こうして同一種類の地物を並べてみるとタグの改善点や次回マッピングの際のチェックポイントなどが分かりやすくなります。
![](https://i.imgur.com/5vvAv3q.png)
パッと見ただけでも以下のような点に気付きます。

* brand未設定のチェーン店がある。同じbrandで違う値がある。
* nameの()は取り除いてローマ字名欄か英語名欄へ。nameが未設定のものがある。
* branch未設定のチェーン店がある。
* cuisineが未設定。先頭大文字は良いんだっけ。
* opening_hoursが入っていない。書式が誤っている。

現地へ行かずとも地元の知識で明らかな値や[OSMWiki](https://wiki.openstreetmap.org/wiki/JA:Main_Page)で確認できる書式誤りは、いったん非表示にした「＠id」欄（カラム上はタイトルが消えている）をクリックして再表示し、そこからOSMのオブジェクトにジャンプしてすぐさま修正することもできます。
![](https://i.imgur.com/NH5fB4e.png)
![](https://i.imgur.com/axMvZTm.png)
### 2.3.3 amenityの分析
restaurantタグのオブジェクトは377件あることが分かりましたが、さらにその中を絞り込んで見ていくこともできます。
例えばnameタグが空のものは以下のように「空白ファセット」を追加して「true」を選ぶことで探すことができます。3件見つかりました。
![](https://i.imgur.com/eXxGCz8.png)
![](https://i.imgur.com/Wd2Tm4L.png)
知っている店舗なら名前を登録し、知らなければ次回マッピング時の確認項目にします。

タグ値の網羅度合いを確認することもできます。
nameファセットの条件をいったん外すために「true」を再度クリックして非選択状態に戻すか、左上のXでnameファセット自体を削除します。

次に、cuisineタグがどの程度セットされているか調べるためにカラム名左端▼から空白ファセットを追加します。
![](https://i.imgur.com/MpOKz1e.png)
するとfalse(値あり)が210件、true(値なし)が167件と表示され、約56%のレストランに料理が設定されていることが分かります。まずまずですね。
![](https://i.imgur.com/Pk00c4I.png)
## 2.4 外部APIからのデータ取得 - エンリッチ1
手元のデータを充実させることをエンリッチという言い方をします。OpenRefineはこのエンリッチにも使えるツールです。外部API利用例として、本末転倒気味ですが、オブジェクトIDをもとに[nominatimのAPIを利用して緯度経度を取得](https://wiki.openstreetmap.org/wiki/JA:Nominatim#.E4.BE.8B_3)してみます。

restaurantはやや件数が多いので22件のlibraryに切り換えてみます。
![](https://i.imgur.com/Gm7AORt.png)
id欄でカラム編集＞URLでカラムを追加　より、下記を入力して「OK」。
![](https://i.imgur.com/mjIbBFi.png)
新しいカラム名：座標
フェッチ間隔の遅延：1000
式：下記の[GREL](https://github.com/OpenRefine/OpenRefine/wiki/General-Refine-Expression-Language)記述を貼り付け

`"https://nominatim.openstreetmap.org/lookup?format=json&osm_ids="+toUppercase(value.partition(smartSplit(value,"/")[-2])[1][0])+value.partition(smartSplit(value,"/")[-1])[1]`

結果が「座標」にjson形式で返されました。これをパースして緯度経度を取り出します。
![](https://i.imgur.com/PKycAwe.png)
「座標」欄でセル編集＞変換　より、下記を入力して「OK」。
![](https://i.imgur.com/5HLF9LU.png)
`forEach(value.parseJson(),v,v.lat)[0]+", "+forEach(value.parseJson(),v,v.lon)[0]`

座標が取り出せました。
![](https://i.imgur.com/tUQK0rB.png)
最初のGeoJSONロード時に除外したものを改めて取得し直すという、無駄な処理ではありますが、APIを使ったエンリッチ方法のサンプルということでご理解ください。

## 2.5 照合によるデータ取得 - エンリッチ2
表記の揺れを統合することなどを「名寄せ」と言いますが、これは割と広い意味で使われるのでOpenRefineでは値を元に外部データベースのID(キー)に半自動的に一致させることに[照合(Reconciliation)](https://github.com/OpenRefine/OpenRefine/wiki/Reconciliation)という言葉を当てています。

先ほどの座標を取得した図書館のデータに、さらにWikidataから住所とISIL識別子を取得して追加してみます。

図書館のname欄で照合（名寄せ）＞照合（Reconcile）を開始 と選ぶと下記ウィンドウが開きます。
![](https://i.imgur.com/JoRD7VF.png)
初期状態ではWikidata(英語)の照合サービスしか登録されていないので「サービスを追加」ボタンをクリックして下記日本語用エンドポイントを設定します。

<del>`https://tools.wmflabs.org/openrefine-wikidata/ja/api`</del>
`https://wdreconcile.toolforge.org/ja/api`(2020/7/11変更)

登録されたら、そのサービスを選びます。
![](https://i.imgur.com/MMe6qrd.png)
「型を問わずに照合する」をチェックして「照合候補の最大数」に2をセットし、右下の「照合を開始」をクリックします。
![](https://i.imgur.com/uQcUmlj.png)
画面上部に黄色い矩形の背景に進捗率が表示され、100%になったら終わりです。
![](https://i.imgur.com/xE51gyo.png)
混み具合や件数により処理速度は変わりますので時間がかかる場合は気長にお待ち下さい。

照合が終わり、左サイドにname欄の照合結果が表示されました。
![](https://i.imgur.com/bKfKuzC.png)
matchedは名前が100%一致し、それに近い名前もなかったため、同じものだと自動的に判定されたものです。

noneは一致しなかったもので3件ありました。noneを選んで内容を確認します。

1件目は類似した名前が無く、Wikidataには無い図書館と思われますので「新しい項目を作成」をチェックします。（2020/5/6追記：「分館」と「分室」の違いでマッチしなかった模様。実際にはWikidataにありました）2,3件目は類似した名前があるために自動判定されませんでしたがわずかに名称が違うものは別の図書館なので、100%のものをチェックして選びます。

そして照合結果のファセットを削除すると照合結果が一致したものが青字でWikidataの項目にリンクされました。
![](https://i.imgur.com/WLLprqy.png)
この一致したものについてWikidataの情報を取得します。

カラム編集＞照合値からカラムを作る
![](https://i.imgur.com/8vOKCS3.png)
Add Property欄に追加したいWikidataのプロパティ名を入力します。正確でなくても近い候補を挙げてくれます。（どのようなプロパティがあるか調べたい場合は適当な図書館をWikidataで検索してみてください。例：[松戸市立図書館稔台分館](https://www.wikidata.org/wiki/Q54645336)）

「住所」と入力すると候補として「所在地」が2件表示されましたが、1件目が目的のものなのでこれを選びます。
![](https://i.imgur.com/NWwo5GL.png)
![](https://i.imgur.com/bL6pn71.png)
以下、同様に「ISIL識別子」を検索、設定して「OK」。
![](https://i.imgur.com/lIvTDVL.png)
照合が完了し、所在地とISIL識別子が取得できました。
![](https://i.imgur.com/qVqD6bE.png)
編集結果はこのままOpenRefine上にプロジェクトとして残すこともできますし、右上の「出力」ボタンから様々な形式で出力できます。
![](https://i.imgur.com/EswmHFP.png)

このようにOpenRefineにはWikidataに対する豊富な照合機能が作り込まれていますが、その[API仕様](https://github.com/OpenRefine/OpenRefine/wiki/Reconciliation-Service-API)はW3Cのコミュニティグループでもより詳細に定義されています。これに沿って、Wikidataだけでなく、VIAFやOpenCorporatesの企業情報など[様々なデータベースの照合用エンドポイント](https://github.com/OpenRefine/OpenRefine/wiki/Reconcilable-Data-Sources)が提供されており、いずれもOpenRefineでデータの動的なクレンジングやエンリッチに利用することができます。（提供機能の範囲はエンドポイントにより異なります）

# 3 最後に
様々なデータを手元に集めてローカルで使っている限りはあまり問題は起きませんが、そのデータを一般公開したり、元のデータベースに書き戻す場合などは知財、利用ライセンス、コミュニティの規範などを侵害しないように十分ご注意ください。