地球上の2点間の距離を求める

地球を真球と仮定して球面余弦定理に当てはめれば近似値が出る。

ググるとhaversine fomula(半正矢関数)という関数を使った計算式が出てくる。これは三角関数表を引く回数を減らすために球面余弦定理を変形したもの。PCではsinやcosを簡単に計算できるので特にそういう工夫は必要なく、普通に球面余弦定理に当てはめれば良い。


球面上に三角形ABCを描き、距離AB=c、BC=a、CA=bとする。∠BACをαとする。球面余弦定理より

 \cos a = \cos b \cos c + \sin b \sin c \cos \alpha ... ①

が成り立つ。点BCの距離aは

 a = \arccos(\cos b \cos c + \sin b \sin c \cos \alpha) ... ②

で表すことができる。ここで点Aを北極点、北緯90度(= π/2), 東経0度とし、Bの緯度経度をφ1 , λ1、Cの緯度経度をφ1 , λ2とする(角度は弧度法に直しておく)

ABの長さc、ACの長さbは

 c = \frac{\pi}{2} - \phi_1

 b = \frac{\pi}{2} - \phi_2

また∠BACの角度αは

 \alpha = \lambda_1 - \lambda_2

これらを②に代入

 a = \arccos(\cos(\frac{\pi}{2} - \phi_1) \cos(\frac{\pi}{2} - \phi_2) + \sin(\frac{\pi}{2} - \phi_1) \sin(\frac{\pi}{2} - \phi_2) \cos(\lambda_1 - \lambda_2)) --- ③

ここで

 \cos(\frac{\pi}{2} - x) = \sin x

 \sin(\frac{\pi}{2} - x) = \cos x

なので、③は

 a = \arccos(\sin \phi_1 \sin \phi_2 + \cos \phi_1 \cos \phi_2 \cos(\lambda_1 - \lambda_2)

これに地球平均半径6371kmを乗じると2点間の距離が出る。JavaScriptで計算すると、

function calcDistanceBetween(point1, point2) {
  const φ1 = point1.lat * (Math.PI / 180);
  const λ1 = point1.lng * (Math.PI / 180);
  const φ2 = point2.lat * (Math.PI / 180);
  const λ2 = point2.lng * (Math.PI / 180);

  const r = 6371;
  const a = Math.sin(φ1) * Math.sin(φ2) + Math.cos(φ1) * Math.cos(φ2) * Math.cos(λ2 - λ1)
  return Math.acos(a) * r;
}

// 東京駅
const tokyo = {
  lat: 35.6812362,
  lng: 139.7671248
}

// 大阪駅
const osaka = {
  lat: 34.7343206,
  lng: 135.4948995
}

// シドニー
const sydney = {
  lat: -33.847927,
  lng: 150.6517825
}

// リオデジャネイロ
const rio = {
  lat: -22.9140693,
  lng: -43.5860691
}

実行すると

> calcDistanceBetween(tokyo, osaka)
402.13559042331207
> calcDistanceBetween(tokyo, sydney)
7813.628751296817
> calcDistanceBetween(tokyo, rio)
18558.97478229851

だいたいあってる。実際の地球は真球ではなく極方向にわずかに潰れた楕円体であるため近似値(最大で誤差0.5%)となる。

実際に2点間の距離を求める場合は、自前で実装するよりライブラリを利用しよう。例えばRubyにはgeocoderというgemがある。geocoderのソースを見るとhaversineを使っているが違いはない。