January 2009

Volume 24 Number 01

Windows Mobile - 位置認識アプリケーションに GPS と Web マップを使用する

Christopher Mitchell | January 2009

コードは MSDN コード ギャラリーからダウンロードできます。
オンラインでのコードの参照

この記事では、次の内容について説明します。

  • MapPoint Web サービス
  • タスクとマップをキャッシュする
  • 必要な周囲のポイントを取得する
  • 新しいタスクを作成する
この記事では、次のテクノロジを使用しています。
Windows Mobile 6、MapPoint

目次

MapPoint を使用して位置情報を組み込む
タスクと Pocket Outlook
アプリケーション アーキテクチャ
周囲のポイントを取得する
タスクを追加する
さらに先へ

私は最近、新しい家に引っ越しました。その際、親切な友人が何人か、荷造りと荷ほどき、公益サービス事業者への連絡など、転居に伴う重要だが面倒な作業をまる 1 日手伝ってくれました。しかし、前の家で最後の荷物を運び出して車で新居に戻る途中、私は友人たちの夕食を買うのをもう少しで忘れるところでした。Windows Mobile 搭載の携帯電話にアラームを設定していたにもかかわらず、一番便利なテイクアウト レストランのそばを通り過ぎたときに鳴らなかったのです。

このような用事を済ませることのできる場所に近づいたときに、正確に通知してくれる携帯電話があればよかったのです。適切な場所でタイミングよくアラームが鳴っていたら、皆でテイクアウトの料理を堪能したでしょう。荷物を運び続けた大変な 1 日の終わりに、不慣れな土地で店を探し回る必要はなかったはずです。

Windows Mobile には、デバイスに周りの環境に関する情報 (デバイスが今どこにあるか、電波は届いているか、電波の強さはどの程度か、など) を提供し続けるのに役立つ数多くのインターフェイスと機能が用意されています。しかし、アプリケーションではこれらの機能をどのように利用できるのでしょうか。これらの機能のうち、最も有名で、役に立つであろう機能は、位置認識です。この機能を利用すると、やや単純な衛星ナビゲーション プログラムから、ここで説明するずっと高度なタスク リストまで、さまざまな位置認識アプリケーションを作成することができます。

この記事では、この機能を使用する際の問題と、便利なアプリケーションを開発するうえで記述する必要のある追加のコードについて説明します。また、モバイル アプリケーションの開発環境とユーティリティについて説明し、適切な場所でタイミングよくタスクを通知できる位置認識タスク リスト アプリケーションの構築方法を紹介します。

MapPoint を使用して位置情報を組み込む

ここで紹介する wheretodo という名前の位置認識タスク リスト アプリケーションでは、いくつかの主要なタスクを実行する必要があります。まず、電話の現在位置に関する情報を取得すると共に、タスクを格納および監視する必要があります。また、電話の現在地付近では現在のタスクを遂行するためにどのような店やサービスを利用できるのかを把握しなければなりません。さらに、電話に通知を提供する必要があります。このアプリケーションのインターフェイスを図 1 に示します。

fig01.gif

図 1 wheretodo アプリケーション

このアプリケーションにまず必要なのは、地理データです。この記事の目的に沿って、私は Microsoft MapPoint Web サービスを使用することにしました。この Web サービスは Live Search マップと Virtual Earth の基になっているテクノロジであり、ヨーロッパと米国のマップ検索サービスを提供します。用意されているサンプル コードでは、ヨーロッパのマップ データ設定を使用しています。そのため、どこでも機能するように変更しなければなりません。

MapPoint は、その引数として、GPS の緯度と経度、および特定の種類の店の検索コードを受け取ります。位置情報は、エミュレータまたはスマートフォンにインストールした FakeGPS を使用して提供できます (このユーティリティの詳細については、補足記事「FakeGPS」を参照してください)。

MapPoint は、SOAP API を使用して XML Web サービスを提供します。この Web サービスは、共通サービス、検索サービス、レンダリング サービス、ルート サービスという 4 つの主要なサービスに分けられます。このアプリケーションに関係する主要サービスは検索サービスですが、マップでユーザーに道順を示す必要がある場合は、それ以外のサービスを使用して機能を拡張できます。

共通サービス (CommonServiceSoap) には、検索サービス、ルート サービス、およびレンダリング サービスに共通のクラス、メソッド、およびプロパティが含まれています。これらの要素は、基本的なユーティリティ関数です。

検索サービス (FindServiceSoap) を使用すると、MapPoint Web サービスのデータから、住所、地理エンティティ、緯度と経度の座標、および POI (points of interest) を探し出せます。住所を解析し、指定された緯度と経度で位置情報を返すこともできます。

レンダリング サービス (RenderServiceSoap) を使用すると、ルートや位置を示すマップの描画、画鋲の配置、ポリゴン領域の描画、マップのサイズとビューの設定、マップ上のポイントの選択、マップ上のポイントとポリゴンの位置情報の取得、レンダリングされたマップのパンとズームが可能です。

ルート サービス (RouteServiceSoap) は、位置と中間地点に基づいて、ルート、道順、計算されたルートの表示 (マップ上で強調表示されたルートを描画するために使用) を生成します。また、セグメントとルートの基本設定を設定し、セグメントと道筋を示すマップ ビューを生成します。

詳細なオブジェクト モデル クラス ダイアグラムについては、MSDN を参照してください。MapPoint では、検索、ルート、レンダリングの各サービスに使用されるデータは、地域ごとに、または必要な情報の種類に応じて、さまざまなデータ ソースで保持されます。サービスの使用の詳細については、MSDN で提供されている幅広い MapPoint の技術記事を参照してください。

タスクと Pocket Outlook

Pocket Outlook オブジェクト モデル (POOM) を使用すると、Windows Mobile 向けのタスクと連絡先の管理アプリケーションにメニューや機能を追加し、関連のアイテムやデータを操作できます。位置認識アプリケーションには、IAppointment、ITask、および IContact という 3 つの主要なインターフェイスが関係します。

IAppointment は、予定表フォルダ内の予定を表します。予定オブジェクトは、会議、1 回限りの予定、定期的な予定または会議などです。

ITask は、指定の期間内に実行される、割り当てられたタスク、委任されたタスク、または自分で割り当てたタスクを表します。タスク アイテムはタスク フォルダ内に格納されます。

IContact は、連絡先フォルダ内の連絡先を表します。連絡先を保存、削除、複製、または表示するには、IContact のメソッドを使用します。IPOutlookItemCollection インターフェイスを使用すると、新しい連絡先の追加や、既存の連絡先の取得が可能です。

サンプル アプリケーションでは ITask を使用します (機能的には IAppointment を使用してもかまいませんが、このインターフェイスはアプリケーションの当面のニーズを満たすうえで適しているとは言えません)。POOM はデスクトップの Outlook オブジェクト モデルと似ています。これらの詳細については、MSDN の記事「Pocket Outlook オブジェクト モデルと Outlook オブジェクト モデルの違い」を参照してください。

アプリケーション アーキテクチャ

位置認識アプリケーションは、2 つの機能チェーンから構成されます。最初のチェーンは、図 2 に示すような、現在のタスクと問題を監視するシステムです。タスクは SQL Server Compact のデータベース テーブルに格納されます。また、補助データのセットは、geocache と呼ばれる別のテーブルに格納されます。別のテーブルを使用するのは、必要な接続のレベルを抑えるためです。この内容については、後ほど説明します。

fig02.gif

図 2 位置認識タスクの監視と通知

付近のポイントの情報は、Microsoft MapPoint Web サービスによって提供されます。MapPoint Web サービスでは、世界の地域ごとに異なるデータ ソースが使用されます。機能の数はデータ ソースごとに異なります。サンプル アプリケーションで使用されているデータ ソースは、NavTech.EU です。ポイントの情報の収集後、距離計算を行って、必要に応じて通知を出すことができます。

イベントの 2 番目のチェーン (図 3 を参照) では、POOM を介して、Windows Mobile デバイス上のタスク リストにタスクを追加します。このフレームワークによって、予定表や SMS 機能へのアクセスも可能になります。

fig03.gif

図 3 wheretodo アプリケーションにタスクを追加するためのシステム フロー

タスクとキャッシュは、監視と通知のイベント チェーンの最初の部分です。タスク マネージャは、単純に POOM からタスクの一覧を取得します。また、接続に制限のあるデバイスで Web サービスを使用するときに生じる問題に対処する geocache も管理します。デバイスの接続レベルは、接続がまったく途切れない状態、たまにしか接続できない状態、まったく接続できない状態など、さまざまです。

さまざまな接続レベルに対処するには、Web サービス データに対する適切なキャッシュ ポリシーと、ほとんどの接続方法を把握できるシンプルなシステムが必要です。非常に複雑なシナリオもすぐに思い付きましたが、この記事の目的に沿って、単純な範囲のキャッシュ ポリシーを設定することにしました。たとえばこのポリシーでは、MapPoint Web サービスに問い合わせを行って、現在地を中心とする指定範囲 (たとえば 80 km) 内にあるすべての中国料理のテイクアウト レストランを検索できます。そしてユーザーがいずれかの方向に 40 km 移動すると、新たに検索が行われます。このポリシーは、Web サービスへの問い合わせのたびにコストがかかるというプリンシパルに基づいています。

また、ユーザーがタスクを入力した地点から 40 km 以上移動することがあまりないとも想定しています。距離はキャッシュのアクションを制御するために使用されますが、ユーザーや国によって移動距離はまちまちなので、ユーザー自ら入力して設定するように設計されています。

問い合わせが行われた場所のログは geocachelog データベースに格納され、別の問い合わせが行われる前にチェックされます。したがって、重複する情報が Web サービスから要求されることはありません。また、システムにあらかじめかなりの量の情報を格納しておくこともできます。

FakeGPS

sidebarfig.gif

FakeGPS アプリケーション

Windows Mobile 6 SDK には、FakeGPS というユーティリティ アプリケーションが含まれています。FakeGPS を利用すると、ダミーの GPS データを使ってアプリケーションをテストすることができます。FakeGPS のインストールと FakeGPS を使用したテストの詳細については、MSDN ライブラリの記事「FakeGPS ユーティリティを使用する」を参照してください。

FakeGPS は、事前に記録されている GPS 命令のセットを読み取ります。GPS データは、GPS データ レコーダをそのまま使用して、実際の GPS デバイスから取得できます。作成されたファイルは FakeGPS デバイスによって読み取られます。これを再度実行し、テスト目的で位置データの変化をシミュレートできます。私の場合は、自分のアプリケーション用のデータを収集するためにロンドンを一回りして行程を記録し、その行程を FakeGPS を通じてシミュレートしました。

周囲のポイントを取得する

次のコンポーネントは、geocache にデータを格納する方法を定義するものです。アプリケーションで場所を検索する必要がある場合、GetNearByPOI 関数が呼び出されます (図 4 を参照)。このコードは、MapPoint Web サービスに対して問い合わせを行います。ここで示すコードは、わかりやすさを重視して簡略化されています。コード サンプルに含まれているものとは異なりますので、注意してください。

図 4 必要な周囲のポイントを取得する

private void GetNearByPOI( string KeyWord, LatLong CurrentPosition) 
{ 
     FindServiceSoap findService = new FindServiceSoap(); 
     FindNearbySpecification findNearBySpec = new FindNearbySpecification(); 
     findService.Credentials = new System.Net.NetworkCredential(myUserName, myPassword); 
     findService.PreAuthenticate = true; 
     findNearBySpec.Distance = Convert.ToDouble(inputdistance.Text); 
     findNearBySpec.LatLong = new LatLong(); 
     findNearBySpec.LatLong.Latitude = CurrentPosition.Latitude; 
     findNearBySpec.LatLong.Longitude = CurrentPosition.Longitude; 
     findNearBySpec.Filter = new FindFilter(); 
     //findNearBySpec.Filter.EntityTypeName = KeyWord;      
     findNearBySpec.Filter.EntityTypeName = "FoodType3"; 
     // SIC CODE 
     findNearBySpec.DataSourceName = "NavTech.EU"; 
     FindResults foundResults; 
     foundResults = findService.FindNearby(findNearBySpec); 
     string connectionString; 
     string fileName = System.IO.Path.GetDirectoryName( System.Reflection.Assembly.GetExecutingAssembly().GetName().CodeBase) + "\\wheretodo.sdf"; 
     string password = "sa"; 
     connectionString = string.Format("DataSource=\"{0}\"; Password='{1}'", fileName, password); 
     SqlCeConnection cn = new SqlCeConnection(connectionString); cn.Open(); 
     SqlCeCommand cmd; 
     //Loop Round and add it to the geocache 
     foreach (FindResult fr in foundResults.Results) 
     { 
          //This needs to include LongLat in geocache 
          string sql = "insert into geocache2 (POI_Name, POI_Address, POI_Tel, POI_Web, " + "POI_POST_ZIP, POI_Lat, POI_Long, POI_KeyWord) " + "values           (@Name, @Address, @Tel, @Web, @POST_ZIP, '" + fr.FoundLocation.LatLong.Latitude.ToString() + "', '" + fr.FoundLocation.LatLong.Longitude.ToString() + "', '" + KeyWord.ToString() + "')"; 
          cmd = new SqlCeCommand(sql, cn); 
          cmd.Parameters.Add("@Name", SqlDbType.NVarChar, 255, "Name").Value = fr.FoundLocation.Entity.Properties[0].Value.ToString(); 
          cmd.Parameters.Add("@Address", SqlDbType.NVarChar, 255, "Address").Value = fr.FoundLocation.Entity.Properties[1].Value.ToString() + fr.FoundLocation.Entity.Properties[2].Value.ToString(); 
          cmd.Parameters.Add("@Tel", SqlDbType.NVarChar, 255, "Tel").Value = fr.FoundLocation.Entity.Properties[9].Value.ToString(); 
          cmd.Parameters.Add("@Web", SqlDbType.NVarChar, 255, "Web").Value = "http://m.live.com"; 
          cmd.Parameters.Add("@POST_ZIP", SqlDbType.NVarChar, 255, "POST_ZIP").Value = fr.FoundLocation.Entity.Properties[7].Value.ToString(); 
          cmd.ExecuteNonQuery(); 
          //Update geocachelog 
          sql = "insert into GeoCodeLog (Keyword, Lat, Long ) values ('" + KeyWord + "', '" + fr.FoundLocation.LatLong.Latitude.ToString() + "', '" + fr.FoundLocation.LatLong.Longitude.ToString() + "' )"; 
          cmd = new SqlCeCommand(sql, cn); cmd.ExecuteNonQuery(); 
     } 
}

CurrentPosition.Latitude と CurrentPosition.Longitude が呼び出されているセクションを見てください。これは、最終的に GPS 中間ドライバ (アプリケーションと、GPS ハードウェア用のデバイス ドライバの間に位置するソフトウェア層) から取得されます。この抽象層によって、アプリケーションを 1 度記述するだけで複数の GPS デバイスに対応させることができます。GPS 中間ドライバ API は、ネイティブ コード ライブラリを通じて公開されます。このライブラリにマネージ コードからアクセスするには、Windows Mobile 6 Professional SDK に含まれているサンプルを使用してください (「マネージ コードから GPS 中間ドライバを使用する」を参照)。

Navtech.EU データ ソースで使用されている業種コードを基に、現在地とタスクに関係する近くの POI を特定したら、これらのポイントが現在地からどのくらい離れているかを正確に割り出す必要があります。私は、Web サービスと GPS (ここでは FakeGPS) から返される経度と緯度の値を使用しました。図 5 に、どのような方法でこれらの値を距離に変換したかを示します。

 

図 5 距離を計算する

private double GetLatLongTuppleDistance( double Lat1, double Long1, double Lat2, double Long2) 
{ 
     //Convert Degress to Radians for Calculations 
     double Lat1r = ConvertDegreesToRadians(Lat1); 
     double Lat2r = ConvertDegreesToRadians(Lat2); 
     double Long1r = ConvertDegreesToRadians(Long1); 
     double Long2r = ConvertDegreesToRadians(Long2); 
     // Spherical law of cosines formula—ignores the effect of hills 
     double R = 6371;      // Earth's radius (km) 
     double d = Math.Acos(Math.Sin(Lat1r) * Math.Sin(Lat2r) + Math.Cos(Lat1r) * Math.Cos(Lat2r) * Math.Cos(Long2r—Long1r)) * R; 
     //Returns distances in km return d; 
}

球面三角法の余弦定理 (ピタゴラスの定理の一種) に基づく球面三角関数は、緯度と経度の組み合わせを距離 (km) に変換する関数を提供します。当然のことながら、この関数を使用する場合は、現在地から探している場所までの距離を割り出す際に妨げとなる要素について、詳細に検討する必要があります。地球は完全な球体ではないため、これらの式をそのまま使用するとエラーが生じます。マイル数はキロメートル数を 1.609344 で割ると求められることはご存知でしょう。しかし皆さんは、パンを買うためにボートに乗って出かけることもあるかもしれません。そこで付け加えておきます。海里 (ノーティカル マイル) 数は、キロメートル数を 1.852 で割ると算出できます。

このイベント チェーンの最後の部分は、現在地を中心とする特定の範囲内でタスクに対するソリューションが見つかった場合にユーザーに対して行う通知です。この範囲には移動手段 (車、徒歩、自転車など) が大きく影響するため、ユーザーが入力する項目として残されています。

タスクを追加する

当然のことながら、アプリケーションで目的の場所を検索するには、ユーザーがモバイル デバイスにタスクを追加する必要があります。POOM には Outlook オブジェクト モデルが反映されていますが、その機能の範囲はモバイル デバイスの実用上の制約に合わせて制限されています。

POOM を使用すると、予定、タスク、連絡先アイテムの変更と表示のほか、それらを含むフォルダの操作が容易になります。タスク アイテムを作成するためのコードを次に示します。

OutlookSession outlooksession = new OutlookSession(); 
Task NewTask = new Task(); 
NewTask.Body = textBox2.Text.ToString(); 
string MyString = dateTimePicker2.Value.ToShortDateString() + " " + dateTimePicker1.Text.ToString(); 
DateTime MyDateTime = new DateTime(); 
MyDateTime = DateTime.ParseExact(MyString, "M/d/yy h:mm:ss tt", null); 
NewTask.DueDate = MyDateTime.ToUniversalTime(); 
NewTask.Subject = textBox1.Text.ToString(); 
outlooksession.Tasks.Items.Add(NewTask);

さまざまなパラメータを変更することで、終了日やタスクの本文テキストを設定し、それを POOM に追加できます。

このコンポーネントは、wheretodo アプリケーションの機能を提供するために使用される 2 つのコンポーネント チェーンのうち、後のチェーンを構成するものです。システムの UI 自体は非常にシンプルで、すべての関連情報を設定する機能と、アプリケーションで検索する業種コードを指定する機能だけが用意されています。アプリケーション全体は、位置認識アプリケーションの使用方法を示すため、単純に無限ループにラップされています。このアプリケーションは、モバイル デバイスにバックグラウンド サービスとして簡単に追加できます。これにより、アプリケーションを Windows Mobile 6 環境または Windows Mobile 5 環境とより効率的かつクリーンに統合することができます。

モバイル データ リソース

モバイル アプリケーションとデスクトップ アプリケーションの両方に使えるコードを 1 回で記述する

いろいろな場所へ: Windows Mobile 用のアダプタブル アプリケーション

データ ポイント: モバイル アプリケーションからデータにアクセスする

さらに先へ

ここまで、1 種類の位置認識デバイスと、これらのアプリケーションの開発を容易にするツールを見てきました。この記事で紹介した位置認識タスク リストの基本的な概念は、RFID 向けに簡単に拡張できます。

このコードに施すことのできる改良は、任意の位置認識アプリケーションにも適用できます。最も多くの改良が可能な領域は、位置認識を他のコンテキスト認識サービスと結び付ける方法に関係するものです。コンテキスト認識サービスは、ほとんどの携帯電話で実行されるサービスです。タスクは、ユーザーが実行しようとしている他のタスクを認識しません。認識するのはユーザーの位置だけです。ユーザーは、特定の時刻に目的の場所に近づいたとき (職場から家に向けて車を走らせているときなど) にタスクを実行できるように、タスクに時刻と場所を設定することもできます。さらにすばらしいことに、タスクのアラームは、あと 10 分でユーザーが出席する会議が始まることなどを認識できます。また、ユーザーがその会議に向かう途中で郵便局のそばを通っていても、切手を買う余裕はないということを認識できます。

ここで説明したアプリケーションや、独自に変更を加えた位置認識アプリケーションのカスタム インストーラを構築する方法については、MSDN Magazine 2007 年 10 月号の筆者の記事「周りのノイズに応じて着信音の音量を調整する」を参照してください。

この記事を親愛なる友人 Tom Passey に捧げます。Tom と彼の妻はいつも私を支えてくれており、感謝の気持ちでいっぱいです。彼らのご多幸を祈ります。

Christopher Mitchell は、機械学習と音楽信号および音声信号処理の博士号を取得した後、カウフマン財団に所属していました。現在は英国のケンブリッジで新規事業を立ち上げています。また、英国のケンブリッジにあるアングリア ラスキン大学で非常勤講師を務めています。連絡先は、chris.mitchell@anglia.ac.uk です。