地理位置服务(Location Service)【WP7学习札记之十五】
Microsoft 地理位置服务允许开发者为 Windows Phone 创建位置感知应用程序。该服务从获得来源(如 GPS、Wi-Fi 和蜂窝)获取位置数据。它可以使用一个或多个来源推导出 Windows Phone 的位置,从而根据应用程序的需要平衡性能和电能利用。通过事件驱动的托管代码接口向应用程序公开位置。
◇定位服务体系结构:
第一层由 Windows Phone 设备中的硬件组成。这包括 GPS 接收器、Wi-Fi 和蜂窝收音机。这些可以全部充当精度和能耗级别各不相同的位置数据的提供程序;
在硬件的上面是本机代码层。该层直接与可用的位置数据来源通信并决定使用哪个来源,根据数据的可用性以及应用程序指定的性能要求确定设备的位置。本机代码层还借助 Microsoft 托管的 Web 服务与 Internet 通信,以从数据库查找与位置有关的信息;
定位服务的顶层是托管接口,通过 Windows Phone SDK 附带的 DLL 公开。应用程序使用该接口启动和停止定位服务,设置应用程序所需的精度级别以及从本机代码层(当它变为可用时)接收位置数据。
◇创建使用Location Service程序的最佳实践:
创建位置感知应用程序时,开发人员必须平衡应用程序的以下两个要求:具有精确位置数据;耗电量最小。在移动设备上,这两个应用程序要求是反比关系。生成不太精确位置信息的硬件(如 Wi-Fi 和蜂窝收音机)使用的电量要比 GPS 接收器(通常,可以获得更精确的位置数据)使用的电量小。设计应用程序时,要遵循两个基本原则。
为位置数据选择适当的精度级别:尽管定位服务使用多个位置信息来源,但是在任何给定的时间任何来源都可能会不可用(例如,无法访问 GPS 卫星或基站),本机代码层负责计算可用数据并选择最佳来源集。您的应用程序所需要做的就是在高精度或默认的电量优化设置之间进行选择。可以在初始化主定位服务类 GeoCoordinateWatcher 时设置该值。
GeoCoordinateWatcher watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.Default);
设置合理的移动阈值:由于移动设备中的 GPS 硬件没有天线,因此传感器通常设计为非常敏感。这种灵敏度可能会导致信号中有少量来自表面反射以及其他环境影响的噪音。主定位服务类 GeoCoordinateWatcher 显示 MovementThreshold 属性。该属性指定在引发 PositionChanged 事件之前必须进行的位置方面的最小更改。如果您将 MovementThreshold 属性设置为一个非常低的值,则可能会导致您的应用程序接收实际上是由信号噪音所导致的事件。为了平滑信号以便仅表示位置中的重大更改,请将 MovementThreshold 属性设置为至少 20 米。这也会使您应用程序的耗电量降低。将移动阈值设置为 0 米将导致频繁引发事件,一秒钟一个事件。该设置对于导航应用程序可能非常有用。
下面给出一个例子演示如何从 Windows Phone 的定位服务获取数据:
MainPage.xaml代码如下:
<phone:PhoneApplicationPage
x:Class="GeoPosition.MainPage"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:phone="clr-namespace:Microsoft.Phone.Controls;assembly=Microsoft.Phone"
xmlns:shell="clr-namespace:Microsoft.Phone.Shell;assembly=Microsoft.Phone"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
mc:Ignorable="d" d:DesignWidth="480" d:DesignHeight="768"
FontFamily="{StaticResource PhoneFontFamilyNormal}"
FontSize="{StaticResource PhoneFontSizeNormal}"
Foreground="{StaticResource PhoneForegroundBrush}"
SupportedOrientations="Portrait" Orientation="Portrait"
shell:SystemTray.IsVisible="True">
<!--LayoutRoot is the root grid where all page content is placed-->
<Grid x:Name="LayoutRoot" Background="Transparent">
<Grid.RowDefinitions>
<RowDefinition Height="Auto"/>
<RowDefinition Height="*"/>
</Grid.RowDefinitions>
<!--TitlePanel contains the name of the application and page title-->
<StackPanel x:Name="TitlePanel" Grid.Row="0" Margin="12,17,0,28">
<TextBlock x:Name="ApplicationTitle" Text="演示程序" Style="{StaticResource PhoneTextNormalStyle}"/>
<TextBlock x:Name="PageTitle" Text="地理位置" Margin="9,-7,0,0" Style="{StaticResource PhoneTextTitle1Style}"/>
</StackPanel>
<!--ContentPanel - place additional content here-->
<Grid x:Name="ContentPanel" Grid.Row="1" Margin="12,0,12,0">
<TextBlock Height="30" HorizontalAlignment="Left" Margin="27,76,0,0" Name="textBlock1" Text="经度" VerticalAlignment="Top" />
<TextBlock Height="30" HorizontalAlignment="Left" Margin="24,153,0,0" Name="textBlock2" Text="纬度" VerticalAlignment="Top" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="70,50,0,0" Name="logTxtBox" Text="" VerticalAlignment="Top" Width="460" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="70,128,0,0" Name="latTxtBox" Text="" VerticalAlignment="Top" Width="460" />
<Button Content="开始" Height="72" HorizontalAlignment="Left" Margin="70,299,0,0" Name="button1" VerticalAlignment="Top" Width="160" Click="button1_Click" />
<Button Content="结束" Height="72" HorizontalAlignment="Left" Margin="248,299,0,0" Name="button2" VerticalAlignment="Top" Width="160" Click="button2_Click" />
<TextBlock Height="30" HorizontalAlignment="Left" Margin="24,230,0,0" Name="textBlock3" Text="状态" VerticalAlignment="Top" />
<TextBox Height="72" HorizontalAlignment="Left" Margin="70,206,0,0" Name="statusTxtBox" Text="" VerticalAlignment="Top" Width="460" />
</Grid>
</Grid>
<!--Sample code showing usage of ApplicationBar-->
<!--<phone:PhoneApplicationPage.ApplicationBar>
<shell:ApplicationBar IsVisible="True" IsMenuEnabled="True">
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button1.png" Text="Button 1"/>
<shell:ApplicationBarIconButton IconUri="/Images/appbar_button2.png" Text="Button 2"/>
<shell:ApplicationBar.MenuItems>
<shell:ApplicationBarMenuItem Text="MenuItem 1"/>
<shell:ApplicationBarMenuItem Text="MenuItem 2"/>
</shell:ApplicationBar.MenuItems>
</shell:ApplicationBar>
</phone:PhoneApplicationPage.ApplicationBar>-->
</phone:PhoneApplicationPage>
MainPage.xaml.cs代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using Microsoft.Phone.Controls;
using System.Device.Location;
namespace GeoPosition
{
public partial class MainPage : PhoneApplicationPage
{
GeoCoordinateWatcher watcher;
// Constructor
public MainPage()
{
InitializeComponent();
}
private void button1_Click(object sender, RoutedEventArgs e)
{
if (watcher == null)
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);// using high accuracy
watcher.MovementThreshold = 20;// use MovementThreshold to ignore noise in the signal
//为 StatusChanged 和 PositionChanged 事件添加事件处理程序
watcher.StatusChanged += new EventHandler<GeoPositionStatusChangedEventArgs>(watcher_StatusChanged);
watcher.PositionChanged += new EventHandler<GeoPositionChangedEventArgs<GeoCoordinate>>(watcher_PositionChanged);
//
watcher.Start();
}
}
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
//回调,多线程操作
Dispatcher.BeginInvoke(() =>
{
logTxtBox.Text = e.Position.Location.Longitude.ToString("0.000");
latTxtBox.Text = e.Position.Location.Latitude.ToString("0.000");
});
}
void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Disabled :
if (watcher.Permission == GeoPositionPermission.Denied)
{
// The user has disabled the Location Service on their device.
statusTxtBox.Text = "you have this application access to location.";
}
else
{
statusTxtBox.Text = "location is not functioning on this device";
}
break;
case GeoPositionStatus.Initializing :
// The Location Service is initializing.
// Disable the Start Location button.
break;
case GeoPositionStatus.NoData :
statusTxtBox.Text = "location data is not available.";
break;
case GeoPositionStatus.Ready:
statusTxtBox.Text = "location data is available";
break;
}
}
private void button2_Click(object sender, RoutedEventArgs e)
{
watcher.Stop();
}
}
}
(注意这个多线程操作~)
在模拟器上运行效果如图:
具体请参见MSDN:Windows Phone 的位置(http://msdn.microsoft.com/zh-cn/library/ff431803(v=vs.92).aspx)