私が開発しているアプリで位置情報をつかった技術を実装したので共有します。
概念・基本
位置情報を扱う際の基本的な語句や概念を整理しておきます。
Geolocation API
- ユーザーの位置情報を扱うためのAPI
- HTML5からJavaScriptで位置情報を取得できるように標準化されている
- getCurrentPosition() …… ユーザーの現在の位置情報を一回だけ取得する
- watchPosition() …… ユーザーの位置情報を定期的に監視する
- clearWatch() …… watchPosition()による位置情報の監視をクリアする
- 参考
ジオコーディング
- 広義には, 各種情報に対して、関連する地理座標(典型的には緯度・経度)を付加すること(wikipedia)
- 狭義には, 狭義には地名、住所が示す場所に対して、地理座標を与えることを言う(wikkipedia)
- 例としてはGoogle Maps API や geocoder gem
実装
やりたかったこと
- データベースに登録した位置情報をgooglemapで表示したい
- 現在位置を監視して、特定の住所に近づいたら画面に通知をだす
- データベースに格納された位置情報を現在位置に近い順に表示する
やりたいことを実現するために必要な実装
- フロント側で現在位置を取得する
- データベースに位置情報を格納する
- mapへサーバの位置情報を表示する
- フロント側で2つの位置間の距離を計算する
- サーバ側で距離を2つの位置間の計算する
フロント側で現在位置を取得する
get_current_pos.coffee
successCallback = (position) ->
# 位置情報が渡っってくるので、ajaxでおくるなど、距離計算につかうなどすえ
errorCallback = (errors) ->
# エラー処理
navigator.geolocation.getCurrentPosition(successCallback,errorCallback)
データベースに位置情報を格納する
GemFile
gem 'geocoder'
gem "gmaps4rails"
space.rb
class Space < ActiveRecord::Base
geocoded_by :address
after_validation :geocode
end
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へサーバの位置情報を表示する
application.html.slim
script[src="//maps.googleapis.com/maps/api/js?key=あなたのキー&libraries=geometry" type="text/javascript"]
application.js
// マーカークラスターの読み込み
// download here!
// https://github.com/googlemaps/js-marker-clusterer
//= reqire markerclusterer_compiled
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
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);
}
);
フロント側で2つの位置間の距離を計算する
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)
サーバ側で距離を2つの位置間の計算する
config/initializer/geocoder.rb
Geocoder.configure(units: :km)
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