【Ruby on Rails】緯度経度から 2 点間の距離を算出する
概要
最近では企業が多くの API を公開しており、それらを利用して簡単にアプリケーションを作成できるようになりました。
その中には、店舗情報を扱う API も数多く存在します。
店舗情報を扱う API の多くは、店舗の位置情報を表すため緯度経度を利用しています。
位置情報を利用したアプリケーションを作成する場合、緯度経度から 2 点間の距離を求めたいと思うことがあるかもしれません。
例えば、店舗情報 API と組み合わせて、『現在地周辺の半径 ○○ m 以内のレストランを近い順に表示したい』と思った場合、これに該当します。
今回、緯度経度から 2 点間の距離を算出するプログラムを作成する機会があったので、その時のことをまとめました。
計算式
地球は球体なので、地球上の 2 点間の距離を算出するには、大円距離を求める必要があります。
大円距離の計算式はいくつかあるようですが、全ての距離に対して用いることのできる Vincenty 法を利用することとします。
プログラムを作成
Vincenty 法に当てはめていきます。
def distance(lat1, lng1, lat2, lng2)
# ラジアン単位に変換
x1 = lat1.to_f * Math::PI / 180
y1 = lng1.to_f * Math::PI / 180
x2 = lat2.to_f * Math::PI / 180
y2 = lng2.to_f * Math::PI / 180
# 地球の半径 (km)
radius = 6378.137
# 差の絶対値
diff_y = (y1 - y2).abs
calc1 = Math.cos(x2) * Math.sin(diff_y)
calc2 = Math.cos(x1) * Math.sin(x2) - Math.sin(x1) * Math.cos(x2) * Math.cos(diff_y)
# 分子
numerator = Math.sqrt(calc1 ** 2 + calc2 ** 2)
# 分母
denominator = Math.sin(x1) * Math.sin(x2) + Math.cos(x1) * Math.cos(x2) * Math.cos(diff_y)
# 弧度
degree = Math.atan2(numerator, denominator)
# 大円距離 (km)
degree * radius
end
abs は数値の絶対値を取得するメソッドです。
絶対値を取得する - 数値(Numeric)クラス - Ruby入門
数値計算用のメソッドはこちらを参考にしてください。
算出結果の確認
先ほどのプログラムで新宿駅と渋谷駅の距離を求めます。
# 新宿駅
lat1 = 35.689407
lng1 = 139.700306
# 渋谷駅
lat2 = 35.658034
lng2 = 139.701636
distance = distance(lat1, lng1, lat2, lng2)
# 小数点 6 桁で四捨五入
puts "#{distance.round(6)} km"
プログラムを実行します。
$ ruby distance.rb
3.494497 km
下記サイトで 2 点間の緯度経度を入力した結果と同じになりました。
まとめ
緯度経度から 2 点間の距離を算出する場合、大円距離を求める必要があります。
プログラムは Vincenty 法の公式に当てはめれば実装できます。
最後に
Ruby on Rails には位置情報を扱う gem がいくつか用意されています。
今回、作成した 2 点間距離を求めるプログラムも機能として盛り込まれています。
他にも多くの機能が用意されているようなので gem を使ってみるのも有りだと思います。
自分のアプリケーションにあった方法を採用してください。
【Ruby on Rails】 gem を使わないで環境毎に定数管理をする
概要
Ruby on Rails には定数管理用の gem がたくさん存在します。
有名な gem だと以下の名前が上がるかと思います。
- config は環境毎に yaml ファイルを用意して定数を管理するので、大規模アプリケーション向けのイメージがあります。管理する定数が多い場合にオススメです。
- settingslogic は 1 つの yaml ファイルで環境毎に定数を管理するので、中小規模アプリケーション向けのイメージがあります。管理する定数が少ないのであれば見やすくてオススメです。
ただ、ここで『定数管理ぐらい自分で実装できないのか?』と疑問に思ったことがあります。
そこで今回、自分で簡単に実装できる環境毎に定数を管理する方法をまとめました。
はじめに
gem を使わない定数管理方法だと、以下の 2 つがオーソドックスな方法かと思います。
- application_controller.rb で定数を管理する方法
- config/initializers/constants.rb のようなファイルを用意して、そこで定数を管理する方法
application_controller.rb はアプリケーション共通の処理を管理する場所なので、ここで定数を管理するのはあまり相応しくないと思います。
config/initializers/constants.rb のようにファイルを用意して、そこで定数を管理する方法は賛成です。
ただし ruby ファイルではなく、環境毎に設定を管理しやすい yaml ファイルを利用しようと思います。
Yaml を利用した定数管理
用意するファイルは以下の2 つです。
- config/constants.yml
- config/initializers/00_load_config.rb
config/constants.yml
これが定数を管理するためのファイルです。
yaml の機能であるアンカーとエイリアスを利用して、環境毎に定数を出し分けています。
common の設定をベースに development と production で設定を上書きしている感じです。
# アプリケーション共通の設定を記載します
common: &common
domain:
example: http://example.com/
port:
- 80
- 443
message: This is application common setting
# 開発環境の設定を記載します
development:
<<: *common
message: This is development environment setting
# 本番環境の設定を記載します
production:
<<: *common
message: This is production environment setting
config/initializers/00_load_config.rb
これは先程の /config/constants.yml に記載した設定をアプリケーション起動時に読み込んで、ruby で扱える定数に変換しています。
file = "#{Rails.root}/config/constants.yml"
# 再帰的にオブジェクトを凍結します
def deep_freeze(hash)
hash.freeze.each_value do |i|
i.kind_of?(Hash) ? deep_freeze(i) : i.freeze
end
end
CONFIG = deep_freeze(YAML.load_file(file)[Rails.env].deep_symbolize_keys)
deep_symbolize_keys は再帰的にシンボル形式に変換してくれます。
https://apidock.com/rails/v4.0.2/Hash/deep_symbolize_keys
freeze は オブジェクトを凍結、つまり変更不可 (定数) にします。
http://ref.xaio.jp/ruby/classes/object/freeze
動作確認
期待通り定数が取得出来ることをコンソール経由で試してみます。
設定した定数は以下のように CONFIG で呼び出す事が出来ます。
$ rails c
Loading development environment (Rails 5.0.2)
irb(main):001:0> CONFIG
=> {:domain=>{:example=>"http://example.com/", :port=>[80, 443]}, :message=>"This is development environment setting"}
# 開発環境用の設定に上書きされています
irb(main):002:0> CONFIG[:message]
=> "This is development environment setting"
# 凍結されています
irb(main):003:0> CONFIG.frozen?
=> true
# ネストした値も凍結されています
irb(main):004:0> CONFIG[:domain][:exapmle].frozen?
=> true
# 変更しようとするとエラーになります
irb(main):005:0> CONFIG[:message] << 'Hello'
RuntimeError: can't modify frozen String
まとめ
gem を使わずに環境毎に定数管理する方法をまとめました。
割と簡単に出来たかなと思います。
自分で実装することのメリットはこの 2 つかなと思います。
- 仕様を把握できる点
- 自分で拡張できる点
あとは gem の依存関係などでどうしても定数管理用の gem をインストール出来ない場合などは使えると思います。
ただし、1 つの yaml ファイルで全ての定数を管理するので、中小規模アプリケーション向けの方法だと思います。
Ruby on Rails の定数管理方法に答えはないと思います。
どの方法も一長一短なので、開発しているアプリケーションに合った方法を見極めて採用するのがいいと思います。