酢ろぐ!

カレーが嫌いなスマートフォンアプリプログラマのブログ。

C#で緯度経度の2点間の直線距離を求める

日本測地系から世界測地系(WGS84)に変換する方法については、「C#で位置情報を日本測地系から世界測地系(WGS84)に変換する」をごらんください。

直線距離を求めるのはとても簡単なんですが計算結果にしっくり来ず調べてみたところ、どうやら地球が球であることを考慮しなければいけないようです。そりゃそうか……

このサイトのプログラムを参考にしながら、C#へソースコードを移植しました。Windows Mobile(.NET Frameworkのサブセットの.NET CF)で扱えるクラスとメソッドで対応しているつもりなので他のバージョンでも動くと思います。

C#

  /// <summary>
  /// 度単位から等価なラジアン単位に変換します。
  /// </summary>
  /// <param name="deg">度単位</param>
  /// <returns></returns>
  static double deg2rad(double deg)
  {
      return (deg / 180) * Math.PI;
  }

  /// <summary>
  /// 2点間の位置情報から距離を求める
  /// </summary>
  /// <param name="posA"></param>
  /// <param name="posB"></param>
  /// <returns></returns>
  public static int CalculateDistance(Positions posA, Positions posB)
  {
      // 2点の緯度の平均
      double latAvg = deg2rad(posA.Latitude + ((posB.Latitude - posA.Latitude) / 2));
      // 2点の緯度差
      double latDifference = deg2rad(posA.Latitude - posB.Latitude);
      // 2点の経度差
      double lonDifference = deg2rad(posA.Longitude - posB.Longitude);

      double curRadiusTemp = 1 - 0.00669438 * Math.Pow(Math.Sin(latAvg), 2);
      // 子午線曲率半径
    double meridianCurvatureRadius = 6335439.327 / Math.Sqrt(Math.Pow(curRadiusTemp, 3));
      // 卯酉線曲率半径
      double primeVerticalCircleCurvatureRadius = 6378137 / Math.Sqrt(curRadiusTemp);

      // 2点間の距離
      double distance = Math.Pow(meridianCurvatureRadius * latDifference, 2) 
          + Math.Pow(primeVerticalCircleCurvatureRadius
          * Math.Cos(latAvg) * lonDifference, 2);
      distance = Math.Sqrt(distance);

      return (int)Math.Round(distance);
  }

VB.NET

''' <summary>
''' 度単位から等価なラジアン単位に変換します。
''' </summary>
''' <param name="deg">度単位</param>
''' <returns></returns>
Private Shared Function deg2rad(deg As Double) As Double
	Return (deg / 180) * Math.PI
End Function

''' <summary>
''' 2点間の位置情報から距離を求める
''' </summary>
''' <param name="posA"></param>
''' <param name="posB"></param>
''' <returns></returns>
Public Shared Function CalculateDistance(posA As Positions, posB As Positions) As Integer
	' 2点の緯度の平均
	Dim latAvg As Double = deg2rad(posA.Latitude + ((posB.Latitude - posA.Latitude) / 2))
	' 2点の緯度差
	Dim latDifference As Double = deg2rad(posA.Latitude - posB.Latitude)
	' 2点の経度差
	Dim lonDifference As Double = deg2rad(posA.Longitude - posB.Longitude)

	Dim curRadiusTemp As Double = 1 - 0.00669438 * Math.Pow(Math.Sin(latAvg), 2)
	' 子午線曲率半径
	Dim meridianCurvatureRadius As Double = 6335439.327 / Math.Sqrt(Math.Pow(curRadiusTemp, 3))
	' 卯酉線曲率半径
	Dim primeVerticalCircleCurvatureRadius As Double = 6378137 / Math.Sqrt(curRadiusTemp)

	' 2点間の距離
	Dim distance As Double = Math.Pow(meridianCurvatureRadius * latDifference, 2) + Math.Pow(primeVerticalCircleCurvatureRadius * Math.Cos(latAvg) * lonDifference, 2)
	distance = Math.Sqrt(distance)

	Return CInt(Math.Round(distance))
End Function

追記(2016/2/17)

.NET Framework 4.0からGeoCoordinateオブジェクト同士の差を求めることができるようになっているようです。

var distance = new GeoCoordinate(lat1, lon1)
    .GetDistanceTo(new GeoCoordinate(lat2, lon2));

さすがに.NET Compact Frameworkでの対応をする方はもういないと思いますので、特別理由がなければこちらのクラスを使った方が良さそうです。

.NET Frameworkで2点間(緯度経度)の距離を求める - プログラムの事とか」にて詳しく書かれていますのでこちらをお読みください。

関連記事

Windows Mobile(.NET Compact Framework)を使ってアプリ開発する際に逆引きとしてお使いください。