Rails5でGeocoderとGeolocationを使って周辺の施設を検索する方法

Railsでアプリケーションを作っている時に位置情報を使って周辺の施設（自分の場合は飲食店）を検索する機能を付けたいと思ったので今後、位置情報を使う時にメモの代わりになればいいかなと思ったので書きます。

最後の方で壊滅的な事に気づいたのでそれも書きますww

ではやっていきましょう！！
## 前提
Rails　５.0.0
Ruby  2.5.3
下記を含むモデルがある事

```ruby:
t.string "prefecture"   都道府県
t.string "address_city"   区市
t.string "address_street"   町
t.string "address"   都道府県+区市
```

ブラウザに位置情報を使用する事が許可されている事

##手順
gem導入
↓
住所をgeocording
↓
緯度経度をそれぞれカラムへ保存
↓
現在地情報をgeolocationを使って取得
↓
検索

##gemの導入
```ruby:Gemfile
gem 'geocoder'
```

##住所をgeocording
モデルにfloat型でlatitudeとlongitudeを追加

```ruby:モデル.rb
    t.float "latitude"
    t.float "longitude"
```

ここで気づいたのですが実はgeocorderはスポットに強い代わりに住所に対して弱く
どういう事かというと「スカイツリー」で緯度経度を保存しようとすれば正確な緯度経度が出るのですが
「東京都墨田区押上１丁目１−２」で保存しようとすると緯度経度が出ず、「東京都墨田区」までしか出ませんwww

結論を述べるとgeocorderでは無く、googleのAPIを使用する方が正確な緯度経度を取得出来ます。
自分もそれに手を付け始めたので完成したらその記事はまた書きます！（今書けよ、、）

##緯度経度をそれぞれカラムへ保存
自分の場合は住所をそれぞれ都道府県、市、区、町村、番地とそれぞれ別のカラムに保存したので都道府県、市、区までのカラムから緯度経度を出します。

なので、自分はgeocording用にaddressカラムを作ってその中に、都道府県、市、区の３つだけを入れました。
その前にgeocorderにどのカラムから緯度経度を出すか教えてあげましょう！
この2行をモデルに追加してあげます。
下の行はどのタイミングでgeocordingするかです。この書き方だと住所が編集された時となります。（これ以外知らない、、）

```ruby:モデル.rb
   	geocoded_by :address
	after_validation :geocode, if: :address_changed?
```
これは自分の場合ですが一応書いときます。

```ruby:コントローラ
    def create
		@shop = Shop.new(shop_params)
		@shop.address = @shop.prefecture + @shop.address_city
		@shop.address = @shop.address.gsub(/\d+/, "").gsub(/\-+/, "")
		@shop.save
	end
```

##現在地情報をgeolocationを使って取得
下はHTMLに組み込まれてるAPI（自信はない、、）を使用するスクリプトです。
これを書き込んだViewにはこんな感じのボタンを入れてください。


```ruby:View
   	<button onclick="getPosition();">Find!!</button>
```

```ruby:適宜使いたいView
   	<script>

    // 現在地取得処理
    function getPosition() {
      // 現在地を取得
      navigator.geolocation.getCurrentPosition(
        // 取得成功した場合
        function(position) {
            window.location.href = "/shops/search_location?latitude="+position.coords.latitude+"&longitude="+position.coords.longitude
            // $.ajax({
            //   url: "/shops/search_location",
            //   type: "GET",
            //   dataType: 'html',
            //   data: {latitude : position.coords.latitude,
            //         longitude : position.coords.longitude},
            // });
        },
        // 取得失敗した場合
        function(error) {
          switch(error.code) {
            case 1: //PERMISSION_DENIED
              alert("位置情報の利用が許可されていません");
              break;
            case 2: //POSITION_UNAVAILABLE
              alert("現在位置が取得できませんでした");
              break;
            case 3: //TIMEOUT
              alert("タイムアウトになりました");
              break;
            default:
              alert("その他のエラー(エラーコード:"+error.code+")");
              break;
          }
        }
      );
    }
  </script>

```

途中のajaxの記述は別に無くて大丈夫です。
興味本位でajaxとwindow.location.hrefを比較してました。（結果は後者の方が記述が少なくてわかりやすいから採用）
window.location.href の部分は書き方は参考までに書きますが、適宜修正してください。

```ruby:モデル.rb
window.location.href = "渡すURL?latitude="+position.coords.latitude+"&longitude="+position.coords.longitude

```
渡すURLはコントローラーのアクションを呼び出します。

##検索
先ほど記述したURLからコントローラのアクションを呼び出します。
ポイントはlatitudeとlongitudeをfloat型で受け取っている所です。
@locationsに半径500m以内にある施設（自分の場合は飲食店）を配列で入れます。

.within_boxというメソッドを作ります。
モデルに以下を書き込んでください。

```ruby:モデル.rb
class << self
		def within_box(distance, latitude, longitude)
			distance = distance
			center_point = [latitude, longitude]
			box = Geocoder::Calculations.bounding_box(center_point, distance)
			self.within_bounding_box(box)
		end
	end

```

```
Model.within_box(マイル, 緯度, 経度)
```
という使い方が出来ます。これで半径マイルのが出せます。
ちなみに0.310686はメートルに直すと500mです。

```ruby:コントローラ
def search_location
		latitude = params[:latitude].to_f
		longitude = params[:longitude].to_f
		@locations = Shop.within_box(0.310686, latitude, longitude)
end
```

##終わり
初めて記事を書いてみましたが恐ろしいほど復習になりました。
間違っている箇所もあるとは思いますが優しく教えてください！
ありがとうございました。