35
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

2点間の距離計算 (C, Clojure, Go, Haskell, Java, LOGO, OCaml, Ruby, Rust, Scratch, Swift)

Last updated at Posted at 2017-01-31

地球を真球とみなして、緯度経度による2点間の距離をいろいろなプログラミング言語で計算する。

基本方針は以下。

  • 関数/メソッド1つをコピペして使えそうなコードにする
  • require/import系を書かなくてよいなら書かない
  • 標準ライブラリだけを使う
  • 関数/メソッドの引数はプリミティブな浮動小数点4つ
  • 変数/再代入不可変数/定数については「シンプルで違和感のない表現で」と思ってはいるけど決めかねている

C

#include <math.h>
#include <stdio.h>

// 球面三角法により、大円距離(メートル)を求める
double distance(double lat1, double lng1, double lat2, double lng2) {

    // 円周率
    const double pi = 3.14159265359;

    // 緯度経度をラジアンに変換
    double rlat1 = lat1 * pi / 180;
    double rlng1 = lng1 * pi / 180;
    double rlat2 = lat2 * pi / 180;
    double rlng2 = lng2 * pi / 180;

    // 2点の中心角(ラジアン)を求める
    double a =
      sin(rlat1) * sin(rlat2) +
      cos(rlat1) * cos(rlat2) *
      cos(rlng1 - rlng2);
    double rr = acos(a);

    // 地球赤道半径(メートル)
    const double earth_radius = 6378140;

    // 2点間の距離(メートル)
    double distance = earth_radius * rr;

    return distance;
}

int main(int argc, char *argv[]) {
  // 計算サンプル
  printf("%f\n", distance(35.1730990, 136.883466, 35.1855732, 136.899092));
  printf("%f\n", distance(35.1855732, 136.899092, 35.1730990, 136.883466));
  return 0;
}

Clojure

; 球面三角法により、大円距離(メートル)を求める
(defn distance [lat1 lng1 lat2 lng2]

  ; 緯度経度をラジアンに変換
  (let [rlat1 (/ (* lat1 Math/PI) 180)
        rlng1 (/ (* lng1 Math/PI) 180)
        rlat2 (/ (* lat2 Math/PI) 180)
        rlng2 (/ (* lng2 Math/PI) 180)

        ; 2点の中心角(ラジアン)を求める
        a (+ (* (Math/sin rlat1) (Math/sin rlat2))
             (* (* (Math/cos rlat1) (Math/cos rlat2))
                (Math/cos (- rlng1 rlng2))))
        rr (Math/acos a)

        ; 地球赤道半径(メートル)
        earth_radius 6378140]

  ; 2点間の距離(メートル)
  (* earth_radius rr)))

; 計算サンプル
(println (distance 35.1730990 136.883466 35.1855732 136.899092))
(println (distance 35.1855732 136.899092 35.1730990 136.883466))

Go

package main

import (
  "fmt"
  "math"
)

func main() {
  // 計算サンプル
  fmt.Printf("%f\n", distance(35.1730990, 136.883466, 35.1855732, 136.899092));
  fmt.Printf("%f\n", distance(35.1855732, 136.899092, 35.1730990, 136.883466));
}

// 球面三角法により、大円距離(メートル)を求める
func distance(lat1 float64, lng1 float64, lat2 float64, lng2 float64) float64 {

  // 緯度経度をラジアンに変換
  rlat1 := lat1 * math.Pi / 180
  rlng1 := lng1 * math.Pi / 180
  rlat2 := lat2 * math.Pi / 180
  rlng2 := lng2 * math.Pi / 180

  // 2点の中心角(ラジアン)を求める
  a :=
    math.Sin(rlat1) * math.Sin(rlat2) +
    math.Cos(rlat1) * math.Cos(rlat2) *
    math.Cos(rlng1 - rlng2)
  rr := math.Acos(a)

  earth_radius := 6378140. // 地球赤道半径(メートル)
  distance := earth_radius * rr
  return distance
}

Haskell

-- 球面三角法により、大円距離(メートル)を求める
distance :: Double -> Double -> Double -> Double -> Double
distance lat1 lng1 lat2 lng2 = let {

  -- 緯度経度をラジアンに変換
  rlat1 = lat1 * pi / 180;
  rlng1 = lng1 * pi / 180;
  rlat2 = lat2 * pi / 180;
  rlng2 = lng2 * pi / 180;

  -- 2点の中心角(ラジアン)を求める
  a = sin rlat1 * sin rlat2 + cos rlat1 * cos rlat2 * cos (rlng1 - rlng2);
  rr = acos a;

  earth_radius = 6378140; -- 地球赤道半径(メートル)
  distance = earth_radius * rr;

} in distance

main = do
  -- 計算サンプル
  print (distance 35.1730990 136.883466 35.1855732 136.899092)
  print (distance 35.1855732 136.899092 35.1730990 136.883466)

Java

public class Earth {

  public static void main(String args[]) {
    // 計算サンプル
    System.out.println(distance(35.1730990, 136.883466, 35.1855732, 136.899092));
    System.out.println(distance(35.1855732, 136.899092, 35.1730990, 136.883466));
  }

  // 球面三角法により、大円距離(メートル)を求める
  public static double distance(double lat1, double lng1, double lat2, double lng2) {

    // 緯度経度をラジアンに変換
    double rlat1 = Math.toRadians(lat1);
    double rlng1 = Math.toRadians(lng1);
    double rlat2 = Math.toRadians(lat2);
    double rlng2 = Math.toRadians(lng2);

    // 2点の中心角(ラジアン)を求める
    double a =
      Math.sin(rlat1) * Math.sin(rlat2) +
      Math.cos(rlat1) * Math.cos(rlat2) *
      Math.cos(rlng1 - rlng2);
    double rr = Math.acos(a);

    // 地球赤道半径(メートル)
    double earth_radius = 6378140;

    // 2点間の距離(メートル)
    double distance = earth_radius * rr;

    return distance;
  }
}

LOGO

; 球面三角法により、大円距離(メートル)を求める
to distance :lat1 :lng1 :lat2 :lng2

  ; 円周率
  make "pi (radarctan 0 1) * 2

  ; 緯度経度をラジアンに変換
  make "rlat1 :lat1 * :pi / 180
  make "rlng1 :lng1 * :pi / 180
  make "rlat2 :lat2 * :pi / 180
  make "rlng2 :lng2 * :pi / 180

  ; 2点の中心角(ラジアン)を求める
  make "a1 (radsin :rlat1) * (radsin :rlat2)
  make "a2 (radcos :rlat1) * (radcos :rlat2)
  make "a3 radcos (rlng1 - rlng2)
  make "a :a1 + :a2 * :a3
  make "b sqrt((-1 * :a * :a) + 1)
  make "c radarctan((-1 * :a) / :b)
  make "rr :c + :pi / 2

  ; 地球赤道半径(メートル)
  make "earth_radius 6378140

  ; 2点間の距離(メートル)
  make "distance :earth_radius * :rr

  output :distance
end

; 計算サンプル
print distance 35.1730990 136.883466 35.1855732 136.899092
print distance 35.1855732 136.899092 35.1730990 136.883466

OCaml

(* 球面三角法により、大円距離(メートル)を求める *)
let distance lat1 lng1 lat2 lng2 =

  (* 円周率 *)
  let pi = 3.14159265359 in

  (* 緯度経度をラジアンに変換 *)
  let rlat1 = lat1 *. pi /. 180. in
  let rlng1 = lng1 *. pi /. 180. in
  let rlat2 = lat2 *. pi /. 180. in
  let rlng2 = lng2 *. pi /. 180. in

  (* 2点の中心角(ラジアン)を求める *)
  let a = sin rlat1 *. sin rlat2 +. cos rlat1 *. cos rlat2 *. cos (rlng1 -. rlng2) in
  let rr = acos a in

  let earth_radius = 6378140. in (* 地球赤道半径(メートル) *)
  let distance = earth_radius *. rr in

  distance;;

(* 計算サンプル *)
print_endline (string_of_float (distance 35.1730990 136.883466 35.1855732 136.899092));;
print_endline (string_of_float (distance 35.1855732 136.899092 35.1730990 136.883466));;

Ruby

# 球面三角法により、大円距離(メートル)を求める
def distance(lat1, lng1, lat2, lng2)

  # 緯度経度をラジアンに変換
  rlat1 = lat1 * Math::PI / 180
  rlng1 = lng1 * Math::PI / 180
  rlat2 = lat2 * Math::PI / 180
  rlng2 = lng2 * Math::PI / 180

  # 2点の中心角(ラジアン)を求める
  a =
    Math::sin(rlat1) * Math::sin(rlat2) +
    Math::cos(rlat1) * Math::cos(rlat2) *
    Math::cos(rlng1 - rlng2)
  rr = Math::acos(a)

  earth_radius = 6378140 # 地球赤道半径(メートル)
  earth_radius * rr
end

# 計算サンプル
puts distance(35.1730990, 136.883466, 35.1855732, 136.899092)
puts distance(35.1855732, 136.899092, 35.1730990, 136.883466)

Rust

fn main() {
  // 計算サンプル
  println!("{}", distance(35.1730990, 136.883466, 35.1855732, 136.899092));
  println!("{}", distance(35.1855732, 136.899092, 35.1730990, 136.883466));
}

// 球面三角法により、大円距離(メートル)を求める
fn distance(lat1: f64, lng1: f64, lat2: f64, lng2: f64) -> f64 {

  // 緯度経度をラジアンに変換
  let rlat1 = lat1.to_radians();
  let rlng1 = lng1.to_radians();
  let rlat2 = lat2.to_radians();
  let rlng2 = lng2.to_radians();

  // 2点の中心角(ラジアン)を求める
  let a =
    rlat1.sin() * rlat2.sin() +
    rlat1.cos() * rlat2.cos() *
    (rlng1 - rlng2).cos();
  let rr = a.acos();

  let earth_radius = 6378140.; // 地球赤道半径(メートル)
  earth_radius * rr
}

Scratch

distance_scratch.png

Swift

import Foundation

// 球面三角法により、大円距離(メートル)を求める
func distance(lat1: Double, lng1: Double, lat2: Double, lng2: Double) -> Double {
    
  // 緯度経度をラジアンに変換
  let rlat1 = lat1 * M_PI / 180
  let rlng1 = lng1 * M_PI / 180
  let rlat2 = lat2 * M_PI / 180
  let rlng2 = lng2 * M_PI / 180
    
  // 2点の中心角(ラジアン)を求める
  let a =
    sin(rlat1) * sin(rlat2) +
    cos(rlat1) * cos(rlat2) *
    cos(rlng1 - rlng2)
  let rr = acos(a)
    
  // 地球赤道半径(メートル)
  let earth_radius = 6378140.0
    
  // 2点間の距離(メートル)
  let distance = earth_radius * rr
    
  return distance
}

// 計算サンプル
print("\(distance(lat1: 35.1730990, lng1: 136.883466, lat2: 35.1855732, lng2: 136.899092))")
print("\(distance(lat1: 35.1855732, lng1: 136.899092, lat2: 35.1730990, lng2: 136.883466))")

参考資料

35
32
15

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
35
32

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?