自動でアクセス情報を入力する方法について

[プロダーツプレイヤーが本職](http://www.target-darts.jp/shopdetail/000000000022/)の[徐　聖博](https://www.xushengbo.com/)です。

様々なサービスによく出てくるあのアクセス情報。
食べログ様など、飲食店の情報を表示するサービスにももちろんある、いつものあれです。
<img width="677" alt="スクリーンショット 2016-07-30 11.14.37.png" src="https://qiita-image-store.s3.amazonaws.com/0/69406/97d79c66-4c0e-94ea-6445-5dc5fd5e701e.png">
※[食べログ様](http://tabelog.com/shizuoka/A2202/A220201/22029041/)のスクショ

でも、実際に数十万件にも及ぶデータ(今回は施設と呼ぶ)全てに対して、1件1件入力していくのは猫の手を借りても一苦労です。
今回は、そんなアクセスデータをGoogleAPIを用いて自動で生成する方法を紹介します。


## まずはなんと言っても緯度経度
アクセス情報を自動で生成する上で最も肝となるのが、駅データと施設への緯度経度情報です。

緯度経度情報は、11桁の数字なので、RubyOnRailsで言ったら<b>decimal型</b>を使いましょう。
間違って、float型を使うと後でおかしなアクセス情報になります。

### 駅の情報を取得
自動アクセスは、基本的には「どこの駅から徒歩（or車）で◯◯分」という情報が作成されます。
そのため、まずは駅のデータが必要です。
駅のデータは、[駅データ.jp](http://www.ekidata.jp/)から会員登録し、無料データダウンロードできる範囲のデータで正確な緯度経度情報が取得できます。

### 施設の緯度経度情報を挿入
もともと緯度経度情報が挿入されている場合は問題ないのですが、多くの場合そんなものを入力させる登録フォームはありません。
そのため、<b>住所→緯度経度情報</b>へと変換する必要があります。

これは、Google様の[Google Maps Geocording API](https://developers.google.com/maps/documentation/geocoding/intro?hl=ja)を使って変換しましょう。（これが１番精度よかった）

```
{
   "results" : [
      {
....
....
....
         "formatted_address" : "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
         "geometry" : {
            "location" : {
               "lat" : 37.4224764,
               "lng" : -122.0842499
            },
            "location_type" : "ROOFTOP",
            "viewport" : {
               "northeast" : {
                  "lat" : 37.4238253802915,
                  "lng" : -122.0829009197085
               },
               "southwest" : {
                  "lat" : 37.4211274197085,
                  "lng" : -122.0855988802915
               }
            }
         },
         "place_id" : "ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
         "types" : [ "street_address" ]
      }
   ],
   "status" : "OK"
}
```

こんなデータが返ってくるので、うまく緯度経度の情報だけ抜き出してあげましょう。

## 次に、施設から近い駅を検索
rubyには``geokit-rails``とgemがあって、このgemによって半径◯◯kmの施設を取得することができる。
下記のような設定をしてあげる。

```
  acts_as_mappable(default_units: :kms,
                   default_formula: :sphere,
                   distance_field_name: :distance,
                   lat_column_name: :latitude,
                   lng_column_name: :longitude)
```

そして、大事なのが<b>対象となる施設を駅として扱うこと</b>である。

```
  scope :find_candidate_stations, lambda { |from, num|
    Station.where(id: Station.within(num, origin: from))
      .by_distance(origin: from)
  }
# というようなscopeを使い
# 対象施設（destination）

from = Station.new(lat: destination.lat, lon: destination.lon)
near_stations = find_candidate_stations(from, 10) # 対象施設から半径10kmの駅を取得している
```

これで、対象施設から半径10kmの候補となる駅を取得することに成功したはず。

## 駅からのアクセス情報を取得
下準備が整ったので、ついにアクセスデータを取得していきます。

[Google Maps Direction API](https://developers.google.com/maps/documentation/directions/?hl=ja)を使います。

https://maps.googleapis.com/maps/api/directions/json?origin={始点の緯度経度}&destination={終点の緯度経度}&mode={walking or driving}&key={APIキー}

※APIキーはここで取得する[Google Developer Console](https://console.developers.google.com/)

こうして得られたデータはjson形式で、必要な部分を抜き出すと下記の部分。

```
{
....
....
....
   "routes" : [
....
....
....
         "legs" : [
            {
               "distance" : {
                  "text" : "0.4 km",
                  "value" : 425
               },
               "duration" : {
                  "text" : "5分",
                  "value" : 315
               },
               "end_address" : "日本, 〒101-0062 東京都千代田区神田駿河台４丁目 東京復活大聖堂",
               "end_location" : {
                  "lat" : 35.6980715,
                  "lng" : 139.7660134
               },
               "start_address" : "日本, 〒113-0034 東京都文京区湯島１丁目５ 外堀通り",
               "start_location" : {
                  "lat" : 35.7006197,
                  "lng" : 139.7639851
               },
....
....
....
}
```

この部分を抽出すれば、

◯◯駅から徒歩（or車）で☓☓分というアクセス情報が感性する。

## まとめ

数万施設も手でいちいち手で入力するのは現実問題無理なので、自動化しないとやってられない。
そのため、今回GoogleのAPIを使ったアクセス情報の自動生成方法について書いた。

手順をまとめると

1. 駅データの取得
2. 目的データの緯度経度情報取得
3. 候補となる最寄り駅（半径◯◯kmの駅）候補を取得
4. Google Map Direction APIで経路情報を取得

である。

手入力で数万件のアクセス情報を入れるのは辛いので、ぜひこの方法で
「奥様、そう思うでしょ？でもね、・・・・」と言い返してあげてください。

※ダーツの対戦相手・スポンサー募集してます。よろしくお願いします！