きゃまなかのブログ

新卒5年目のWEBエンジニアです。仕事は運用メインで、空いた時間に開発しています。何故かブログを始めた次の日に会社の先輩に見つかりました。変な記事書くとダメ出し食らうので、いい記事書けるように頑張ります。

【Ruby on Rails】緯度経度から 2 点間の距離を算出する

概要

今日では、企業が公開している API を利用して、簡単にアプリケーションを作成できるようになりました。

その中には、店舗情報を扱う API も数多く存在しています。

店舗情報を扱う API の多くは、店舗の位置情報を表すため、緯度経度を利用しています。

位置情報を利用したアプリケーションを作成する場合、緯度経度から 2 点間の距離を求めたいと思うことがあるかもしれません。

例えば、店舗情報 API と組み合わせて、『現在地周辺の半径 ○○ m 以内のレストランを近い順に表示したい』と思った場合、これに該当します。

今回、緯度経度から 2 点間の距離を算出するプログラムを作成する機会があったので、その時のことをまとめました。

計算式

地球は球体なので、地球上の 2 点間の距離を算出するには、大円距離を求める必要があります。

 大円距離 - Wikipedia

大円距離の計算式はいくつかあるようですが、全ての距離に対して用いることのできる Vincenty 法を利用することとします。

Vincenty法 - Wikipedia

プログラムを作成

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入門

数値計算用のメソッドはこちらを参考にしてください。

module Math (Ruby 2.4.0)

算出結果の確認

先ほどのプログラムで新宿駅と渋谷駅の距離を求めます。

# 新宿駅
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地点間の距離と方位角 - 高精度計算サイト

まとめ

緯度経度から 2 点間の距離を算出する場合、大円距離を求める必要があります。

プログラムは Vincenty 法 に当てはめるだけなので、簡単に実装できます。

最後に

Rails で位置情報を扱う場合、ここら辺の gem が有名かと思います。

github.com

github.com

今回、作成したプログラムも機能として盛り込まれていると思います。

他にも、多くの機能が用意されているので、gem を利用した方法も有りだと思います。

自分のアプリケーションにあった方法を採用してください。