HTML5 アプリケーションをビルドする

Web アプリケーションに地理位置情報を組み込む

Brandon Satrom

コード サンプルのダウンロード

スマートフォンをお持ちであれば、アプリケーションや組み込み機能を利用して、現在地から目的地までの道順を調べたり、知らない町で近くのレストラン、ホテル、ガソリンスタンドを探した経験がおありでしょう。技術用語では、この機能を地理位置情報と言い、デバイスで外部位置データ (全地球測位システム [GPS]、携帯電話基地局による三角測量、および Wi-Fi データ) を使用してユーザーの位置を特定し、位置データを必要とするアプリケーションやサービスに取得した情報を提供する機能のことです。通常、位置情報は緯度や経度、針路、速度、方向などのデータを使用して表されます。デバイスは、こうした情報を基にして、ターン バイ ターン ナビゲーションなどの機能を提供したり、午前 2 時にオープンする近くの Taco Truck (メキシコ料理の屋台) の位置を特定できるようにしています。

モバイル デバイスでは当たり前のように使用されている地理位置情報ですが、本来のモバイル アプリケーション以外にも急速に普及してきています。GPS チップを搭載したノートパソコンやモバイル PC が増え、モバイル アプリケーション開発の発展性のあるプラットフォームとして Web が急激に普及し、ブラウザーで地理位置情報サービスを利用する需要が高まっています。

さいわい、W3C はこの傾向にすばやく着目し、Web アプリケーションでの地理位置情報サービスの標準利用方法を規定する仕様を策定しました。この仕様 (bit.ly/3r737c、英語) は HTML5 に "正式" には含まれていませんが、この連載では、一連のオープン Web テクノロジを表すのに (W3C と同様に) 「HTML5」という用語を使用しているため、この優れたテクノロジについても概要を説明しないわけにはいかないでしょう。

今月は、Geolocation API の概要とその使用方法について説明し、地理位置情報の要求中に発生する、最も一般的な種類のエラーに対処する方法を紹介します。その後、デスクトップ ブラウザーとモバイル ブラウザーに地理位置情報を実装する方法を説明し、最後に、古いブラウザーを使用しているユーザーにポリフィル ライブラリを使用して基本的な地理位置情報サポートを提供する方法を紹介します。

地理位置情報入門

W3C の仕様によれば、地理位置情報とは「ホスト デバイスに関連付けられた地理上の位置情報へのスクリプト アクセスを提供する API」です。地理位置情報は組み込み API なので、地理位置情報をサポートするブラウザーさえあれば使用できます。この記事の執筆時点では、(Windows Phone 7.5、コードネーム "Mango" にインストールされた Internet Explorer 9 を含む) Internet Explorer 9 と、Chrome、Firefox、Safari、および Opera の最新バージョンは、すべて地理位置情報をサポートしています。つまり、この仕様は広くサポートされているため、多くのユーザーが利用可能です。

Geolocation API は、JavaScript のグローバルな "navigator" オブジェクト内 (window.navigator.geolocation) に存在します。位置情報サービスには、単発の位置要求と反復的な位置更新の 2 つの使用方法があります。単発の位置要求を行うには、getCurrentPosition メソッドを次のように使用します。

navigator.geolocation.getCurrentPosition(function(position) {
       var coordinates = position.coords;
       console.log("Your latitude: " + coordinates.latitude + " and longitude:
         " + coordinates.longitude + " (Accuracy of:
         " + coordinates.accuracy + " meters)");
}, errorHandler, { maximumAge: 100, timeout: 6000, enableHighAccuracy: true});

getCurrentPosition メソッドは、3 つのパラメーターを受け取ります。1 つ目は、ブラウザーがデバイスの現在地の特定に成功した場合に呼び出すコールバック関数です。2 つ目は、エラーの発生時に呼び出すコールバック関数です (これについては、後で詳しく説明します)。3 つ目は、地理位置情報のオプションを指定するためのオブジェクト リテラルです。利用できるオプションとその既定値を、図 1 に一覧します。

図 1 地理位置情報のオプション

オプション 説明
maximumAge 取得した位置情報をキャッシュしておく時間長 (ミリ秒単位) 0 ~ Infinity (既定値は 0)
Timeout 位置情報サービスが応答するまで待機する時間長 (ミリ秒単位) 0 ~ Infinity (既定値は Infinity)
enableHighAccuracy ブラウザーおよびデバイスでサポートしている高精度の位置情報サービス (GPS など) をブラウザーが使用するかどうか true または false (既定値は false)

位置情報の取得に成功した場合に呼び出すハンドラーは、ユーザーの現在地に関連する重要なプロパティを保持する position オブジェクトを提供します。Geolocation API の仕様ではいくつかのプロパティが定義されていますが、すべてのブラウザーに一貫して実装されているのは latitude、longitude、および accuracy の 3 つだけです。heading、direction、altitude などのその他のプロパティもブラウザーに実装することが可能で、今後のモバイル ブラウズのシナリオにおいて非常に役に立ちます。

位置情報を反復要求する場合は、geolocation オブジェクトの 2 つのパブリック メソッド (watchPosition と clearWatch) を使用します。watchPosition メソッドは次のように使用します。

var watchId = navigator.geolocation.watchPosition(function(position) {
       var coordinates = position.coords;
       console.log("Your latitude: " + coordinates.latitude + " and longitude:
         " + coordinates.longitude + " (Accuracy of:
         " + coordinates.accuracy + " meters)");
}, errorHandler, {maximumAge: 100, timeout: 6000, enableHighAccuracy: true});

watchPosition メソッドも、getCurrentPosition メソッドと同様に、成功時のハンドラー、エラー ハンドラー、およびオプションを指定するパラメーターを受け取ります。主な違いは、watchPosition メソッドが受け取った関数を識別するための ID をすぐに返す点です。watchPosition は繰り返し呼び出される関数なので、ウィンドウを閉じるか、スクリプトから監視を明示的に中止するまで、ブラウザーはユーザーの位置の変化を検出するたびにこのメソッドを呼び出します。watchPosition メソッドの呼び出しを中止するには、次のように watchPosition メソッドが返した ID を使って clearWatch メソッドを呼び出します。

clearWatch(watchId);

サンプル アプリケーションの getCurrentPosition メソッドの使用方法を見てみましょう。この例では、先月号の WebMatrix (https://aka.ms/webm) Bakery サンプルを引き続き使用します。このサイトでは、ユーザーが商品を注文して配送を依頼できますが、注文した商品を受け取るために、実在する近くの店舗を指定できるよう機能を拡張します。Best Buy が家電製品の店内受け取りに関して実施していることを、動向が目まぐるしいパン業界でも行ってみたいと思います。

まず、11 月号の記事 (msdn.microsoft.com/magazine/hh547102) で HTML5 フォームの機能を駆使して変更済みの Bakery サンプル アプリケーションを開きます。次に、新しく機能を追加し、商品を受け取る近くのパン屋をフォーム上のリンクをクリックして選択できるようにします。今回は Bing Maps AJAX コントロール (msdn.microsoft.com/library/gg427610 (英語) からダウンロード可能) を使用します。適切な参照を追加して、地図を表示する HTML 要素を作成した後、フォームの受け取りセクションと配送セクションを表示するために、ページにいくつか div 要素を追加します。各 div 要素には、ユーザーが 2 つのオプションを切り替えるリンクを含めます。[Interested in picking your order up at one of our stores?] (注文した商品を店舗で受け取る) リンクがクリックされたときに、locateBakery.js という JavaScript ファイル内の関数を実行します。図 2 にこの関数の重要なメソッドを示します。

図 2 locateBakery.js

var mapDiv = document.getElementById("map");
var _map = new Microsoft.Maps.Map(mapDiv, { credentials: myCredentials });
function hideMap() {
    $('#pickup').hide();
    $('#map').hide();
    $('#ship').show();
}
function displayError(msg) {
    $('#error').val(msg);
}
function placeLocationOnMap(latitude, longitude) {
    var location = new Microsoft.Maps.Location(latitude, longitude);
    var bakeries = lookupNearbyBakeries(latitude, longitude);
    _map.setView({ zoom: 12, center: location });
    // Add a pushpin to the map representing the current location
    var pin = new Microsoft.Maps.Pushpin(location);
    _map.entities.push(pin);
    for (var i=0;i<bakeries.length;i++) {
        _map.entities.push(bakeries[i]);
    }
}
function errorHandler(e) {
    if (e === 1) { // PERMISSION_DENIED
        hideMap();
    } else if (e === 2) { //POSITION_UNAVAILABLE
        displayError('Cannot find your location. Make sure your network
        connection is active and click the link to try again.');
        hideMap();
    } else if (e === 3) { //TIMEOUT
        displayError('Cannot find your location. Click the link to try again.');
        hideMap();
    }
}
function locate() {
    navigator.geolocation.getCurrentPosition(function (position) {
        var coordinates = position.coords;
        placeLocationOnMap(coordinates.latitude, coordinates.longitude);
    }, errorHandler);
}

locate 関数は、navigator.geolocation.getCurrentPosition を呼び出し、呼び出しが成功すると座標を placeLocationOnMap 関数に引数として渡します。これにより、ユーザーの位置とその近くの店舗が地図上に表示されます (スペースの都合でパン屋を探すロジックは省略します)。ユーザーは、注文した商品を受け取れる近くのパン屋をこの地図で確認することができます。このページを図 3 に示します。

Using Geolocation with the Bakery Application
図 3 Bakery アプリケーションでの地理位置情報の使用

もちろん、地理位置情報はネットワークに依存するサービスです。では、地理位置情報がうまく動作しない場合はどうすればよいでしょうか。おそらく、getCurrentPosition 関数および watchPosition 関数の両方に、errorHandler という 2 つ目のパラメーターを指定していることに気付いた方もいるでしょう。Geolocation API の仕様では、getCurrentPosition 関数と watchPosition 関数を実行するときは、成功時のハンドラーとエラー ハンドラーの両方を呼び出すべきであると規定されています。地理位置情報サービスを呼び出す際に発生する可能性のあるエラーは 3 つあります。

  • 位置情報を追跡するというブラウザーの要求をユーザーが拒否する
  • 位置要求がタイムアウトになる
  • タイムアウト以外の理由で要求が失敗する

地理位置情報サポートをアプリケーションに追加する場合は、この 3 つのエラーすべてに対処します。この例は、図 2 のソースで確認できます。このソースでは、なんらかの理由で地理位置情報の呼び出しが失敗した場合、エラー メッセージを表示して発送先情報のセクションを再表示するようにしています。

地理位置情報のしくみ

Geolocation API の仕様は、各ブラウザーが開発者に提供する必要のあるパブリック API の定義については厳密に規定していますが、地理位置情報をブラウザーに実装する方法については細かく言及していません。一般的なガイダンスとして、各ブラウザーは、許容可能な精度で座標をアプリケーションに提供するという目標を持ちつつ、正確さと電力消費に関する考慮事項のバランスを取る必要があるとされています。

ほとんどのブラウザーは、(利用するバックグラウンド サービスは異なりますが) デスクトップやノートパソコンのシナリオでも同じしくみを利用して地理位置情報データを提供します。Internet Explorer 9 以降の場合、ブラウザーは IP アドレスまたは近くの Wi-Fi ホットスポットの一覧を取得し、その情報を Microsoft Location Service (Windows Phone デバイスに内蔵された位置情報機能で使用するものと同じサービス) に送信して座標を特定します。Google Chrome ブラウザーや Mozilla Firefox ブラウザーも IP アドレスと Wi-Fi ホットスポットのデータを使用しますが、実際の要求には Google Location Service を利用します。ユーザーの位置を特定する位置情報サービス メソッドは、正確性はある程度損なわれても速度が早い (電力消費も低い) ため、バックアップとして、または GPS チップがデバイスに内蔵されていない場合に非常に役に立ちます。

モバイル ブラウザーの場合、状況はもう少し複雑になりますが、これも使用するブラウザーによって異なります。最新のスマートフォンには GPS チップが内蔵されているため、より正確な座標を取得するために、モバイル OS が提供している GPS 位置情報サービスをブラウザーが自由に利用できます。Windows Phone 7.5 上の Internet Explorer 9 や iOS 上の Safari の場合、ブラウザーはまさにこの動作を行います。結果として、電力消費量は高くなりますが正確な位置情報が提供されます。通常ほとんどのモバイル上の地理位置情報シナリオ (ターン バイ ターン ナビゲーションなど) では、これが許容される妥協点です。ただし、GPS チップを使用できるという理由だけで、モバイル デバイス ブラウザーで GPS ベースの地理位置情報を利用できるとは限りません。Geolocation API の仕様の実装方法はブラウザーによって異なります。そのため、ブラウザーやその基盤となるモバイル OS サービスが、携帯電話基地局による三角測量または IP ルックアップに機能低下させることを選択する場合も、携帯電話基地局による三角測量と GPS を組み合わせて使用することを選択する場合もあります (既に多くのモバイル OS が、組み込み位置情報サービスの一部として、ネイティブ アプリケーションに対してこのような選択を行います)。

Web アプリケーション開発者にとって、これは朗報です。地理位置情報を利用すれば、(ネイティブ アプリケーション開発者と同じように) デバイスに対してネイティブな位置情報機能を利用したクロスプラットフォーム モバイル アプリケーションをビルドできます。

地理位置情報サポートの検出とポリフィル

アプリケーションに地理位置情報サポートを追加するときは、この機能をサポートしていないブラウザーを使用してサイトを閲覧するユーザーに対して、どのような手法をとればよいかを考えておく必要があります。この連載で既に説明したように、特定のブラウザーが地理位置情報をサポートしているかどうか判断するには、手動で機能検出 ("if (!!navigator.geolocation)") を実行するか Modernizr (modernizr.com、英語) のようなライブラリを使用します。地理位置情報をサポートしないブラウザーを使用しているユーザーには、アプリケーションのグレースフル デグラデーションと、外部ライブラリを使用したポリフィル サポートの 2 つの選択肢があります。前者は、ユーザーが現在地を指定するフォームを表示することで、アプリケーションの機能を限定する方法です。このように指定した現在位置を渡してサービスを呼び出すことで、ユーザーの現在地の緯度と経度を特定できます。このシナリオについてはこの連載の 9 月号で説明したので、ここでは割愛します。このシナリオおよびオンラインのコード サンプルについては、msdn.microsoft.com/magazine/hh394148 を参照してください。

今月は、後者の方法についてもう少し詳しく説明します。以前の記事で紹介したように、HTML5 テクノロジ用のクロスブラウザー ポリフィルを入手したいときは、まず、使いやすいポリフィルの包括的な一覧が掲載され、GitHub で公開されている Modernizr のホーム ページにアクセスします (bit.ly/eBMoLW、英語)。この記事の執筆時点では、地理位置情報に関して 3 つのポリフィルが掲載されています。今回は、Paul Irish が作成したポリフィル (bit.ly/q20TOl、英語) を使用します。

9 月号の記事で、ポリフィルとはHTML5 の機能をスクリプト ベースで実装するライブラリであり、多くの場合、仕様の策定中に文書化された API に準拠していると説明しました。この後半部分が重要です。つまり、アプリケーションにこうしたライブラリを追加すれば、ブラウザーが完全にサポートしているかのように HTML5 機能利用し続けることができます。古いブラウザーをサポートするために、プログラム フローを条件付きロジックで分岐させる必要はありません。

この連載で以前行ったように、Modernizr を使用してブラウザーが地理位置情報機能をサポートしているかどうかを検出します。サポートしていることが検出されない場合は geolocationShim.js スクリプト ファイルを非同期に読み込みます。

Modernizr.load({
  test: Modernizr.geolocation,
  nope: '../js/geolocationShim.js',
  complete: function() {
  locate();
  }
});

ブラウザーが地理位置情報をサポートしている場合は、続けて locate 関数を実行し、サポートしていない場合は、地理位置情報のポリフィルを読み込んだ後に locate 関数を実行します。Internet Explorer 9 でこのロジックを使用してサンプルを実行すると、想定どおりの処理が行われ、ネイティブの地理位置情報機能が呼び出されます。

この処理を古いブラウザーでテストするには、Internet Explorer 9 F12 開発者ツールを起動して、ブラウザー モードを Internet Explorer 8 に切り替えます。この操作を行ってページを再び実行すると、地理位置情報がサポートされていないことを Modernizr が検出し、shim を読み込みます。次に、shim 内の getCurrentPosition メソッドが呼び出されると、図 4 のコードが実行されます。

図 4 shim を使用して実装した getCurrentPosition メソッド

geolocation.getCurrentPosition = function(callback){   
  if (cache) callback(cache);
  $.getScript('//www.google.com/jsapi',function(){     
    cache = {
      coords : {
        "latitude": google.loader.ClientLocation.latitude,
        "longitude": google.loader.ClientLocation.longitude
      }
    };
    callback(cache);
  });   
};

このポリフィル ライブラリは、Geolocation API の仕様に準拠していることに加えて getCurrentPosition メソッドおよび watchPosition メソッドを呼び出すため、メインの getLocation 関数にその他のチェックまたは条件付きロジックを実装する必要はありません。

アプリケーションへの地理位置情報の導入においては、ユーザーが現在地を手動で指定できるようアプリケーションのグレースフル デグラデーションを行うか、shim の実装を試すかにかかわらず、適切な方法をとることで新しいブラウザーを使用していないユーザーに優れたエクスペリエンスを提供し続けることができます。

HTML5 (またはオープン Web テクノロジ) と呼ばれるものの大部分は、Web 上での実際のアプリケーション開発を可能にすることを目的とした一連のテクノロジです。Web とデスクトップとの距離が狭まってきている中、地理位置情報は、現在 Web アプリケーションで可能なことを示す優れた例です。この記事では、基本的な Geolocation API の仕様について説明し、地理位置情報の使用方法、デスクトップ ブラウザーとモバイル ブラウザーに地理位置情報を実装する方法、および古いブラウザーで地理位置情報サポートにポリフィルを使用する方法を紹介しました。次は、読者がこの優れた機能を Web アプリケーションに加える番です。今すぐすばらしいアプリケーションの作成に取り掛かりましょう。

Internet Explorer 9 における地理位置情報サポートの詳細については、Internet Explorer 9 開発者ガイド (msdn.microsoft.com/ie/ff468705) を参照してください。地理位置情報に関連するその他のクロスブラウザー ポリフィルについては、bit.ly/qNE92g (英語) の一覧を参照してください。

今回紹介したすべてのデモ (オンラインで閲覧可能) は、WebMatrix という、マイクロソフトが無償で提供している軽量の Web 開発ツールを使用してビルドしています。WebMatrix は、aka.ms/webm で試すことができます。

Brandon Satrom は、テキサス州オースティン郊外でマイクロソフトの開発者エバンジェリストとして活躍しています。彼のブログは userinexperience.com (英語) です。Twitter のアカウントは @BrandonSatrom (英語) です。

この記事のレビューに協力してくれた技術スタッフの Jon BoxJohn HrvatinClark Sell、および Andy Zeigler に心より感謝いたします。