本文章是由機器翻譯。

Windows Mobile

將 GPS 和網站地圖用於能感知位置的應用程式

Christopher Mitchell

代碼下載位置:MSDN 代碼庫
線上流覽代碼

本文將介紹以下內容:

  • MapPoint Web 服務
  • 緩存任務和地圖
  • 獲取所需的鄰近點
  • 新建任務
本文使用了以下技術:
Windows Mobile 6、MapPoint

目錄

使用 MapPoint 進行定位
任務和 Pocket Outlook
應用程式體系結構
獲取鄰近點
添加任務
一路前行

最近我喬遷新居,一些要好的朋友花費了一整天的時間幫我裝箱拆箱、擺放物品和處理其他乏味卻又不得不做的搬遷工作。回老房子取最後一個箱子的路上,我差點忘記為他們準備晚餐。我在自己的 Windows Mobile 電話上設置了提醒,但它不能在我正好駕車經過最方便的外賣店時告訴我。

我希望電話能在我到達合適的目標附近時通知我。如果能在合適的時間和地點得到提醒,我們就可以在勞累一天后大飽口福,而不是還要駕車在陌生的地段來回尋覓。

Windows Mobile 提供了許多能夠説明設備瞭解其環境的介面和功能——所處位置、是否有信號、信號量等。但如何才能將這些功能用於您的應用程式呢?在這些功能中,最突出和最有用的無疑是位置感知。您可以創建各種位置感知應用程式,從目的明確的衛星導航程式到本文將討論的更為複雜的任務清單。

本文將主要介紹使用此功能時所涉及的問題,以及為開發有用應用程式需要編寫的其他代碼。在本文中,我將討論移動應用程式開發環境和實用工具,並向您展示如何構建可以在適合的時間和地點提醒您注意的位置感知任務清單應用程式。

使用 MapPoint 進行定位

我的位置感知任務清單應用程式(名為 wheretodo)需要執行幾個核心任務。它需要獲取電話地理位置的資訊。還需要存儲和監視任務。此外,應用程式還需要知道電話的當前位置附近有哪些合用的商店和服務,可以用於解決當前任務。最後,它需要給予電話提醒。應用程式介面如圖 1 所示。

fig01.gif

圖 1 Wheretodo 應用程式

應用程式首先需要地理資料。考慮本文的目的,我選擇使用Microsoft MapPoint Web 服務。此 Web 服務是Live Search 地圖Virtual Earth的基礎技術,可為歐洲和美國提供就近搜索服務。提供的示例代碼使用歐洲地圖資料設置;您需要根據工作地點來更改此設置。

MapPoint 將 GPS 維度、經度以及特定存儲類型的搜索代碼作為其參數。安裝在模擬器或智慧手機上的 FakeGPS 實用工具可提供位置資訊。(有關此實用工具的詳細資訊,請參閱“FakeGPS”側欄。)

MapPoint 為 XML Web 服務提供了 SOAP API。Web 服務分為四種主要服務:公共服務、查找服務、呈現服務和路線服務。此應用程式最受關注的服務是查找服務,但如果您想為使用者或地圖指引方向,也可以使用其他服務來擴展功能。

公共服務 (CommonServiceSoap) 包含查找服務、路線服務、呈現服務通用的類、方法和屬性,或基本實用工具函數。

您可以使用查找服務 (FindServiceSoap) 找到位址、地理實體、維度和經度座標以及 MapPoint Web 服務資料的興趣點 (POI)。您還可以解析位址並針對指定的維度和經度返回位置資訊。

您可以使用呈現服務 (RenderServiceSoap) 繪製路線圖和位置圖、放置圖釘、繪製多邊形區域、設置地圖大小和地圖視圖、在地圖上選點、獲取地圖上相關點和多邊形的位置資訊、平移和縮放呈現的地圖。

路線服務 (RouteServiceSoap) 可生成路線、行駛方向、根據位置或路標計算出的路線表示(用於在地圖上呈現突出顯示的路線)、設置路段和首選路線、生成路段和方向的地圖視圖。

MSDN 上有一整套物件模型類關係圖。MapPoint 根據地理區域或所需的資訊類型將用於查找服務、路線服務和呈現服務的資料保存在多個不同的資料來源中。您在 MSDN 雜誌上能夠找到全部有關使用服務的MapPoint 技術文章

任務和 Pocket Outlook

Pocket Outlook 物件模型 (POOM) 允許您將功能表和功能添加至 Windows Mobile 的“任務”和“連絡人”應用程式中,並允許處理其關聯項和資料。位置感知應用程式有三個主要介面:Iappointment、Itask 和 Icontact。

IAppointment 代表 Calendar 資料夾中的一個約會。一個約會物件可以代表一個會議、一次約會,也可以代表定期約會或會議。

ITask 代表一項在指定時間範圍內要完成的已分配、已委派或自派任務。任務項包含在 Tasks 資料夾內。

Icontact 代表 Contacts 資料夾內的連絡人。使用其方法可以保存、刪除、複製或顯示連絡人。IPOutlookItemCollection 介面可用於添加新連絡人或檢索現有連絡人。

示例應用程式將使用 Itask。(從功能上來講,可以使用 Iappointmen,但該介面不太適合於應用程式的直接需要。)POOM 類似于桌面 Outlook 物件模型,您可以在 MSDN 上的“Pocket Outlook 物件模型與 Outlook 物件模型之間的差異”一文中瞭解更多相關資訊。

應用程式體系結構

位置感知應用程式由兩個函數鏈組成。第一個是監視當前任務和問題的系統,如圖 2 所示。任務存儲在 SQL ServerCompact 資料庫表中。此外,還有一組輔助資料存儲在另一個名為 geocache 的表中。使用單獨的表可限制所需的連接數,我將在本文的稍後部分中加以介紹。

fig02.gif

圖 2 監視位置感知任務併發出警報

Microsoft MapPoint Web 服務可提供鄰近點的資訊。世界不同地區的 MapPoint Web 服務中有不同的資料來源,資料來源的功能也各不相同。示例應用程式使用的資料來源是 NavTech.EU。收集完此類資訊後,即可執行一些距離計算併發出警報(如適用)。

第二個是事件鏈(請參見圖 3),可通過 POOM 將任務添加至 Windows Mobile 設備上的任務清單中。該框架還允許訪問日曆和 SMS 函數。

fig03.gif

圖 3 用於向 Wheretodo 應用程式添加任務的系統流

“Tasks and Cache”(任務和緩存)代表事件監視和警報鏈的第一部分。工作管理員只需從 POOM 獲得一個任務清單。它還能管理 geocache,解決對連接有局限性的設備使用 Web 設備時產生的問題。設備連接的級別分為永久性連接、偶爾連接或根本無連接。

為適應多種連接情況,您需要一個合適的 Web 服務資料緩存策略以及一個能夠瞭解如何充分利用連接的簡單系統。儘管可以輕鬆想像出非常複雜的情形,但在本文中制定的是簡單的半徑緩衝策略。例如,向 MapPoint Web 服務發出一個查詢,以現在的位置為中心、在給定半徑(比如說 80 公里 (km))內搜索所有中餐外賣店。在您朝任一方向走了 40 km 後,發出一條新的搜索。它遵循的原則是:向 Web 服務發出每一條查詢的成本都很高。

它還假設您離開任務輸入點的距離不太可能超出 40 km。由於用來控制緩衝操作的距離可能因人和國家/地區而異,因此它被設計為由使用者輸入。

查詢已運行位置的日誌存儲在 geocachelog 資料庫中,在執行任何其他查詢之前會檢查該日誌,以防從 Web 服務請求重複的資訊。它還為使用大量資訊預填充系統提供了機會。

FakeGPS

sidebarfig.gif

FakeGPS 應用程式

Windows Mobile 6 SDK 包含名為 FakeGPS 的實用工具,此工具允許您使用類比的 GPS 資料測試應用程式。有關使用 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 Intermediate Driver,一個介於應用程式和 GPS 硬體設備驅動程式之間的軟體層。 該抽象層允許一次性編寫應用程式並使之能與多個 GPS 設備結合使用。 GPS Intermediate Driver API 通過本機代碼庫公開。 您可以通過使用 Windows Mobile 6 Professional SDK 附帶的示例獲得從託管代碼訪問該庫的許可權,(請參閱“ 通過託管代碼使用 GPS Intermediate Driver)”。

現在有了與任務相關的當前位置和附近的 POI,在此它們由 Navtech.EU 資料來源中使用的標準工業分類碼 (SIC) 標識,您需要準確地確定出這些點離您當前所在位置的距離。 我使用從 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 英里 = 1.609344 公里,而海上的 1 海裡 = 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 應用程式功能的兩個元件鏈的後一個。系統 UI 本身非常簡單,利用它可設置所有相關資訊並確定應用程式將找到的 SIC 代碼。整個應用程式封裝到一個無限迴圈中,它的作用就是說明如何使用位置感知應用程式。您可以非常輕鬆地將它作為背景服務添加至移動設備,使其更高效簡潔地集成到 Windows Mobile 6 或 Windows Mobile 5 環境中。

移動資料資源

編寫同時適用于移動和桌面應用程式的代碼

平步青雲:適用于 Windows Mobile 的自我調整應用程式

資料點:從移動應用程式訪問資料

一路前行

您已看到了一種位置感知設備和能輕鬆開發這些應用程式的工具。可針對 RFID 輕鬆擴展位置感知任務清單的這一基本概念。

此代碼的改進可以應用於任何位置感知應用程式。以上內容可歸結為:位置感知如何與在大多數行動電話上運行的其他上下文感知服務緊密地聯繫在一起。除了您的位置,任務對您還想嘗試的其他事情一無所知。您可為任務設定時間和位置,以便在某個特定時間(比如說,下班後駕車回家)接近某個位置時可以觸發它們。更棒的是,任務提醒會知道您 10 分鐘後有一個會議,還知道即便您在開會的路上會路過郵局,也沒有足夠的時間去買郵票。

如果您對為本文中的位置感知應用程式或您自己最近更新的位置感知應用程式構建自訂安裝程式感興趣,請參閱我為《MSDN 雜誌》2007 年 10 月刊撰寫的文章(調整鈴聲音量,避免環境噪音)。

謹以此文獻給我親愛的朋友 Tom Passey 和他的妻子,他們多次給予我關心和照顧,為我留下了很多美好的回憶。在此獻上最美好的祝願。

Christopher Mitchell 擁有機器學習和音樂/聲音信號處理專業的博士學位,是一名 Kauffman/NCGE 研究員,目前正在英國劍橋大學執教。他是英國劍橋鎮安格利亞魯斯金大學的一名兼職講師。您可以通過電子郵件chris.mitchell@anglia.ac.uk與 Chris 取得聯繫。