日本の住所の正規化に本気で取り組んでみたら大変すぎて鼻血が出た。

先日、弊社では Community Geocoder というサービスをリリースしました。

* [Community Geocoder](https://community-geocoder.geolonia.com/)
* [紹介記事](https://blog.geolonia.com/2020/06/01/community-geocoder.html)

さて、このジオコーダーは、住所を正規化してそれを「大字町丁目コード」という12桁の数字に変換し、そのコードをファイル名として GitHub ページ上に大量においた JSON ファイルにアクセスして緯度経度を取得するということをやっています。

つまり、住所の正規化からコードに変換する部分がとても重要で、そもそも正規化に失敗してしまうとどうしようもないという仕様なんです。

さいわい先日経産省が公開した [IMI コンポーネントツール](https://blog.geolonia.com/2020/05/29/imi-tools.html) である程度のことをやってくれるのですが（というかそうであることを期待したのですが）、いろいろ調べ始めると住所という仕組みはほんとに複雑で、Facebook で絡んでくださった @hfu さんいわくまさに「自然言語処理そのもの」であることに気づきました。

というわけで前置きが長くなりましたが、ここ数日で得た知見を共有させていただきます。

## まず IMI コンポーネントツールのソースをみる

まず、住所以前にユーザーの入力をそれなりにバリデーションする必要があります。

スペース云々とか XSS うんぬんとかは当然それなりにやるとして、全角半角の統一とかそこまでは想定していましたが、IMI コンポーネントツールのソースをみたら一回目の鼻血が出ました。

![](https://www.evernote.com/l/ABWYX7YPc3dNG4dxOjxJqJ2IMma4WaCQaHIB/image.png)

なんとなくミルクボーイの漫才が聞こえてきた気がしたのは私だけでしょうか？

あと漢数字のゼロって `〇` なんですね。。。よく考えたら漢数字でゼロって書いたことなかった。

さらに、数字でやっかいなのは、国土数値情報の中の `丁目` の数字が漢数字になっていて漢数字と数字の変換が必要になります。

## 都道府県から市区町村までを正規化

これは意外と簡単です。IMI コンポーネントツールを信じるだけですね。

実際のソースをみると JSON の辞書を内蔵していて、それと照合して一度都道府県コード及び[全国地方公共団体コード](https://www.soumu.go.jp/denshijiti/code.html)に一旦変換していました。

タイポには対応できないですが、`八` を `ハ` と入力してしまう人でも、これぐらいは正しく入力してくれると期待しましょうw

## 町字

国会議事堂の住所 `東京都千代田区永田町１丁目７−１` を例にするところの永田町から後ろにあたるのが大字です。

ここで、最初のボスキャラが登場します。

皆さんご存知の京都の住所の「通り名」ですね。この中でも最強のボスキャラが以下の住所ではないかなと。

```
京都府京都市東山区大和大路通正面下る大和大路2
```

この住所から「大字町丁目コード」を取得するには、`京都府/京都市/東山区/大和大路/二丁目` という住所に正規化する必要があります。

ところがこの住所でやっかいなのは大字にあたる `大和大路` という文字列が 2 回登場するため、それに続く小字（丁目以降）との境界を判別する方法がとても困難です。

つまり対処を間違えると `通正面下る大和大路2` が小字として拾われてしまうということです。

これは国土数値情報のデータから取り出した大字リストを `lastIndexOf` を叩きながらぶん回すことで、なんとか攻略できました。

## 丁目以下

京都の「通り名」を倒した後、完全に油断していましたが敵はこれだけではありませんでした。

京都ほどのボスキャラではないものの、そこそこ攻撃力があるモンスターたちが全国各地にたくさんいることがわかりました。

特に `丁目` がやばい。

* 一番大きい `丁目` は `万丁目`。まさか `10000 丁目` とか入力する人いないですよね？？？
* `壱丁目`というパターンもある。これを一丁目とか書かれたら判別不能。。。
* 大阪市中央区上町は `丁目` のかわりに `A番` とかはいる。まさかのアルファベット！大阪に10年以上住んでたけど知らんかった！
* `丁目` の後に 「n番」 とか来ると思ったら大間違い。「三丁目大横」というパターンもある。
* 堺市は `丁目` じゃなくて `丁` ですから！うちの嫁さんが販売員の仕事をしてた時によく間違えたらしい。
* 札幌市は `丁目` の前に1条とかある。
* 「西野町七丁目北町」という強敵。まさかの町2つ。つまり `町` という文字以降が大字とかはダメです。
* 石川県では `丁目` ではなくイロハ、甲乙丙、十二支を使う。「七尾市藤橋町亥45番地1」みたいな。つまり大字と小字を数字を使って切るとかも許してくれない。
* ほかにも「八幡平市大更第35地割62番地」とか「東京都青ヶ島無番地」というパターンもある。

とうわけでまだまだ先は長いのですがせっせと攻略しています。

## お願い

というわけで、冒頭で紹介した [Community Geocoder](https://community-geocoder.geolonia.com/) では、入力いただいた住所をうまく認識できなかった場合に以下のように Issue で報告できるようになっています。

（自動で収集してもいいのですが、その場合オープンソース化できるコードとできないコードが混在してしまうのはめんどくさいなと思ってやめました。）

[https://github.com/geolonia/community-geocoder/issues](https://github.com/geolonia/community-geocoder/issues)

![](https://www.evernote.com/l/ABXU2uHjSotKCo1pxDj94af8A6mmsPdsQboB/image.png)

すべてに対処できるわけではないと思いますが、ご報告いただいた内容をもとに住所の正規化エンジンを改良しオープンソースで公開していきたいと思っています。

ぜひ、鼻血が出そうな住所のご報告をお待ちしています。

## 参考

@hfu さん、 @nyampire さん、いろいろ教えていただいてありがとうございます！

* [ふれっしゅのーと](http://fffw2.hateblo.jp/) - 地図という仕組みそのものについて勉強になりました。
* [OSMの地名・行政単位・郵便住所の対応](https://docs.google.com/spreadsheets/d/1eAE72mjCLoJVGZo5qRhCYK22UxVQ8bpbQSU9ZLHq40o/edit)

## このジオコーダーを作った動機（6/6)

このあたり説明不足だったのであらためて説明させていただきますが、たとえば多くの方が利用する Google の Geocoding API は、規約上 Google Map 以外の地図で利用することを許可されていません。

> (e) No Use With Non-Google Maps. To avoid quality issues and/or brand confusion, Customer will not use the Google Maps Core Services with or near a non-Google Map in a Customer Application. For example, Customer will not (i) display or use Places content on a non-Google map, (ii) display Street View imagery and non-Google maps on the same screen, or (iii) link a Google Map to non-Google Maps content or a non-Google map.

https://cloud.google.com/maps-platform/terms

一方で、日本には、弊社はもちろんのこと、最近上陸した Mapbox さんなどのいくつかの選択肢が増えてきました。

そこで、特定の地図ベンダーにロックインされることなく、自由に使えて安定運用が可能なジオコーダーがあってもよいのではないかと思った次第です。

この Community Geocoder は GitHub でフォークして、GitHub ページの設定を行えば即座に自社内のソリューションとしても使うことができ、ビルドするためのコードや JavaScript API も内蔵していますので、精度が低いとはいえライセンスをきにすることなく緯度経度をプロットするためのツールとして、有用であると思っています。

## 追記 & お礼 (6/6)

皆さんのおかげでいくつかのパターンに絞れてきました！

* 数字の表記ゆれは直せそうです。
* bug というラベルが付いているものは、まだはっきりと原因がわかってないですが直せそうです。
* 京都の同じ町名で「通り名」で区別してる問題はやはりラスボスになるかもですね。

すべての Issue を完全に解決することはできないかもですが、積み重ねて徐々に育てていけるとは思います。
ほんとうにありがとうございます！

参考までに、対処済みの住所のパターンのテストコードはこちらです。

https://github.com/geolonia/community-geocoder/blob/master/test/find.spec.js

## 2021/4/17 追記

この記事をきっかけにたくさんのフィードバックを頂きまして、それらをもとに住所を正規化するための NPM モジュールを公開しました。

* https://github.com/geolonia/normalize-japanese-addresses
* https://blog.geolonia.com/2021/03/28/normalize-japanese-addresses.html

名寄せを目的とした正規化の場合、商用の正規化エンジンと比較しても遜色のない精度で正規化ができると思います。