皆様こんにちは。t.i.でございます。
今回はHTML5でGPSとカメラを利用するサンプルプログラムをご紹介します。今までのHTMLは「ブラウザに表示されるもの」という括りに収まっていましたが、HTML5ではデバイスと連携することが可能となりました。例えば、スマートフォンでGPSやカメラを利用するアプリを作りたい時、必ずしもJavaやObjective-Cを使う必要はありません。HTML(JavaScript)だけでアプリを作ることが可能なことを「その1 GPSを使ってみよう」と「その2 カメラを使ってみよう」でご紹介します。
その1 GPSを使ってみよう
初めに謝っておきますと、「GPSを使ってみよう」という表現は正確ではありません。正確には「位置情報を取得してみよう(取得する際に、GPSを使ってくれるかもしれない)」です。GPSを使う/使わないはブラウザが必要に応じて判断します(もちろん、できるだけブラウザがGPSを使ってくれるようなプログラムにはしています)。
位置情報の取得には、HTML5のGeolocation APIというものを使用します。これは、以下2つの機能を3つのAPIで実現しています。
- 位置情報を一回取得する (取得API: getCurrentPosition)
- 位置情報を監視しながら取得する(監視開始API: watchPosition、監視終了API: clearWatch)
■1. 位置情報を一回取得する
ソースコードにコメントが細かく書いてあります。下記の流れを把握した上で、ソースコードを参照してみて下さい。
- ボタンを押すと、start()を実行する
- start()ではブラウザがGeolocationAPIに対応しているかをチェックし、対応していれば位置情報取得処理を非同期で実行する
- 位置情報取得処理が終了すると、コールバック関数を呼ぶ
- 処理が成功した場合、successCallback()を呼びメッセージ表示
- 処理が失敗した場合、errorCallback()を呼びメッセージ表示
[html] <!DOCTYPE HTML> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>Geolocation API(getCurrentPosition) Sample</title> <script type="text/javascript"> // 位置情報取得処理に渡すオプション var options = { // 高精度な位置情報を取得するか(デフォルトはfalse) enableHightAccuracy: true, // 取得した位置情報をキャッシュする時間(ミリ秒。デフォルトは0) maximumAge: 0, // 何秒でタイムアウトとするか(ミリ秒。タイムアウトするとerrorCallback()がコールされる) timeout: 120000 } // 位置情報取得処理が成功した時にコールされる関数 // 引数として、coords(coordinates。緯度・経度など)とtimestamp(タイムスタンプ)の2つを持ったpositionが渡される function successCallback(position){ // メッセージを表示 document.getElementById("message").innerHTML += "API成功<br />"; // 引数positionからcoordinates(緯度・経度など)を取り出す // ただし、“★必ず取得できる”以外は中身が空っぽの可能性もある // 緯度(-180~180度) ★必ず取得できる var latitude = position.coords.latitude; // 経度(-90~90度) ★必ず取得できる var longitude = position.coords.longitude; // 高度(m) var altitude = position.coords.altitude; // 緯度・経度の誤差(m) ★必ず取得できる var accuracy = position.coords.accuracy; // 高度の誤差(m) var altitudeAccuracy = position.coords.altitudeAccuracy; // 方角(0~360度) var heading = position.coords.heading; // 速度(m/秒) var speed = position.coords.speed; // 引数positionからtimestamp(位置情報を取得した時刻のミリ秒)を取り出す ★必ず取得できる var timestamp = position.timestamp; // timestampをDate型に変換 var successDate = new Date(timestamp); // メッセージを表示 document.getElementById("message").innerHTML += "取得日時:" + successDate.toLocaleString() + "<br />"; document.getElementById("message").innerHTML += "緯度:" + latitude + " 度<br />"; document.getElementById("message").innerHTML += "経度:" + longitude + " 度<br />"; document.getElementById("message").innerHTML += "高度:" + altitude + " m<br />"; document.getElementById("message").innerHTML += "緯度・経度の誤差:" + accuracy + " m<br />"; document.getElementById("message").innerHTML += "高度の誤差:" + altitudeAccuracy + " m<br />"; document.getElementById("message").innerHTML += "方角:" + heading + " 度<br />"; document.getElementById("message").innerHTML += "速度:" + speed + " m/秒<br />"; // 緯度・経度を地図上で確認するためにGoogleMapへのリンクを作成 document.getElementById("message").innerHTML += "<a target='_blank' href='https://maps.google.co.jp/maps?q=" + latitude + "," + longitude + "+%28%E7%8F%BE%E5%9C%A8%E4%BD%8D%E7%BD%AE%29&iwloc=A'>緯度・経度をGoogleMapで確認</a><br /><br />"; } // 位置情報取得処理が失敗した時にコールされる関数 // 引数として、code(コード)とmessage(メッセージ)の2つを持ったpositionErrorが渡される function errorCallback(positionError){ // メッセージを表示 document.getElementById("message").innerHTML += "API失敗<br />"; // 引数positionErrorの中身2つを取り出す // コード(1~3のいずれかの値) var code = positionError.code; // メッセージ(開発者向けデバッグ用メッセージ) var message = positionError.message; // コードに応じたメッセージを表示 switch (code) { case positionError.PERMISSION_DENIED: // codeが1 document.getElementById("message").innerHTML += "GeolocationAPIのアクセス許可がありません<br />"; break; case positionError.POSITION_UNAVAILABLE: // codeが2 document.getElementById("message").innerHTML += "現在の位置情報を特定できませんでした<br />"; break; case positionError.TIMEOUT: // codeが3 document.getElementById("message").innerHTML += "指定されたタイムアウト時間内に現在の位置情報を特定できませんでした<br />"; break; } // 開発者向けデバッグ用メッセージを表示 document.getElementById("message").innerHTML += message + "<br /><br />"; } // 位置情報取得開始 function start(){ // メッセージ表示領域をクリア document.getElementById("message").innerHTML = ""; // ブラウザがGeolocation APIに対応しているかをチェック if (navigator.geolocation) { // 対応している → 位置情報取得開始 // 位置情報取得成功時にsuccessCallback()、そして取得失敗時にerrorCallback()がコールされる // optionsはgetCurrentPosition()に渡す設定値 navigator.geolocation.getCurrentPosition(successCallback, errorCallback , options); // メッセージを表示。↑は非同期処理なので、直ぐにメッセージが表示される document.getElementById("message").innerHTML += "API実行<br />"; } else { // 対応していない → alertを表示するのみ alert("Geolocation not supported in this browser"); } } </script> </head> <body> <h1>Geolocation API(getCurrentPosition) Sample</h1> <button onclick="start()">位置情報取得開始(getCurrentPosition)</button> <div id="message"></div> </body> </html> [/html]
行数が多いように感じますが、コメント等の不要な部分を削除すればコンパクトになります。
それでは実際に動かしてみましょう。具体的には、HTMLファイルをブラウザで表示するだけです。最近のブラウザであれば、PC、スマートフォン共に対応していないものはまず無いと思います。
- GeolocationAPIを動かすには、OSやブラウザに設定が必要になる可能性があります
- ブラウザによっては、ローカルにあるHTMLファイルに対してはGeolocationAPIが動かない可能性もあります
- Android4.3&FirefoxでGPSが利用されることを確認しています(「位置情報にアクセス」と「GPS機能」を ON、「Wi-Fi/モバイル接続時の位置情報」と「Wi-Fiとモバイルデータ」をOFF)
- iPhone(iOS7)でもGPSが利用されることを確認しています(位置情報サービス許可をSafariに設定)

実際に私のデスクトップPC(Windows7。GPSやWiFiなし)で実行した所、以下のようになりました。
それぞれのブラウザの緯度・経度は以下の通りです。
Firefox/Chrome/Operaは同じ方法でIPアドレスから緯度・経度を取得していると予想できます。実際は東京都千代田区外神田4-9-8ですので、IPアドレスからだけでもある程度の位置情報が取得できることがわかります。
■2. 位置情報を監視しながら取得する
「1. 位置情報を一回取得する」とほとんど同じです。異なるのは「監視停止するまで、ずっと位置情報を監視し続ける(位置情報が変更される度にコールバック関数が呼ばれる)」という点で、start()とstop()以外の処理は全く同じです。
ソースファイルをダウンロード
実際にブラウザで開く
[html] ~省略~ // watchPosition()の戻り値。この戻り値をclearWatch()に渡すことでwatch(監視)を停止させる。 var watchId = null; // 位置情報監視開始 function start(){ // watchIdが設定されているかをチェック if (watchId == null) { // 設定されていない → 監視中ではないのでwatch開始 // メッセージ表示領域をクリア document.getElementById("message").innerHTML = ""; // ブラウザがGeolocation APIに対応しているかをチェック if (navigator.geolocation) { // 対応している → 位置情報監視開始 // 取得成功時にsuccessCallback()、そして取得失敗時にerrorCallback()がコールされる // optionsはgetCurrentPosition()に渡す設定値 watchId = navigator.geolocation.watchPosition(successCallback, errorCallback , options); // メッセージを表示。↑は非同期処理なので、直ぐにメッセージが表示される document.getElementById("message").innerHTML = "API実行<br />"; } else { // 対応していない → alertを表示するのみ alert("Geolocation not supported in this browser"); } } } // 位置情報監視停止 function stop(){ // watchIdが設定されているかをチェック if (watchId != null) { // 設定されている → 監視中なのでwatch停止 navigator.geolocation.clearWatch(watchId); // watchIdを空にする watchId = null; // メッセージを表示 document.getElementById("message").innerHTML += "clearWatch実行(watch停止)<br />"; } } </script> </head> <body> <h1>Geolocation API(watchPosition/clearWatch) Sample</h1> <button onclick="start()">位置情報監視開始(watchPosition)</button> <button onclick="stop()">位置情報監視停止(clearWatch)</button> <div id="message"></div> </body> </html> [/html]

こちらはスマートフォンなど持ち運びが容易なもので動かしてみてください。先ほどのサンプルと同様にHTMLファイルをブラウザで表示するだけです。
その2 カメラを使ってみよう
カメラを使うには、HTML5の以下の仕様を使用します。
前者のHTML Media Captureを一言で言いますと「カメラやマイクから取得したデータをファイルとしてアップロードする仕様」です。取得したデータをJavaScriptで取り扱うには、別のHTML5の機能(FileAPI)でローカルにあるファイルを読み込む必要があります。
そして後者のMedia Capture and Streamsを一言で言いますと「カメラやマイクからストリームデータを取得する(リアルタイム取得する)仕様」です。
後者の方が多くのことをできるのですが、今回はサンプルということで簡単な前者を使います。
■カメラから取得したデータをファイルとしてアップロードする
これは非常に簡単です。ポイントは10行目の「accept=”image/*” capture=”camera”」のみです。acceptとcaptureを変更することで、マイクの音声をアップロードすることも可能になります(accept=”audio/*” capture=”microphone”)。
ソースファイル(とupload.php)をダウンロード
[html] <!DOCTYPE HTML> <html lang="ja"> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8"> <title>HTML Media Capture Sample</title> </head> <body> <h1>HTML Media Capture Sample</h1> <form action="upload.php" method="post" enctype="multipart/form-data"> <input type="file" name="capture" accept="image/*" capture="camera" /> <input type="submit" value="Upload" /> </form> </body> </html> [/html]

実際に動かしてみましょう。こちらもHTMLファイルをブラウザで表示するだけです。ファイル選択ボタンを押すとカメラが起動し、Uploadボタンを押すとカメラが撮影した画像をサーバにUploadします。
※ブラウザによってはカメラが起動せず、ローカルファイル選択になります
※Android 4.3(標準ブラウザ、Chrome30.0.1599.82)で動作確認をしています
(以下はFirefoxの画面)
おわりに
今回はGPSとカメラを利用するサンプルプログラムをご紹介しました。容易にHTMLと連携が取れることがわかっていただけたと思います。是非とも活用してみて下さい。また、今回サンプルを紹介できなかったMedia Capture and Streamsをうまく使えば、ブラウザだけでビデオチャットをすることも夢ではありません。興味のある方は「WebRTC」をキーワードに調べてみて下さい。
それでは次回をお楽しみに。