2012年6月25日月曜日

Powershellで2点間の距離計算

PowerShell覚え書き

背景

 個人的お遊びです。地球上の2地点間の距離を計算してみたくなりました。

お題

 地球上の2地点について、Google Geocoding APIで住所から緯度経度を取得し、その距離(キロメートルとマイル)を計算して表示する。

param( [String]$address1="関西国際空港",[String]$address2="北京国際空港")

$radius = 6378.137 #地球の赤道半径(単位:キロメートル)

function address2latlng([String]$address)
{
    $encoded_address=[System.Uri]::EscapeDataString($address)
    $google_geocode_api_uri="http://maps.googleapis.com/maps/api/geocode/xml?address="+
                            $encoded_address+"&sensor=false&language=ja"

    $enc = [System.Text.Encoding]::GetEncoding("utf-8")
    $bytes=(New-Object System.Net.WebClient).DownloadData($google_geocode_api_uri)
    $xml=[XML]$enc.getString($bytes)
    $xml.GeocodeResponse.result|
        %{ @{ lat = $_.geometry.location.lat; lng = $_.geometry.location.lng; address = $_.formatted_address} }
}

function sin($r){[math]::sin($r)}
function cos($r){[math]::cos($r)}
function acos($r){[math]::acos($r)}
function d2r($d){ $d /180 * [math]::pi } #degreeからradianに変換

function distance($latlng1,$latlng2)
{
    $x1 = d2r $latlng1["lng"] ;$y1 = d2r $latlng1["lat"]
    $x2 = d2r $latlng2["lng"] ;$y2 = d2r $latlng2["lat"]

    $delta_x = $x2 - $x1
    $radius * (acos ( (sin $y1) * (sin $y2) + (cos $y1) * (cos $y2) * (cos $delta_x) ) )
}

$l1 = address2latlng $address1
$l2 = address2latlng $address2
$d = distance $l1 $l2

$l1;$l2;
$d.ToString() + "km"
($d/1.609334).ToString() + "mi"


結果

    Name Value
    ---- -----
    address 日本, 〒549-0001 大阪府泉佐野市泉州空港北1 関西国際空港
    lat 34.4348875
    lng 135.2443930
    address 中華人民共和国 北京 朝陽区 首都国际机场 (PEK)北京首都国際空港 (PEK) 邮政编码: 100621
    lat 40.0753910
    lng 116.5921350
    1763.70556211725km
    1095.92263763597mi


メモ

  • 2点間の距離についてはこのページを参考にしました。この計算は地球が完全な球形と仮定している(今回は赤道半径を使用)ので、実際とは誤差が出るそうです。(極半径は赤道半径より約21km小さいので、計算結果は極に近づくほど実際より少し大きめになる)
  • 関西国際空港と北京国際空港の場合にはマイレージ表とほぼ一致するのですが、成田国際空港と北京国際空港ではけっこう(10マイル以上)誤差が出ました。計算の誤差なのか、マイル計算が前提とする空港の緯度経度がgeocoding apiの結果と違うのかは不明です。おそらく後者なんでしょうね。(数10mから1km以下のような短距離については特に誤差が確認できなかった。)
  • 距離計算の式を完結にするために三角関数のラッパー関数を定義しましたが、単純に[math]の関数を呼び出しているだけなので冗長な気がします。javaのimportのようなやり方はないものか。
  • 手元のマシンではdistance関数1回あたり2.3msかかっている。遅いが、[system.math]の問題か。


0 件のコメント:

コメントを投稿