経度から緯度の距離を求める方法

前回のブログでは、2点間の距離を求める方法を紹介しました。
前回のブログはmy-hobby : GoogleAPI~2つの住所から距離を求める~で確認できます。

今回はITとはかなりかけ離れていますが、番外編として前回のブログで紹介した計算方法の詳しい内容を紹介します。
まず前回の内容より1度あたりの2点間の緯度差から距離を求めるには
1.0061504(緯度差)×111Km
で計算する事が出来ますが、
なぜ経度差から距離を求めるのは、
4.2389033(経度差)×111Km×cos34.6524841(緯度)
となるのでしょうか?
※なぜ「cos34.6524841(緯度)」が必要なのか?

※前回までのおさらい

上記内容は1度あたりの距離ですが、説明を解りやすくする為に
まず地球の緯線と経線の外周の距離を見てみたいと思います。
【経線】
必ず極点(北極点、南極点)を通過する為、どの経度でも必ず同じ長さとなります。
※前回のブログより、
実際の値(赤道半径):6,378.137km
実際の値(極半径):6,356.752km
と説明しているので、実際には地球は楕円形をしていますが、今回は真円とみなし、かつ外周を4万Kmと仮定しています。

【緯線】
赤道上(北緯・南緯0度)では4万Kmですが、それぞれ極点に近づくにつれ、その距離は小さくなり、
極点(北緯・南緯90度)では、経線の外周は0Kmとなります。

●図1

つまり緯線の外周の距離は、緯度によって異なるのです。
北緯・南緯0度では4万Km、北緯・南緯90度では0Km。
では北緯30度では何kmになるのか?

それは以下の様な方法で求める事が出来ます。
まず北緯0度と北緯30度の地点に対し、地球の中心から線を引きます。
そうすると、角Aは30度になり、線1は北緯0度の外周の半径になります。
また線2も地球の中心から引いた線になるので線1と同じ長さになります。
●図2

・角A:30度
・線1=線2

次に北緯30度の地点から、地球の地軸に対して線を引きます。
そうすると、線3は北緯30度の外周の半径になります。
更に線1と線3は平行なので、角Bも30度となります。
●図3

・線1と線3は平行
・角B:30度

ここで話は変わって、cosは三角形の斜辺を1とした場合の、角Cに対する底辺の長さを示したものになるので、
cos30とした場合は、底辺の長さは0.866となります。
●図4

・斜辺の長さ:1
・底辺の長さ:0.866

図4で得た考え方を図3に当てはめると、
線1に対する線3の長さの比率が解ります。
●図5

・線1:1
・線3:0.866
・線1と線3の比率は1:0.866

線1、線3共に、北緯0度、北緯30度の外周の半径になるので、
線1、線3の比率が解れば、北緯0度の外周から、北緯30度の外周の距離も求まります。
これと同じ考えで、1度あたりの距離も同じ比率を使って求めることが出来ます。
これにより、北緯30度の1度あたりの距離は111Km×0.866=96.13Kmとなります。
●図6

・北緯30度の1度あたりの距離:96.13Km

これを先ほどの式に当てはめると
4.2389033(経度差)×111Km×cos34.6524841(緯度)
といった計算式になります。

なんか数学の授業をやっている様な気がするのは自分だけでしょうか?
最後まで読んで理解してくださった方、ありがとうございます。そしてご苦労様でした。。。
次回はまたITネタに戻します。

GoogleAPI~2つの住所から距離を求める~

前回までは、住所から緯度経度を取得する方法を紹介してきました。
前回の記事はmy-hobby : GoogleAPI~住所から緯度経度を取得する(その2)~に記載されています。

今回は2つの住所から、2点間のおおよその距離を測定する方法にチャレンジしてみたいと思います。
「おおよその距離」とはちょっと歯切れの悪い言い方ですが、
コレには理由があって、今回緯度経度を使って、2点間の距離を求める方法に
3平方の定理を使用するからです。

3平方の定理は、あくまで平面の世界において成立する定理であり、
地球の様に丸い(立体的な)物体に対しては正確な値は得られないので、
「おおよその距離」と表現しました。
ただ、日本の国土くらいの大きさならさほど誤差は出ないだろうという想定で、
今回この方法を使って実際に試してみたいと思います。

考え方は以下の通りです。
1.2地点の住所から緯度経度を求める。

2.2地点の緯度経度から、緯度差、経度差を求める。

経度差:139.7454109-135.5065076=4.2389033
緯度差:35.6586345-34.6524841=1.0061504

3.緯度差、経度差より、距離A、距離Bの距離を求める

距離A:4.2389033×111×cos34.6524841(※2)=387.0557915Km
距離B:1.0061504×111(※1)=111.6826944Km

※1については地球 – Wikipediaの「大きさ、質量、密度」に記載されている、以下の情報から経度、緯度それぞれの1度あたりの距離を算出しました。

赤道半径が6,378.137km、極半径が6,356.752km

計算式:半径×2×円周率÷360
実際の値(赤道半径):6,378.137km×2×3.14159÷360=111.3193968Km
実際の値(極半径):6,356.752km×2×3.14159÷360=110.9461584Km

上記の通り、地球は真円ではありませんが、これを考慮に入れると非常に計算式が難しくなるので、今回は地球を真円とみなし、1度あたりの距離も、111Kmで統一する事にします。

※2については、必ずしも1度あたりの距離が111Kmにはならないので、「cos34.6524841」はそれを補正する為の式となります。
詳細な説明は後日紹介します。

4.3平方の定理を使用し、距離Cを求める。

これらの内容をプログラムに組み込むと以下の様な感じになります。

■入力フォーム

■ソースコード

[vbnet]
Imports System.Xml
Imports System.Web
Imports System.Math

Public Class Form1
Private Sub btn_Search_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Search.Click

Dim xNode As XmlNodeList
Dim Enc_Address As String
Dim APIKey As String = "(各自で取得したAPIキー)"
Dim RequestUrl As String
Dim GeoCode() As String

Dim IdoWK As Double
Dim KeidoWK As Double
Dim KyoriWK As Double
Dim CosIdoWK As Double

If txt_Address.Text = "" Then
Exit Sub
End If

‘住所1の緯度経度を取得
Enc_Address = HttpUtility.UrlEncode(txt_Address.Text)
RequestUrl = "http://maps.google.com/maps/geo?&q=" & Enc_Address & "&output=xml&key=" & APIKey

Dim xDoc As XmlDocument = New XmlDocument
xDoc.Load(RequestUrl)

Dim nsmgr As XmlNamespaceManager = New XmlNamespaceManager(xDoc.NameTable)
nsmgr.AddNamespace("gmap", "http://earth.google.com/kml/2.0")
xNode = xDoc.SelectNodes("/gmap:kml/gmap:Response/gmap:Status/gmap:code", nsmgr)

If xNode.Item(0).InnerText <> "200" Then
Exit Sub
End If

xNode = xDoc.SelectNodes("/gmap:kml/gmap:Response/gmap:Placemark/gmap:Point/gmap:coordinates", nsmgr)
GeoCode = Split(xNode.Item(0).InnerText, ",")
txt_Keido.Text = GeoCode(0)
txt_Ido.Text = GeoCode(1)

‘住所2の緯度経度取得
Enc_Address = HttpUtility.UrlEncode(txt_Address2.Text)
RequestUrl = "http://maps.google.com/maps/geo?&amp;q=" &amp; Enc_Address &amp; "&amp;output=xml&amp;key=" &amp; APIKey

xDoc.Load(RequestUrl)
xNode = xDoc.SelectNodes("/gmap:kml/gmap:Response/gmap:Status/gmap:code", nsmgr)

If xNode.Item(0).InnerText <> "200" Then
Exit Sub
End If

xNode = xDoc.SelectNodes("/gmap:kml/gmap:Response/gmap:Placemark/gmap:Point/gmap:coordinates", nsmgr)
GeoCode = Split(xNode.Item(0).InnerText, ",")
txt_Keido2.Text = GeoCode(0)
txt_Ido2.Text = GeoCode(1)

‘2地点の緯度経度より、距離を計算
If txt_Ido.Text < txt_Ido2.Text Then
CosIdoWK = txt_Ido.Text
Else
CosIdoWK = txt_Ido2.Text
End If

IdoWK = Abs(txt_Ido.Text – txt_Ido2.Text) * 111
KeidoWK = Abs(txt_Keido.Text – txt_Keido2.Text) * Cos(Math.PI / 180 * CosIdoWK) * 111
KyoriWK = (IdoWK ^ 2 + KeidoWK ^ 2) ^ (1 / 2)

txt_Kyori.Text = KyoriWK

End Sub
End Class
[/vbnet]

■実行結果

結果として、402.846385062963Km≒402.85Kmとなりました。
それでは測量計算(距離と方位角の計算)より実際の距離を測ってみたいと思います。

国土地理院のページなので、信頼性が高いと思い、このページで得られた結果を正解とします。
結果は401.99Kmとなりました。
誤差は0.86Km(0.2%の誤差)。予想以上に正確な値が出ました。

計算方法は「別途文献を見て下さい」との事でした。
「文献」と書いていある時点で難しそうなので、今回はここまでとします。

ちなみに、これを応用すれば、例えば日本全国の日帰り温泉の緯度経度情報を予め調べておき、ある地点から一番近い温泉を調べるといった事も出来るようになります。
GPSを搭載した携帯を持っていれば、郊外に出かけた際、ふと温泉に入りたくなった時に近場の日帰り温泉を探すのに便利ですね。。。

後日の調査で別の計算方式を載せた記事がありますので、よろしければ参考にしてください。
my-hobby : 2地点の緯度経度から距離を求める(Goole MAPS API ~距離の計算式編~)

GoogleAPI~住所から緯度経度を取得する(その2)~

前回は、Google MAPSのWeb APIを使用して、緯度経度を含んだ情報をXML形式で取得する方法を紹介しました。
前回の情報はmy-hobby : GoogleAPI~住所から緯度経度を取得する(その1)~で確認できます。

今回はGoogleAPIを使用して取得したXMLの仕様を確認し、そこから緯度経度を取得してみたいと思います。

緯度経度を取得するに当たり、サービス – Google Maps API – Google Codeを参考にしました。
なおリンク先の説明はJSONになってますが、実際に動作テストした結果、リンク先に記載されている内容と合致していたので、今回はこれを基にプログラムを作成しました。

その内容から見るべきXPATHは以下の2つと考えました。
1.kml/Response/Status/code
リターンコードを示しています。
「200」が正常終了。それ以外は今回の処理では異常終了とみなす事にします。
リターンコードの種類についてはGoogle Maps API リファレンス – Google Maps API – Google Codeに記載されています。

2.kml/Response/Placemark/Point/coordinates
リクエストされた住所の 緯度、経度、高度 をカンマ区切りで示しています。
※高度は常に0が返されます。

上記内容を踏まえてWindowsフォームより入力した住所に対して、GoogleAPIを使用して緯度と経度を求めます。
■入力フォーム

■ソースコード

[vbnet]
Imports System.Xml
Imports System.Web
Public Class Form1

Private Sub btn_Search_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Search.Click

Dim xNode As XmlNodeList
Dim Enc_Address As String
Dim APIKey As String = "(各自で取得したAPIキー)"
Dim RequestUrl As String
Dim GeoCode() As String

If txt_Address.Text = "" Then
Exit Sub
End If

Enc_Address = HttpUtility.UrlEncode(txt_Address.Text)
RequestUrl = "http://maps.google.com/maps/geo?&amp;q=" &amp; Enc_Address &amp; "&amp;output=xml&amp;key=" &amp; APIKey

Dim xDoc As XmlDocument = New XmlDocument
xDoc.Load(RequestUrl)

Dim nsmgr As XmlNamespaceManager = New XmlNamespaceManager(xDoc.NameTable)
nsmgr.AddNamespace("gmap", "http://earth.google.com/kml/2.0")
xNode = xDoc.SelectNodes("/gmap:kml/gmap:Response/gmap:Status/gmap:code", nsmgr)

If xNode.Item(0).InnerText &lt;&gt; "200" Then
Exit Sub
End If

xNode = xDoc.SelectNodes("/gmap:kml/gmap:Response/gmap:Placemark/gmap:Point/gmap:coordinates", nsmgr)

GeoCode = Split(xNode.Item(0).InnerText, ",")
txt_Keido.Text = GeoCode(0)
txt_Ido.Text = GeoCode(1)

End Sub
End Class[/vbnet]

■実行結果

ちなみにGoogleAPIの仕様上は、施設名などでも緯度経度の検索は可能ですが、複数ヒットした場合、このプログラムでは考慮されていません。
複数ヒットの場合は、最初のデータが使用されます。

※施設名(東京タワー)で検索した場合

次回は2つの住所から、2点間のおおよその距離を求める方法にチャレンジしてみたいと思います。

GoogleAPI~住所から緯度経度を取得する(その1)~

郵便番号構想を始めてから、住所に関する事にどっぷりと浸かっています。
郵便番号以外にも、住所から何か検索できるものは無いか・・・
住所を使って他にも出来る事は無いか・・・

ふと2つの住所を使って、2点間の距離を求める事が出来たら面白いのではないか?
なんて事を考えて見ました。
ただ住所だけでは2点間の距離を求めることは出来ないので、住所からある情報を検索してそこから2点間の距離を求める方法を考えました。

それは緯度と経度です。
ただ、住所から緯度と経度を検索する事なんで出来るのでしょうか?

出来ます。
それはGoogle MAPSです。
Web APIとしてGoogle MAPSがWeb APIを公開していました。

ただし、GoogleのWeb APIを使うためには、Google APIキー(ライセンスキーの様なもの)が必要という事なので、
先ずはGoogle APIキーの取得を行ないます。

Google APIキーは以下のサイトより入手できます。
Google Maps API – Google Code

より詳細な取得方法はGeekなぺーじ : Google MAPS API keyを取得するが参考になると思います。
※このページではJavaScriptによる緯度経度の取得方法も記載されています。
今回の記事を作成するにあたっても、参考にさせていただきました。

JavaScriptで実装する場合は、専用のオブジェクトが用意されていますが、
VB.netで実装する場合はURIにパラメータを直接指定して、
その戻り値(レスポンス)を元に緯度経度を取得する形式となります。
詳細はこちら
今回は戻り値をXML形式で取得し、そこから緯度経度を抽出する方式とします。

先ずはGoogleAPIにリクエストするURIの仕様は以下の通りです。

ジオコーディングとは、住所 (「1600 Amphitheatre Parkway, Mountain View, CA」など) を地理座標 (緯度37.423021、経度 -122.083739など) に変換する処理のことです。地理座標を使用して、マーカーを配置したり地図の位置決めを行うことができます。Google Maps API は、HTTP 要求を経由して直接アクセスするか、または GClientGeocoder オブジェクトを使用してアクセスできるジオコーディング サービスを備えています。

(中略)

HTTP 要求経由のジオコーディング
サーバー側スクリプトを使用して Maps API ジオコーダに直接アクセスするには、URI
に次のパラメータを指定して http://maps.google.com/maps/geo? に要求を送信します。
q — ジオコーディングする住所。
key — API キー。
output — 生成される出力の形式。xml、kml、csv、または json を指定できます。

※ジオコーディングする住所はURLエンコードが必要です。

【例】住所:日本東京都港区芝公園4丁目2-8とした場合
http://maps.google.com/maps/geo?&q=%93%8C%8B%9E%93s%8D%60%8B%E6%8E%C5%8C%F6%89%80%82S%92%9A%96%DA2-8&output=xml&key=(各個人で取得したAPIキー)

試しにこれをブラウザのアドレスに直接入力すると、
以下の様な結果が表示されます。

これをプログラムで読み込むには以下のコードを記述します。
■入力フォーム

■ソース

[vbnet]
Imports System.Xml
Imports System.Web

Public Class Form1
Private Sub btn_Search_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles btn_Search.Click

Dim Enc_Address As String
Dim APIKey As String = "(各個人で取得したAPIキー)"
Dim RequestUrl As String

If txt_Address.Text = "" Then
Exit Sub
End If

Enc_Address = HttpUtility.UrlEncode(txt_Address.Text)
RequestUrl = "http://maps.google.com/maps/geo?&amp;q=" &amp; Enc_Address &amp; "&amp;output=xml&amp;key=" &amp; APIKey

Dim xDoc As XmlDocument = New XmlDocument

xDoc.Load(RequestUrl)
Console.Write(xDoc.InnerXml)

End Sub
End Class[/vbnet]

■デバッグ

[xml]
&lt;?xml version="1.0" encoding="UTF-8"?&gt;&lt;kml xmlns="http://earth.google.com/kml/2.0"&gt;&lt;Response&gt;&lt;name&gt;日本東京都港区芝公園4丁目2?8&lt;/name&gt;&lt;Status&gt;&lt;code&gt;200&lt;/code&gt;&lt;request&gt;geocode&lt;/request&gt;&lt;/Status&gt;&lt;Placemark id="p1"&gt;&lt;address&gt;4丁目2 8 Shiba Park, Minato, Tokyo, Japan&lt;/address&gt;&lt;AddressDetails Accuracy="8" xmlns="urn:oasis:names:tc:ciq:xsdschema:xAL:2.0"&gt;&lt;Country&gt;&lt;CountryNameCode&gt;JP&lt;/CountryNameCode&gt;&lt;CountryName&gt;Japan&lt;/CountryName&gt;&lt;AdministrativeArea&gt;&lt;AdministrativeAreaName&gt;Tokyo&lt;/AdministrativeAreaName&gt;&lt;Locality&gt;&lt;LocalityName&gt;Minato&lt;/LocalityName&gt;&lt;DependentLocality&gt;&lt;DependentLocalityName&gt;Shiba Park&lt;/DependentLocalityName&gt;&lt;Thoroughfare&gt;&lt;ThoroughfareName&gt;4丁目2?8&lt;/ThoroughfareName&gt;&lt;/Thoroughfare&gt;&lt;/DependentLocality&gt;&lt;/Locality&gt;&lt;/AdministrativeArea&gt;&lt;/Country&gt;&lt;/AddressDetails&gt;&lt;ExtendedData&gt;&lt;LatLonBox north="35.6617821" south="35.6554869" east="139.7485585" west="139.7422633" /&gt;&lt;/ExtendedData&gt;&lt;Point&gt;&lt;coordinates&gt;139.7454109,35.6586345,0&lt;/coordinates&gt;&lt;/Point&gt;&lt;/Placemark&gt;&lt;/Response&gt;&lt;/kml&gt;[/xml]

XmlDocumentオブジェクトを使用すると、非常に簡単に結果を取得する事が出来ます。
次回は取得したXML情報から、緯度と経度を取得する方法を紹介します。