PhoneGap(Cordova) Geolocation API を使おうとしてハマった話

Geolocation API 自体はわりと単純です

安定の公式ドキュメント嫁
Apache Cordova API Documentation
まあとりあえずパーミッションを設定します。これも公式にのってる。ただし、エミュレータ用には以下のパーミッションも必要

<uses-permission android:name="android.permission.ACCESS_MOCK_LOCATION"/>

参考::勉強会/GPS - 日本Androidの会(日本アンドロイドの会)
で、現在位置をとってくるサンプルがあるのでちょっとデバッグ用に回収したものを張る。HTMLはてきとうに推測してください

// なんか最初から deviceready したら Geo とってくれない感じだったので
// jQuery Mobile の tap イベントで発火させた
// ちなみに tap イベントは live で、他のイベントは bind とかだったりよくわからん
// しかも jQuery のイベント発火まわりって on off 推奨になったはずなんだが……
$("h2").live('tap', setListener);

// まあリスナー貼りますと
function setListener() {
    alert("set litener");
    document.addEventListener("deviceready", onDeviceReady, false);
}

// ここで navigator.geolocation.getCurrentPosition メソッド
function onDeviceReady() {
    alert('ready')
    navigator.geolocation.getCurrentPosition(onSuccess, onError, { enableHighAccuracy: true });
}

// 成功コールバック
// サンプルは文字列をベタ書きで + してたけど、なんかダルいので [seq].join('') した
function onSuccess(position) {
    alert('success');
    $("#debug").html(['Latitude: ',          position.coords.latitude, '<br />',
                      'Longitude: ',         position.coords.longitude, '<br />',
                      'Altitude: ',          position.coords.altitude, '<br />',
                      'Accuracy: ',          position.coords.accuracy, '<br />',
                      'Altitude Accuracy: ', position.coords.altitudeAccuracy, '<br />',
                      'Heading: ',           position.coords.heading, '<br />',
                      'Speed: ',             position.coords.speed, '<br />',
                      'Timestamp: ',         position.timestamp, '<br />'].join(''));
}

// 失敗コールバック
function onError(error) {
    alert(['code: ',    error.code,    '\n',
           'message: ', error.message, '\n'].join(''));
}

流れはみてもらえばわかる。geo 系のメソッドは getCurrentPosition, watchPosition, clearWatch くらいしかない。それ自体はそんなに難しくない。

Androidエミュレータでハマる話

エミュレータで Geolocation を有効にするには { enableHighAccuracy: true } しなくてはならない。公式ドキュメントにもあるが、はしっこにあるし見落としがちなポイント。
もうひとつは、エミュレータに現在位置をセットするには telnet でエミュレータにつないで geo fix でセットしないといけないことだ。具体的には

# エミュレータにつなぐ
telnet localhost 5554
# geo コマンドで設定
geo fix 12.34 43.21
# 抜ける
quit

ちなみになんで 5554 なんだよと思ったら Android エミュレータ側で振られるポートらしい。それぞれエミュレータのタイトルに表示されている。複数立ち上げた場合それで区別するようだ。
参考::KMC Staff Blog:Androidのemulator consoleに簡単にコマンドを送る方法
参考::Android Emulator having issues with Geolocation - Stack Overflow

Android 2.3 エミュレータで Geolocation が動かない話

散々ハマってたが、エミュレータのバグらしい。今回は 2.3, 4.x という要件なのでずっと 2.3 で開発してたからハマっていた。
諦めたので 2.2 でやってみたら無事に動いた。エミュレータのバグなのでたぶん実機だと動くと思う(というか実機で動かなかったらあまりに悲惨すぎるバグだ)
参考::Getting GPS on Android 2.3.1 and 2.3.3 - Stack Overflow
参考::Issue 13046 - android - Unable to use "geo fix" command on 2.3 emulator - Android - An Open Handset Alliance Project - Google Project Hosting

まあ

公式ドキュメント読んで「あー、これはまあ楽だねー」とか思ってたらこんなバグ踏んでハマるとはね……。
できれば次回はせっかく Geolocation API なので Google Maps を参照させるくらいのことはしたいかな。