株式会社ライブキャストロゴ 株式会社ライブキャスト

flashcast:フリーで働くITエンジニア集団のブログ: iPhone OS 3.0のSafariでGPS機能を使ったWeb Applicationを作る!(GPS編)の続きです。

前回作ったサンプルアプリiPhone GPS Sampleを実機でテストしてみました。実際にテストしてみていくつかわかったことがあります。

  • 移動してなくても現在地の緯度・経度の値が変わる場合がある。
  • 数歩歩くだけで変化する。

どちらも当たり前のことなんですが、実際に動きながらテストしてはじめて気付かされました。

以下のようなコードで、現在地の緯度・経度の値が変更になったときのイベントを受け、現在地住所を検索するようにしているので、

 function onLocationChanged(e) {
  lat = e.coords.latitude;
  lng = e.coords.longitude;
  getAddress(new GLatLng(lat, lng));
 }

地図上の吹き出しが表示されたり、消えたりを繰り返し、うざいです。なので、ある程度移動すると住所情報を再表示することにしました。

ある程度、はどれぐらいにしたら良いかを考えます。

緯度1秒の長さ(赤道上) 約30.7m
緯度1秒の長さ(緯度35度上) 約30.8m
緯度1秒の長さ(緯度90度上) 約31.0m

緯度 – Wikipediaより引用。

場所にもよりますが緯度1秒は約30メートルです。

経度1秒の長さ(赤道上) 約31m
経度1秒の長さ(緯度35度上) 約25m
経度1秒の長さ(緯度90度上) 0m

経度 – Wikipediaより引用。

経度の場合、場所による1秒の長さの違いは、緯度の場合より顕著です。
緯度35度上で約25メートルということなので、日本を基準にして、現在地の緯度・経度ともに1秒(約30メートル)以上変化した場合に住所を再表示するようにしたいと思います。

まずは、移動前の緯度・経度と移動後の緯度・経度から2点間の移動距離を求めます。

Google Maps APIに便利なメソッドがありました。
GLatLngクラスのdistanceFromメソッドです。

この地点から指定の地点までの距離をメートル単位で返します。デフォルトでは、この距離はデフォルトの地球の赤道半径 6,378,137 メートルに基づいて算出されます。地球を球形として概算するので、距離には特に極点付近で最大で 0.3% の誤差が生じます。オプションの radius 引数を渡して、地球とは異なる半径を持つ球体の GLatLng 座標間の距離を算出することもできます。

Google Maps API リファレンス – Google Maps API – Google Codeより引用。

これは便利!今回はこれを利用します。

 function getDistance(srclat, srclng, dstlat, dstlng) {
  var src = new GLatLng(srclat, srclng);
  var dst = new GLatLng(dstlat, dstlng);
  return dst.distanceFrom(src);
 }

リターン値はメートルになるようなので30メートル(1秒=約30メートル)以上移動したかをチェックします。
これを踏まえると、緯度・経度変化時のコールバック関数は以下のようになります。

 function onLocationChanged(e) {
  if (getDistance(lat, lng, e.coords.latitude, e.coords.longitude) >= 30)
  {
   lat = e.coords.latitude;
   lng = e.coords.longitude;
   getAddress(new GLatLng(lat, lng));
  }
 }

Google Maps APIを利用する上での注意点があります。

注: 逆ジオコーディングは科学的に正確ではありません。ジオコーダは、一定の許容範囲内で最も近いアドレス可能な場所を探そうとします。一致するものがない場合、ジオコーダは通常、G_GEO_UNKNOWN_ADDRESS (602) ステータス コードを返します。

サービス – Google Maps API – Google Codeより引用。

GPSの緯度・経度とGoogle Maps APIを呼び出した結果の緯度・経度が異なる場合があります。要するに、取得できた住所は、意図した地点の住所ではない場合があります。なので、これを理解した上で、住所表示のロジックを実装します。

これを踏まえると、Google Maps APIのコールバック関数は以下のようになります。

 function showAddress(response, latlng) {
  if (response != null && response.Status.code == 200) {
   map.clearOverlays();
   place = response.Placemark[0];
   marker = new GMarker(latlng);
   map.addOverlay(marker);
   marker.openInfoWindowHtml('<b>Address:</b>' + place.address);
  }
  else {
   alert("Not found.");
  }
 }

まずますの精度になったと思います。もし良かったら、試してみてください!
iPhone GPS Sample