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也是很重要的,就需要依賴以下的類別:
該類別主要提供當接收到Location Data時,它會根據初始化該類別時設定的篩選機制,自動選擇最適合的精度度Lcation Data,
針對初始化設定高精準度的條件有:Hight與Default二種<GeoPositionAccuracy Enumeration>。
針對GeoCoordinateWatcher而言,GPS在行動裝置中被設計是非常敏感的,因此,當從A地移動到B地就會觸發Location Data的
接收或改變的事件,但多少的移動範圍需要被觸發呢,可直接設定GeoCoordinateWatcher的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」:
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>
‧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
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. 執行結果:
更詳細的操作可參考<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 智慧型手機應用程式開發總覽