天天看點

wp7 地圖控件

在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」:

wp7 地圖控件

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

1: <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">      
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;      
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>

           ‧Geocode:dev.virtualearth.net/webservices/v1/geocodeservice/geocodeservice.svc

           ‧Imagery :dev.virtualearth.net/webservices/v1/imageryservice/imageryservice.svc

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

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

wp7 地圖控件

    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);      
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. 執行結果:

wp7 地圖控件

    更詳細的操作可參考<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這二個元素一定要好好的結合使用,

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

繼續閱讀