Windows Phone 7 - 學習Location Service與Map地圖

Windows Phone 7 - 學習Location Service與Map地圖

 

System.Device.Location Namespace命名空間裡提供很多相關Location方面的資訊與類別,例如:取得目前

設備所在Location資訊、指定GeoPoint、轉換Location資訊為實際街道、控制GeoPoint改變的事件等等。因此,

撰寫Location相關的服務時,一定要先得了解一下該命名空間裡提供那些開發要注意的內容。

 

在使用Location Service的時候,它的來源可分成三個:WIFI、GPS與Cellular。其中又以WIFI與Cellular最為耗電,

因此,在實作時有二個重點一定要注意<參考來源>:

a. 建議使用低精準度(因愈高精準度需要更多的電力來收集資料)、低耗電與電力最佳化的目標;

b. 建議只有在程式啟動後,再有需要時,才詢問或開啟Location Service;當離開時,即關閉它;

 

因此,在設計Location Service需考量這二個因素之外,在實作時,由於接收Location Data的來源這麼多,但不是

每一個來源的資料都是比較好的,如何選擇一個較好等級精準度的Location Data也是很重要的,就需要依賴以下的類別:

 

GeoCoordinateWatcher

該類別主要提供當接收到Location Data時,它會根據初始化該類別時設定的篩選機制,自動選擇最適合的精度度Lcation Data,

針對初始化設定高精準度的條件有:Hight與Default二種<GeoPositionAccuracy Enumeration>。

針對GeoCoordinateWatcher而言,GPS在行動裝置中被設計是非常敏感的,因此,當從A地移動到B地就會觸發Location Data的

接收或改變的事件,但多少的移動範圍需要被觸發呢,可直接設定GeoCoordinateWatcher的MovementThreshold屬性來規範。

a. MovementThreshold

    設定必需連續觸發GeoCoordinateWatcher.PositionChange事件的最小距離。值由0~N meters,愈小愈耗電。

 

b. Permission(GeoPositionPermission Enumeration)

    針對GeoCoordinateWatcher在連接Location Service時的狀況定義了三個:

    Unknown (連接Location Service是未知的); Granted (連接Location Service是合理的); Denied (連接Location Service是被拒絕的)。

 

c.  GeoPositionStatus (GeoPositionStatus Enumeration)

    針對GeoCoordinateWatcher取得Location Service的Status值,透過列舉的方式來呈現,包括:    Disabled、Ready、Initializing、NoData,共四種。

 

d. GeoCoordinateWatcher.PositionChange

    當Location Service發現座標發生改變時,所觸發的事件。配合MovementThreshold來進行,當超過最小距離時,開始擷取

    Location Data比對是否發生改變,如果有則觸發EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>事件。

 

e. GeoCoordinateWatcher.StatusChanged

    針對Location Service的使用需要特別注意:當Location Service無法使用時的處理。舉例來說,一般啟動手機的GPS定位服務,

    約需要15秒的時間,當今天已經超過120秒時,Application應該要設計提醒用戶可能無法存取定位服務的提示訊息。

 

f. GeoCoordinate

    該類別主要儲存了緯度與經度座標所決定的地理位置,也包括高度、精準度、速度和路線資訊。它的資訊透過PositionChange事件來

    給予,該類別也提供一些重要的資訊讓程式中可以直接取得:

名稱 說明
Altitude 取得GeoCoordinate的高度資訊,以公尺為單位。
Course 取得或設定相對於真北方的航向,以度為單位。

HorizontalAccuracy

取得或設定GeoCoordinate所指定之緯度與經度的精準度,以公尺為單位。
IsUnKnown 取得GeoCoordinate是否不包含緯度或經度的資料。
Latitude 取得或設定GeoCoordinate緯度。
Longitude 取得或設定GeoCoordinate經度。
Speed 取得或設定每秒移動的速度,以公尺為單位。
VerticalAccuracy 取得或設定GeoCoordinate所指定的高度精準度,以公尺為單位。

 

了解了Location Service中重要的元件:GeoCoordinateWatcher之後,簡單寫個範例來玩玩看:

   1: using System.Device.Location;
   2:  
   3: namespace LocationAndMap
   4: {
   5:     public partial class MainPage : PhoneApplicationPage
   6:     {
   7:         // Constructor
   8:         public MainPage()
   9:         {
  10:             InitializeComponent();
  11:  
  12:             StartWatchGPS();
  13:         }
  14:  
  15:         /// <summary>
  16:         /// 啟動GeoCoordinateWatcher元件,並且註冊Status與Position改變的事件
  17:         /// </summary>
  18:         private void StartWatchGPS()
  19:         {
  20:             GeoCoordinateWatcher watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
  21:             watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
  22:             watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
  23:  
  24:             watcher.Start();
  25:         }
  26:  
  27:         void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
  28:         {            
  29:             //記錄Status的改變
  30:             tblStatus.Text = string.Format("{0},{1}", tblStatus.Text, e.Status );
  31:         }
  32:  
  33:         void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
  34:         {
  35:             //取得經緯度
  36:             tblLatitude.Text = e.Position.Location.Latitude.ToString();
  37:             tblLongitude.Text = e.Position.Location.Longitude.ToString();
  38:         }
  39:     }
  40: }

實作起來非常容易,主要是會使用GeoCoordinateWatcher與對應的事件處理即可。

 

[注意]

由於GeoCoordinateWatcher針對Location Data取得後的觸發,預設使用asynchronous(非同步)的方式,通知PositionChage,

讓程式知道要取得新的Position屬性,然而,GeoCoodinateWatcher也提供synchronous(同步)的立即執方式讓程式等待指定

的Timestamp後,回傳取得目前GeoCoordinateWatcher連接Location Service的狀態。使用方式:

   1: //使用同步的方式,取得目前Watcher是否能正常連接到Location Service;
   2: bool tStarted = gWatcher.TryStart(true, TimeSpan.FromMilliseconds(60000));
   3:  
   4: //識別Watcher是否連接到Location Service
   5: if (started)
   6: {
   7:     // Status = Ready代表取得Location Service,並且已啟動
   8:     if (watcher.Status == GeoPositionStatus.Ready)
   9:     {
  10:       txtStatus.Text = "Location data已可正常使用";
  11:     }
  12:     else
  13:     {
  14:       txtStatus.Text = "Location data未能正常使用";
  15:     }
  16: }
  17: else
  18: {
  19:     txtStatus.Text = "Tlocation service啟動失敗";
  20: }

 

以上是針對使用Location Service的介紹,但是只透過GeoCoordinateWatcher取得Position資訊是不夠的,

因此,接下來將針對結合Map的部分加以介紹:

 

〉使用Bing Maps API:

A. 基本事項

A-1. 申請Bing Maps API Key;前往該網址:https://www.bingmapsportal.com/申請Key,一個開發者有5個keys可以使用;

        註冊帳號與Windows Live ID相同,接著申請使用的Key,在Application Type選擇「Mobile」:

       image

A-2. 安裝取得的Key至Map Control的CredentialsProvider屬性中。

   1: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
   2:     <my:Map Height="601" HorizontalAlignment="Left" 
   3:         Margin="6,6,0,0" Name="map1" VerticalAlignment="Top" Width="444" 
   4:         CredentialsProvider="7NUk8MKOa0dTpEdi5sO5orlMvWrSqAJqilZT" />
   5: </Grid>

 

B. 定位目前位置與標記座標圖示

B-1. 使用Pushpin類別來建立需要標示的座標位置

   1: private void AppPushpin(object sender, EventArgs e)
   2: {
   3:     //設定目的地座標
   4:     GeoCoordinate tTarget = new GeoCoordinate(25.007572, 121.521003);    
   5:     //產生圖標,指定圖標座標
   6:     Pushpin tPin = new Pushpin();
   7:     tPin.Location = tTarget;
   8:     
   9:     //要求取得座標zoom:12、設定地圖移至目標位置,將圖標加入地圖
  10:     map1.ZoomLevel = 12;
  11:     map1.Center = tTarget;
  12:     map1.Children.Add(tPin);
  13: }

       然而,Pushpin類別的Style是可以自訂的,可以參考<Pushpin-Styles in Bing Maps for the Windows Phone>的實作,

       該作者寫了蠻多的例子,透過XAML的定義後讓Pushpin直接套用Style就可以做到很漂亮的定位圖標。

      

B-2. 增合Media、Shapes至地圖中

       由於BingMap本身實作提供外部加入UserControl的功能,因此,除了簡單的Pushpin可以加入之外,也可以加入其他的

       元素至BinMap之中。以下就舉加入Video與Shapes的範例:

       B-2-1. 加入Meida,參考<Adding Media to the Map>

   1: public void addVideoToMap()
   2: {
   3:     //取得MayLayer元素,該元素採用圖層累加的方式呈現在BingMap上
   4:     MapLayer imageLayer = new MapLayer();
   5:  
   6:     MediaElement video = new MediaElement();
   7:     //定義MediaElement指定的影片來源
   8:     video.Source = new Uri(@"http://mschnlnine.vo.llnwd.net/d1/ch9/9/2/9/9/1/4/TCS2NBCOlympics_ch9.wmv",
   9:             UriKind.RelativeOrAbsolute);
  10:     //定義Video要呈現的大小
  11:     video.Opacity = 0.8;
  12:     video.Width = 250;
  13:     video.Height = 200;
  14:  
  15:     //指定MapLayer要加在於那一個座標上
  16:     Location location = new Location() { Latitude = 25.007572, Longitude = 121.521003 };
  17:     //指定Video要出現在MapLayer的那個位置
  18:     PositionOrigin position = PositionOrigin.Center;
  19:  
  20:     //定義MapLayer所呈現的內容; 座標
  21:     imageLayer.AddChild(video, location, position);
  22:     //將MapLayer加入Map
  23:     map1.Children.Add(imageLayer);
  24:     //移動Map焦點至指定的座標
  25:     map1.Center = new GeoCoordinate(25.007572, 121.521003);
  26: }

               MapLayer它是呈現層的類別,使用於geographic coordinates環境下,然而,該類別主要以圖層的方式運作,

               因此,在使用時,可以產生多個MayLayer依照需要的座標與內容進行設定,並且用圖層概念讓資訊多元呈現。

     B-2-2. 加入Shape,繪製一個範圍做為區域顯示,參考<Adding Shapes to the Map>,也可實作MapShapeBase類別自定Shape

               BingMap提供二種方式來繪製範圍:

               ‧MapPolygon:該類別透過一連串的座標清單定義出一個Shape,加置於Map上;

   1: void addNewPolygon()
   2: {
   3:     MapPolygon polygon = new MapPolygon();
   4:     //指定Polygon填滿的色系
   5:     polygon.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);
   6:     //指定Polygon邊框的色系
   7:     polygon.Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow);
   8:     polygon.StrokeThickness = 5;
   9:     polygon.Opacity = 0.7;
  10:     //指定座標清單
  11:     polygon.Locations = new LocationCollection() { 
  12:                         new Location(){ Latitude =20, Longitude =-20}, 
  13:                         new Location(){ Latitude =20, Longitude =20}, 
  14:                         new Location(){ Latitude =-20, Longitude =20}, 
  15:                         new Location(){ Latitude =-20, Longitude =-20} };
  16:     //將Polygon加入至地圖
  17:     map1.Children.Add(polygon);
  18: }

               ‧MapPolyline:該類別透過一連串的座標清單定義各自的座標於Map上,並且將座標點全部串聯成一條線;

   1: void addNewPolyline()
   2: {
   3:     MapPolyline polylin = new MapPolyline();
   4:     polylin.Fill = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Red);
   5:     polylin.Stroke = new System.Windows.Media.SolidColorBrush(System.Windows.Media.Colors.Yellow);
   6:     polylin.StrokeThickness = 5;
   7:     polylin.Opacity = 0.7;
   8:     polylin.Locations = new LocationCollection() { 
   9:                         new Location(){ Latitude = 24.997388, Longitude = 121.516044}, 
  10:                         new Location(){ Latitude = 24.982771, Longitude = 121.538564}, 
  11:                         new Location(){ Latitude = 25.007467, Longitude = 121.51663}, 
  12:                         new Location(){ Latitude = 25.007572, Longitude = 121.521003}};
  13:  
  14:     map1.Children.Add(polylin);
  15: }

 

C. 切換地圖模式:街景、衛星模式

    Bing Map支援地圖、街景、衛星模式三種,然而,要切換這些模式是非常容易的,如下的程式碼:

C-1. 切換街景模式(RoadMode)/切換衛星模式(AerialMode)

   1: //切換路況模式
   2: map1.Mode = new RoadMode();
   3:  
   4: //切換衛星模式
   5: map1.Mode = new AerialMode();

 

D. 導航說明

    使用地圖最常用的就是做為路線導航,然而在BingMap提供使用SOAP的方式讓程式可以指定「啟始位置」與「抵達位置」就可以自動回傳

    規劃的路線座標,讓程式可以直接繪製於地圖上,做法非常容易,如下:

    D-1. 在專案中加入Service References,然而要輸入的URL,可參考BingMap提供的SOAP Service,其中Bing Map API提供四種URL:

           參考來源<Visualizing Bing Routes on Windows Phone 7>

           ‧Geocodedev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc

           ‧Imagerydev.virtualearth.net/webservices/v1/imageryservice/imageryservice.svc

           ‧Routedev.virtualearth.net/webservices/v1/routeservice/routeservice.svc  (範例使用此連結)

           ‧Search :dev.virtualearth.net/webservices/v1/searchservice/searchservice.svc

            image

    D-2. 定義要路線規劃的啟始點與抵達點,並且產生路線規劃加置地圖上:

           D-2-1. 設定要求路線規劃的Request,並且設定相關資訊,包括:啟始位置:台北火車站;抵達位置:新店市公所捷運站;

                     使用的Map API Key、要求回傳的結果形式:

   1: private void InitializationService()
   2: {
   3:     //初始化要執行Route的RouteServiceClient
   4:     RouteService.RouteServiceClient tRouteClient = new RouteService.RouteServiceClient("BasicHttpBinding_IRouteService");
   5:     tRouteClient.CalculateRouteCompleted += new EventHandler<RouteService.CalculateRouteCompletedEventArgs>(tRouteClient_CalculateRouteCompleted);
   6:     
   7:     //建立Request執行個體
   8:     var tRouteRequest = new RouteRequest();
   9:     tRouteRequest.Credentials = new Credentials();
  10:     tRouteRequest.Credentials.ApplicationId = "AoNAtwhHc6T0K2Am5AC7zz7JZ6p17NUk8MKOa0dTpEdi5sO5orlMvWrSqAJqilZT";
  11:     //指定使用的距離單位
  12:     tRouteRequest.UserProfile = new UserProfile();
  13:     tRouteRequest.UserProfile.DistanceUnit = DistanceUnit.Kilometer;
  14:     //指定Route之後的結果,回傳一列系的路線節點
  15:     tRouteRequest.Options = new RouteOptions();
  16:     tRouteRequest.Options.RoutePathType = RoutePathType.Points;
  17:  
  18:     //設定要規劃的「啟始」與「抵達」
  19:     Waypoint tStartPoint = new Waypoint()
  20:     {
  21:         Description = "台北火車站",
  22:         Location = new Location() { Latitude = 25.051269, Longitude = 121.512386 }
  23:     };
  24:     Waypoint tArrivePoint = new Waypoint()
  25:     {
  26:         Description = "新店市公所捷運站",
  27:         Location = new Location() { Latitude = 24.9677, Longitude = 121.5414 }
  28:     };
  29:     //將二點加入規劃需求
  30:     tRouteRequest.Waypoints = new System.Collections.ObjectModel.ObservableCollection<Waypoint>();
  31:     tRouteRequest.Waypoints.Add(tStartPoint);
  32:     tRouteRequest.Waypoints.Add(tArrivePoint);
  33:     //開始規劃
  34:     tRouteClient.CalculateRouteAsync(tRouteRequest);
  35: }

           D-2-2. 處理回傳的結果,建立二個MapLayer來呈現路線與定位圖標:

   1: void tRouteClient_CalculateRouteCompleted(object sender, RouteService.CalculateRouteCompletedEventArgs e)
   2: {
   3:     //處理取得的規劃路線節點
   4:     if (e.Result != null && e.Result.Result != null
   5:         && e.Result.Result.Legs != null & e.Result.Result.Legs.Any())
   6:     {
   7:         var result = e.Result.Result; 
   8:         var legs = result.Legs.FirstOrDefault();
   9:         //宣告二個MapLayer來呈現路線與圖標
  10:         MapLayer tRouteResult = new MapLayer();
  11:         MapLayer tPushpinLayer = new MapLayer();
  12:         //使用Polyline來記錄路線節點
  13:         MapPolyline tPolyline = new MapPolyline();
  14:         tPolyline.Locations = new LocationCollection();
  15:         tPolyline.Stroke = new SolidColorBrush(Colors.Blue);
  16:         tPolyline.Opacity = 0.8;
  17:         tPolyline.StrokeThickness = 5;
  18:  
  19:         foreach (var tPoine in e.Result.Result.RoutePath.Points)
  20:         {
  21:             tPolyline.Locations.Add(new Location()
  22:             {
  23:                 Latitude = tPoine.Latitude,
  24:                 Longitude = tPoine.Longitude
  25:             });
  26:  
  27:             //將每一個節點加上pushpin
  28:             Pushpin tPushpin = new Pushpin();
  29:             tPushpin.Background = new SolidColorBrush(Colors.Red);
  30:             tPushpin.Location = new GeoCoordinate() { Latitude = tPoine.Latitude, Longitude = tPoine.Longitude };
  31:             tPushpinLayer.Children.Add(tPushpin);
  32:         }
  33:         tRouteResult.Children.Add(tPolyline);
  34:         //將圖標Layer與規劃Layer加到地圖
  35:         map1.Children.Add(tRouteResult);
  36:         map1.Children.Add(tPushpinLayer);
  37:         //設定地圖移動到適合看到啟始與抵達的畫面
  38:         map1.SetView(new LocationRect(tPolyline.Locations[0].Latitude,
  39:                                       tPolyline.Locations[0].Longitude,
  40:                                       tPolyline.Locations[tPolyline.Locations.Count-1].Latitude,
  41:                                       tPolyline.Locations[tPolyline.Locations.Count-1].Longitude));
  42:     }
  43: }

          D-3-3. 執行結果:

           image

    更詳細的操作可參考<Calculating a Route Using Bing Maps SOAP Services>或<[Silverlight] 使用BingMap 和 RouteService 做出路徑規劃>

    來進行導航的服務。

 

[補充]

a. Map Control改用Google Map的圖資,可以參考<[Silverlight] Phone 7中BingMap Control 使用中文台灣地圖(僅供研究教學用) >。

   針對這篇內容我覺得受益良多,但在閱讀時有些東西我也沒有看過,因此,做一些說明:

   a-1. TileSource (參考<Adding Tile Overlays to the Map>)

          TileSource提供動態抽換地圖的資源,它與MapLayer的使用方法接近,但用途差異很大,其中使用TileSource的UrlFormat屬性,

          即可以抽換圖資(將新的圖資加至原來的BingMap之上)的來源。可參考<Google Maps for Windows Phone 7 using Bing Map Control>。

 

======

開發WP7的App,我比較喜歡寫一些相關LBS的東西,一來是自己比較熟悉的東西,二來可以先從一些常見的需求下手,

進一步做更多整合的東西。因此,要做LBS或在地化的App時,Location(定位)與Map這二個元素一定要好好的結合使用,

因為這可以將用戶的食衣住行全都包進去了。分享一些學習的心得,如果有寫錯的地方也歡迎大家給意見。謝謝。

 

References:

Simulate Geo Location in Silverlight Windows Phone 7 emulator

Building Location Service Apps in Windows Phone 7  (必讀)

Windows Phone 7 Location Services (GPS/Maps) QuickApp (Using Silverlight for Windows Phone 7) (必讀)

Windows Phone 7+ Location service + Bing Maps (使用Bingmap前,必讀)

GPS, Location API and Calling Web Services - Day 3 - Part 10

閱讀過去發行的 WebWorld 電子報 - silverlight UI & [Silverlight] Phone 7中BingMap Control 使用中文台灣地圖(僅供研究教學用) (必讀)

Location and my privacy - Help and how-to

Bing Maps Silverlight Control for Windows Phone & Bing Maps

How to: Use Reactive Extensions to Emulate and Filter Location Data for Windows Phone (延伸Location Service,選擇)

Pushpin-Styles in Bing Maps for the Windows Phone & add a custom pushpin with Bing Maps and C#(參考)

Google Maps for Windows Phone 7 using Bing Map Control (必讀)

Visualizing Bing Routes on Windows Phone 7 (必讀)

Windows Phone 7 智慧型手機應用程式開發總覽

 

Dotblogs Tags: ,