Windows phone 8 已经不使用自家的bing地图,新地图控件可以指定制图模式、视图等。bing地图的定位误差比较大,在模拟器中测试新地图貌似比较理想。本节主要讲解下位置服务以及新地图控件的使用。
通过手机定位服务可以开发利用手机地理位置的应用。我们可以通过应用监视手机行踪,配合地图使用可以用于导航等。定位服务可以及时取得手机地理位置,也可以持续跟踪手机移动,还可以在后台运行。
我们可以通过一次操作获取当前位置,下面的代码演示了实现的方法。
[C#]private async void OneShotLocation_Click(object sender, RoutedEventArgs e) { //地理位置访问服务 Geolocator geolocator = new Geolocator(); //定义精度,米为单位 geolocator.DesiredAccuracyInMeters = 1; try { //开始获取当前位置的经纬度 Geoposition geoposition = await geolocator.GetGeopositionAsync(); LatitudeTextBlock.Text = "经度:" + geoposition.Coordinate.Latitude.ToString("0.00"); LongitudeTextBlock.Text = "纬度:" + geoposition.Coordinate.Longitude.ToString("0.00"); } catch (Exception ex) { if ((uint)ex.HResult == 0x80004004) { StatusTextBlock.Text = "系统设置关闭了位置服务."; } } }
如果开启持续跟踪手机位置,当手机移动距离超出设定距离时,就会触发位置改变事件,这个时候我们就可以通过环境信息计算出手机的行动轨迹,速度方向等。下面演示了如何持续跟踪。
[C#]Geolocator geolocator = null; bool tracking = false; private void TrackLocation_Click(object sender, RoutedEventArgs e) { if (!tracking) { //地理位置访问服务 geolocator = new Geolocator(); //精度级别 geolocator.DesiredAccuracy = PositionAccuracy.High; //超过多少米引发位置改变事件 geolocator.MovementThreshold = 100; //功能状态改变时 geolocator.StatusChanged += geolocator_StatusChanged; //位置改变时 geolocator.PositionChanged += geolocator_PositionChanged; tracking = true; TrackLocationButton.Content = "停止跟踪"; } else { geolocator.PositionChanged -= geolocator_PositionChanged; geolocator.StatusChanged -= geolocator_StatusChanged; geolocator = null; tracking = false; TrackLocationButton.Content = "跟踪位置"; StatusTextBlock.Text = "停止"; } } void geolocator_StatusChanged(Geolocator sender, StatusChangedEventArgs args) { string status = ""; switch (args.Status) { case PositionStatus.Disabled: status = "位置服务设置被禁用"; break; case PositionStatus.Initializing: status = "正在初始化"; break; case PositionStatus.NoData: status = "无数据"; break; case PositionStatus.Ready: status = "已准备"; break; case PositionStatus.NotAvailable: status = "无法使用"; break; case PositionStatus.NotInitialized: break; } Dispatcher.BeginInvoke(() => { StatusTextBlock.Text = status; }); } void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args) { Dispatcher.BeginInvoke(() => { LatitudeTextBlock.Text = "经度:" + args.Position.Coordinate.Latitude.ToString("0.00"); LongitudeTextBlock.Text = "纬度:" + args.Position.Coordinate.Longitude.ToString("0.00"); }); }
位置跟踪可以作为服务在后台运行,这个时候我们不需要更新UI,为了使我们的应用可以作为服务运行,我们需要右键打开清单文件,选择用XML文本编辑器的方式,替换DefaultTask节点为如下信息:
[XML]<DefaultTask Name="_default" NavigationPage="MainPage.xaml"> <BackgroundExecution> <ExecutionType Name="LocationTracking" /> </BackgroundExecution> </DefaultTask>
然后我们需要注册RunningInBackground事件,打开App.xaml添加事件Application_RunningInBackground,代码如下:
[XAML]<!--处理应用程序的生存期事件所需的对象--> <shell:PhoneApplicationService Launching="Application_Launching" Closing="Application_Closing" Activated="Application_Activated" Deactivated="Application_Deactivated" RunningInBackground="Application_RunningInBackground"/>
在App.xaml.cs中添加静态变量RunningInBackground和Geolocator,当Application_RunningInBackground事件时RunningInBackground为true,当Application_Activated事件时,RunningInBackground为false。代码如下:
[C#]//确定应用是否在后台运行 public static bool RunningInBackground { get; set; } //提供对当前地理位置的访问 public static Geolocator Geolocator { get; set; } // 激活应用程序(置于前台)时执行的代码 // 此代码在首次启动应用程序时不执行 private void Application_Activated(object sender, ActivatedEventArgs e) { RunningInBackground = false; } private void Application_RunningInBackground(object sender, RunningInBackgroundEventArgs args) { RunningInBackground = true; }
在mainpage中添加如下代码:
[C#]protected override void OnNavigatedTo(System.Windows.Navigation.NavigationEventArgs e) { if (App.Geolocator == null) { App.Geolocator = new Geolocator(); App.Geolocator.DesiredAccuracy = PositionAccuracy.High; App.Geolocator.MovementThreshold = 100; App.Geolocator.PositionChanged += geolocator_PositionChanged; } } void geolocator_PositionChanged(Geolocator sender, PositionChangedEventArgs args) { if (!App.RunningInBackground) { Dispatcher.BeginInvoke(() => { LatitudeTextBlock.Text = "经度:" + args.Position.Coordinate.Latitude.ToString("0.00"); LongitudeTextBlock.Text = "纬度:" + args.Position.Coordinate.Longitude.ToString("0.00"); }); } else { Microsoft.Phone.Shell.ShellToast toast = new Microsoft.Phone.Shell.ShellToast(); toast.Content = args.Position.Coordinate.Latitude.ToString("0.00") + "," + args.Position.Coordinate.Longitude.ToString("0.00"); toast.Title = "位置:"; toast.NavigationUri = new Uri("/Page1.xaml", UriKind.Relative); toast.Show(); } }
要用到新地图控件,需要先注册,在phone:PhoneApplicationPage注册标识。
[XAML]xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"
在XAML中添加如下代码即可引入控件。我们看到Center就是指当前地图中心点的经纬度;ZoomLevel就是缩放级别; LandmarksEnabled 属性设置为 true 以在 Map 控件上显示地标; PedestrianFeaturesEnabled 设置为 true,以显示步行街构造。
[XAML]<!--ContentPanel - 在此处放置其他内容--> <Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0"> <!--地图控件--> <maps:Map x:Name="MyMap" Center="30.5473, 114.2922" ZoomLevel="10" LandmarksEnabled="true" PedestrianFeaturesEnabled="true" /> <Button Foreground="Red" Content="指定位置" HorizontalAlignment="Left" Margin="295,530,0,0" VerticalAlignment="Top" Click="Button_Click_1" Width="151"/> <Button Foreground="Red" Content="制图模式" HorizontalAlignment="Left" Margin="10,530,0,0" VerticalAlignment="Top" Click="Button_Click_2"/> <Button Foreground="Red" Content="颜色模式" HorizontalAlignment="Left" Margin="134,530,0,0" VerticalAlignment="Top" Click="Button_Click_3"/> <Button Foreground="Red" Content="我的位置" HorizontalAlignment="Left" Margin="10,602,0,0" VerticalAlignment="Top" Click="Button_Click_4"/> </Grid>
在制图模式中有四个选项,分别如下:
Road:显示正常的默认二维地图。
Aerial:显示航测图。
Hybrid:显示与道路和标签重叠的地图的“航测”视图。
Terrain:为显示的高地和水域构造(例如高山和河流)显示自然地形图像。
下面看看如何切换。
[C#]//切换制图模式 private void Button_Click_2(object sender, RoutedEventArgs e) { switch (MyMap.CartographicMode) { case MapCartographicMode.Aerial: MyMap.CartographicMode = MapCartographicMode.Hybrid; break; case MapCartographicMode.Hybrid: MyMap.CartographicMode = MapCartographicMode.Road; break; case MapCartographicMode.Road: MyMap.CartographicMode = MapCartographicMode.Terrain; break; case MapCartographicMode.Terrain: MyMap.CartographicMode = MapCartographicMode.Aerial; break; } }
颜色分为明和暗两种,我们看看如何实现。
[C#]//切换颜色模式 private void Button_Click_3(object sender, RoutedEventArgs e) { if (MyMap.ColorMode == MapColorMode.Light) MyMap.ColorMode = MapColorMode.Dark; else MyMap.ColorMode = MapColorMode.Light; }
我们可以通过编程方式切换视角位置到新的经纬度,并可以指定切换时的过渡效果,这里指定的是抛物线的方式。
[C#]private void Button_Click_1(object sender, RoutedEventArgs e) { //以抛物线的方式,把视角定位到光谷软件园中心湖上空。 MyMap.SetView(new GeoCoordinate(30.476724, 114.406563), 16, MapAnimationKind.Parabolic); }
把地图定位到我的当前位置。这个时候就需要借助定位的功能,通过定位功能获取到的经纬度实例类型不一样,需要预先做一个转换。转换类CoordinateConverter如下。
[C#]public static class CoordinateConverter { /// <summary> /// 把定位位置转换为地图位置 /// </summary> /// <param name="geocoordinate"></param> /// <returns></returns> public static GeoCoordinate ConvertGeocoordinate(Geocoordinate geocoordinate) { return new GeoCoordinate ( geocoordinate.Latitude, geocoordinate.Longitude, geocoordinate.Altitude ?? Double.NaN, geocoordinate.Accuracy, geocoordinate.AltitudeAccuracy ?? Double.NaN, geocoordinate.Speed ?? Double.NaN, geocoordinate.Heading ?? Double.NaN ); } }
然后,我们需要在地图上画一个小正方形标记我的当前位置,并把地图定位到这里。
[C#]//添加其他控件到地图,标识我的当前位置 private async void Button_Click_4(object sender, RoutedEventArgs e) { //获取我的地理位置 Geolocator myGeolocator = new Geolocator(); //精度 myGeolocator.DesiredAccuracyInMeters = 1; Geoposition myGeoposition = await myGeolocator.GetGeopositionAsync(); Geocoordinate myGeocoordinate = myGeoposition.Coordinate; //转换经纬度GeoCoordinate GeoCoordinate myGeoCoordinate = CoordinateConverter.ConvertGeocoordinate(myGeocoordinate); //MessageBox.Show(myGeoCoordinate.ToString()); //定位地图到我的位置 MyMap.SetView(myGeoCoordinate, 16, MapAnimationKind.Parabolic); //画一个正方形,然后渲染在地图的我的当前位置上 Rectangle MyRectangle = new Rectangle(); MyRectangle.Fill = new SolidColorBrush(Colors.Black); MyRectangle.Height = 20; MyRectangle.Width = 20; MapOverlay MyOverlay = new MapOverlay(); MyOverlay.Content = MyRectangle; MyOverlay.GeoCoordinate = myGeoCoordinate; MyOverlay.PositionOrigin = new Point(0, 0.5); MapLayer MyLayer = new MapLayer(); MyLayer.Add(MyOverlay); MyMap.Layers.Add(MyLayer); }
我们还可以通过定位和地图实现导航的功能,下面演示了,从我的当前位置(光谷软件园)到指定的位置(光谷创业街)如何行车。
[XAML]<phone:PhoneApplicationPage.Resources> <DataTemplate x:Key="RouteListTemplate"> <TextBlock Text="{Binding}" FontSize="{StaticResource PhoneFontSizeMedium}" Margin="5,5,0,0"/> </DataTemplate> </phone:PhoneApplicationPage.Resources> <!--LayoutRoot 是包含所有页面内容的根网格--> <Grid x:Name="LayoutRoot" Background="Transparent"> <Grid.RowDefinitions> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> <RowDefinition Height="auto"/> <RowDefinition Height="*"/> </Grid.RowDefinitions> <TextBlock Text="地图导航" Grid.Row="0" FontSize="{StaticResource PhoneFontSizeLarge}" Margin="0,0,0,20"/> <maps:Map x:Name="MyMap" Grid.Row="1" Center="30.476724, 114.406563" ZoomLevel="13"/> <TextBlock Text="驾车路线" Grid.Row="2" FontSize="{StaticResource PhoneFontSizeLarge}" Margin="0,10,0,20"/> <phone:LongListSelector x:Name="RouteLLS" Grid.Row="3" Background="Transparent" ItemTemplate="{StaticResource RouteListTemplate}" LayoutMode="List" IsGroupingEnabled="False"/> </Grid>
[C#]
public partial class Page1 : PhoneApplicationPage { public Page1() { InitializeComponent(); this.GetCoordinates(); } RouteQuery MyQuery = null; GeocodeQuery Mygeocodequery = null; List<GeoCoordinate> MyCoordinates = new List<GeoCoordinate>(); private async void GetCoordinates() { Geolocator MyGeolocator = new Geolocator(); MyGeolocator.DesiredAccuracyInMeters = 5; Geoposition MyGeoPosition = null; try { MyGeoPosition = await MyGeolocator.GetGeopositionAsync(TimeSpan.FromMinutes(1), TimeSpan.FromSeconds(10)); } catch (UnauthorizedAccessException) { MessageBox.Show("系统设置已关闭位置服务。"); } catch (Exception ex) { } MyCoordinates.Add(new GeoCoordinate(MyGeoPosition.Coordinate.Latitude, MyGeoPosition.Coordinate.Longitude)); Mygeocodequery = new GeocodeQuery(); Mygeocodequery.SearchTerm = "光谷创业街"; Mygeocodequery.GeoCoordinate = new GeoCoordinate(MyGeoPosition.Coordinate.Latitude, MyGeoPosition.Coordinate.Longitude); Mygeocodequery.QueryCompleted += Mygeocodequery_QueryCompleted; Mygeocodequery.QueryAsync(); } void Mygeocodequery_QueryCompleted(object sender, QueryCompletedEventArgs<IList<MapLocation>> e) { if (e.Error == null) { MyQuery = new RouteQuery(); MyCoordinates.Add(e.Result[0].GeoCoordinate); MyQuery.Waypoints = MyCoordinates; MyQuery.QueryCompleted += MyQuery_QueryCompleted; MyQuery.QueryAsync(); Mygeocodequery.Dispose(); } } void MyQuery_QueryCompleted(object sender, QueryCompletedEventArgs<Route> e) { if (e.Error == null) { //获取具体的行程路线 Route MyRoute = e.Result; MapRoute MyMapRoute = new MapRoute(MyRoute); MyMap.AddRoute(MyMapRoute); List<string> RouteList = new List<string>(); foreach (RouteLeg leg in MyRoute.Legs) { foreach (RouteManeuver maneuver in leg.Maneuvers) { RouteList.Add(maneuver.InstructionText); } } RouteLLS.ItemsSource = RouteList; MyQuery.Dispose(); } } }