Windows phone 8 已经不使用自家的bing地图,新地图控件可以指定制图模式、视图等。bing地图的定位误差比较大,在模拟器中测试新地图貌似比较理想。本节主要讲解下位置服务以及新地图控件的使用。
一、定位服务
通过手机定位服务可以开发利用手机地理位置的应用。我们可以通过应用监视手机行踪,配合地图使用可以用于导航等。定位服务可以及时取得手机地理位置,也可以持续跟踪手机移动,还可以在后台运行。
1. 立即获取当前位置
我们可以通过一次操作获取当前位置,下面的代码演示了实现的方法。
[C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
|
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 =
"系统设置关闭了位置服务."
;
}
}
}
|
2. 持续跟踪位置信息
如果开启持续跟踪手机位置,当手机移动距离超出设定距离时,就会触发位置改变事件,这个时候我们就可以通过环境信息计算出手机的行动轨迹,速度方向等。下面演示了如何持续跟踪。
[C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
|
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"
);
});
}
|
3. 在后台持续跟踪
位置跟踪可以作为服务在后台运行,这个时候我们不需要更新UI,为了使我们的应用可以作为服务运行,我们需要右键打开清单文件,选择用XML文本编辑器的方式,替换DefaultTask节点为如下信息:
[XML]
1
2
3
4
5
|
<
DefaultTask
Name
=
"_default"
NavigationPage
=
"MainPage.xaml"
>
<
BackgroundExecution
>
<
ExecutionType
Name
=
"LocationTracking"
/>
</
BackgroundExecution
>
</
DefaultTask
>
|
然后我们需要注册RunningInBackground事件,打开App.xaml添加事件Application_RunningInBackground,代码如下:
[XAML]
1
2
3
4
5
|
<!--处理应用程序的生存期事件所需的对象-->
<
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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
//确定应用是否在后台运行
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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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]
1
|
xmlns:maps="clr-namespace:Microsoft.Phone.Maps.Controls;assembly=Microsoft.Phone.Maps"
|
1.引入地图控件
在XAML中添加如下代码即可引入控件。我们看到Center就是指当前地图中心点的经纬度;ZoomLevel就是缩放级别; LandmarksEnabled 属性设置为 true 以在 Map 控件上显示地标; PedestrianFeaturesEnabled 设置为 true,以显示步行街构造。
[XAML]
1
2
3
4
5
6
7
8
9
|
<!--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
>
|
2.设置制图模式
在制图模式中有四个选项,分别如下:
Road:显示正常的默认二维地图。
Aerial:显示航测图。
Hybrid:显示与道路和标签重叠的地图的“航测”视图。
Terrain:为显示的高地和水域构造(例如高山和河流)显示自然地形图像。
下面看看如何切换。
[C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//切换制图模式
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
;
}
}
|
3.设置颜色模式
颜色分为明和暗两种,我们看看如何实现。
[C#]
1
2
3
4
5
6
7
|
//切换颜色模式
private
void
Button_Click_3(
object
sender, RoutedEventArgs e)
{
if
(MyMap.ColorMode == MapColorMode.Light)
MyMap.ColorMode = MapColorMode.Dark;
else
MyMap.ColorMode = MapColorMode.Light;
}
|
4.指定新视角位置
我们可以通过编程方式切换视角位置到新的经纬度,并可以指定切换时的过渡效果,这里指定的是抛物线的方式。
[C#]
1
2
3
4
5
|
private
void
Button_Click_1(
object
sender, RoutedEventArgs e)
{
//以抛物线的方式,把视角定位到光谷软件园中心湖上空。
MyMap.SetView(
new
GeoCoordinate(30.476724, 114.406563), 16, MapAnimationKind.Parabolic);
}
|
5.定位我的位置并标记
把地图定位到我的当前位置。这个时候就需要借助定位的功能,通过定位功能获取到的经纬度实例类型不一样,需要预先做一个转换。转换类CoordinateConverter如下。
[C#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
//添加其他控件到地图,标识我的当前位置
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);
}
|
6.获取行车路线
我们还可以通过定位和地图实现导航的功能,下面演示了,从我的当前位置(光谷软件园)到指定的位置(光谷创业街)如何行车。
[XAML]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
<
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#]
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
|
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();
}
}
}
作者:[Lipan]
|