Rails4で位置情報を扱う実装サンプルまとめ


[私が開発しているアプリ](https://coderstable.com)で位置情報をつかった技術を実装したので共有します。

# 概念・基本
位置情報を扱う際の基本的な語句や概念を整理しておきます。
### Geolocation API
- [ ] ユーザーの位置情報を扱うためのAPI
- [ ] HTML5からJavaScriptで位置情報を取得できるように標準化されている
- [ ]  getCurrentPosition() …… ユーザーの現在の位置情報を一回だけ取得する
- [ ]  watchPosition() …… ユーザーの位置情報を定期的に監視する
- [ ]  clearWatch() …… watchPosition()による位置情報の監視をクリアする
- [ ] [参考](http://www.htmq.com/geolocation/)

### ジオコーディング
- [ ] 広義には, 各種情報に対して、関連する地理座標（典型的には緯度・経度）を付加すること(wikipedia)
- [ ] 狭義には,  狭義には地名、住所が示す場所に対して、地理座標を与えることを言う(wikkipedia)
- [ ] 例としてはGoogle Maps API や geocoder gem

# 実装
### やりたかったこと
- [ ] データベースに登録した位置情報をgooglemapで表示したい
- [ ] 現在位置を監視して、特定の住所に近づいたら画面に通知をだす
- [ ] データベースに格納された位置情報を現在位置に近い順に表示する 

### やりたいことを実現するために必要な実装
- [ ] [フロント側で現在位置を取得する](#フロント側で現在位置を取得する)
- [ ] データベースに位置情報を格納する
- [ ] mapへサーバの位置情報を表示する
- [ ] フロント側で２つの位置間の距離を計算する
- [ ] サーバ側で距離を２つの位置間の計算する


### フロント側で現在位置を取得する

```coffee:get_current_pos.coffee
    successCallback = (position) ->
      # 位置情報が渡っってくるので、ajaxでおくるなど、距離計算につかうなどすえ

    errorCallback = (errors) ->
        # エラー処理

    navigator.geolocation.getCurrentPosition(successCallback,errorCallback)
```
### データベースに位置情報を格納する
[参考:Rails4でGoogleMapを表示させる](http://qiita.com/jacoyutorius/items/a107ff6c93529b6b393e#comment-84eb0d62006da032c37b)

```ruby:GemFile
gem 'geocoder'
gem "gmaps4rails"
```
```ruby:space.rb
class Space < ActiveRecord::Base
  geocoded_by :address
  after_validation :geocode
end
```
```ruby
class CreateSpaces < ActiveRecord::Migration
  def change
    create_table :spaces do |t|
      t.string :address
      t.decimal :latitude, precision: 9, scale: 6
      t.decimal :longitude, precision: 10, scale: 6
    end
  end
end
```
### Googlemapへサーバの位置情報を表示する
[参考:Rails4でGoogleMapを表示させる](http://qiita.com/jacoyutorius/items/a107ff6c93529b6b393e#comment-84eb0d62006da032c37b)

```slim:application.html.slim
    script[src="//maps.googleapis.com/maps/api/js?key=あなたのキー&libraries=geometry" type="text/javascript"]
```
```js:application.js
// マーカークラスターの読み込み
// download here! 
// https://github.com/googlemaps/js-marker-clusterer
//= reqire markerclusterer_compiled 

```

```ruby:spaces_controller.rb
  def show
    # マーカークラスターに渡す情報を生成する
    @hash = Gmaps4rails.build_markers([@space]) do |space, marker|
      marker.lat space.latitude.to_f
      marker.lng space.longitude.to_f
      marker.infowindow space.name
      marker.json(title: space.name)
    end

    @joined_users = @space.joined_users
  end
```

```slim:spaces/show.html.slim
          .map_container
            #map.space_map
            javascript:

              var latlng = new google.maps.LatLng( parseFloat(#{@hash[0][:lat]}), parseFloat(#{@hash[0][:lng]}) );
              handler = Gmaps.build('Google');
              handler.buildMap(
                {provider: {}, internal: {id: 'map'}},
                function(){
                  markers = handler.addMarkers(#{raw  @hash.to_json});
                  handler.bounds.extendWith(markers);
                  handler.fitMapToBounds();
                  handler.getMap().setCenter(latlng);
                  handler.getMap().setZoom(15);
                }
              );

```

![スクリーンショット 2016-05-26 12.11.30 PM.jpg (86.2 kB)](https://img.esa.io/uploads/production/attachments/2315/2016/05/26/9911/97bc25b5-0ba1-4c25-b089-326568f68190.jpg)

### フロント側で２つの位置間の距離を計算する
```coffee:gmap_util.coffee
  calc_distance = (from_pos, to_pos) ->

    from = new google.maps.LatLng( parseFloat(from_pos[0]), parseFloat(from_pos[1]) )
    to = new google.maps.LatLng( parseFloat(to_pos[0]), parseFloat(to_pos[1]) )

    google.maps.geometry.spherical.computeDistanceBetween(from, to)

```

### サーバ側で距離を２つの位置間の計算する

```ruby:config/initializer/geocoder.rb
Geocoder.configure(units: :km)
```

```ruby:spacr.rb
  def distance(pos)
    Geocoder::Calculations.distance_between([pos[0], pos[1]], [latitude, longitude]) * 1000.0
  end
# gemの際とみたらいろいろ便利メソッドあった
# if obj.geocoded?
#  obj.nearbys(30)                      # other objects within 30 miles
# obj.distance_from([40.714,-100.234]) # distance from arbitrary point to object
#  obj.bearing_to("Paris, France")      # direction from object to arbitrary point
end
```

[gem geocoder](https://github.com/alexreisner/geocoder)

# 参考
[Rails4でGoogleMapを表示させる](http://qiita.com/jacoyutorius/items/a107ff6c93529b6b393e#comment-84eb0d62006da032c37b)
[Geolocation API](http://www.htmq.com/geolocation/)