此文章由机器翻译。

Windows Phone 8.1

Windows Phone 8.1 中的映射

Keith Pijanowski

下载代码示例

每一代的 Windows Phone 带来改进的映射功能,和 Windows Phone 8.1 控新地图介绍 Visual Studio 2013 更新 3,也不例外。如果你需要的只是一个简单的地图,集中在一个指定的位置,你可以完成此与只需要几行代码的新地图控制。但是,如果您需要对用户交互作出响应,自定义控件的外观或计算路线,您还可以使用新的地图控件和映射服务 API 对于这些更复杂的情况。

这篇文章将涵盖新的 Windows Phone 8.1 地图控件和映射服务 Api。我要开始显示基本的地图显示功能,然后再看看显示如何将图钉放在地图上位于指定地址的更多高级功能。我还会展示你如何 XAML 将控件放在地图控件,以及如何获得 GPS 定位 (和地址),对应于地图控件上的特定点。这是有用的如果您想要允许用户触摸感兴趣的位置,然后确定 GPS 定位和街道地址。最后,我将演示如何计算开车和走路的方向。

好的姊妹篇,我会掩护的材料是 2014 年 9 月的文章,"创建一个位置感知应用程序与地理围墙,"被托尼冠军。一个地点坐标是周围可以与 Windows 注册一个 GPS 定位指定的区域。一旦注册了该区域,应用程序可以接收通知,当设备进入或离开该区域。地理围墙也是新的 Windows Phone 8.1。如果您要构建的应用程序使用地理围墙,请考虑使用地图控件以允许用户指定 geofences,并表明他们现有的 geofences。

在撰写本文时,是手机的为每个风味和平板电脑平台的不同地图控件:

  • Windows Phone Silverlight 8.0/8.1 应用程序: Microsoft.Phone.Maps.Controls.Map
  • Windows 应用商店 8.x 版应用程序: Bing.Maps.Map
  • Windows Phone 8.1 运行时应用程序: Windows.UI.Xaml.Controls.Maps

这篇文章的重点是新地图控件,可以为 Windows Phone 8.1 运行时编写的应用程序中使用的映射服务。

开始使用

验证您的应用程序您的 Windows Phone 应用程序需要每次使用地图控件或 Windows.Services.Maps 命名空间中的映射服务进行身份验证。要验证您的应用程序,您需要地图服务 ApplicationID 和地图服务的身份验证令牌,这两个国家可以从您的开发人员仪表板在 Windows Phone 开发人员中心中检索。你会发现从 MSDN 库文章在得到 ApplicationID 和 AuthenticationToken 的分步 bit.ly/1y78M2F

一旦你有,你需要将 ApplicationID 放在您的应用程序的 Package.appxmanifest 文件中。执行此操作的说明也在 MSDN 库文章。您也将在您将看到本文中稍后在代码中,使用地图服务的身份验证令牌。

功能许多这篇文章中的示例使用用户当前的位置和地图控件可能需要使用互联网。因此,互联网和位置的能力应宣布在应用程序清单文件中所示图 1。然而,映射服务还没有互联网连接时工作地图下载供脱机使用。下载地图的详细信息,请参阅"如何到下载和更新脱机映射 (XAML)"在 bit.ly/1nXBOrS

宣布互联网和定位功能
图 1 宣布互联网和定位功能

显示地图

显示一个简单的地图向页中添加地图控件是那样轻松加入任何其他控件。将控件从 Visual Studio 工具箱中拖动到页面上或您可以输入 XAML,如下所示:

<Maps:MapControl
  x:Name="myMapControl"
  MapServiceToken="Place your token here."
  Height="200" Width="300"
  HorizontalAlignment="Center"
  Margin="0,0,0,12"
  />

请注意 MapServiceToken 属性。此属性必须有地图服务身份验证令牌,被分配到您的应用程序开发人员仪表板的 Windows Phone 开发人员中心中的价值。另外,请注意您可以定位地图控件像其他任何控件。地图控件并不需要占据整个屏幕。一个小地图缩放到特定位置可以使有效视觉上包含特定位置信息的网页。另一方面,地图放大的时候 (或者可能缩小由用户) 将需要额外的房地产。这种映射,请考虑使用整页。

前面的 XAML 将产生枯燥无味的结果,如果被放在一个应用程序并运行。此控件将显示地图完全缩小。因此,使用地图控件的下一步是确定某个特定的位置为中心点的地图,以及缩放级别。

下面的代码首先计算该用户当前的位置,然后设置该位置到地图的中心属性。它也将缩放级别设置为 15。 缩放级别的最小值是 1,显示地图完全缩小 — — 并在您的地图控件中显示半个地球。缩放级别的最大值是 20,可以缩放地图控件特定的街道地址:

Geolocator geolocator = new Geolocator();
Geoposition geoposition = null;
try
{
  geoposition = await geolocator.GetGeopositionAsync();
}
catch (Exception ex)
{
  // Handle errors like unauthorized access to location
  // services or no Internet access.
}
myMapControl.Center = geoposition.Coordinate.Point;
myMapControl.ZoomLevel = 15;

将图像添加到地图控件如果您想要标记您的地图上的特定位置,您可以添加到地图控件的标题图像。中的代码图 2 将指向该设备的当前的位置,将从项目的资源文件夹中的图像添加到地图控件。

图 2 添加到地图控件的自定义图像

Geolocator geolocator = new Geolocator();
Geoposition geoposition = null;
try
{
  geoposition = await geolocator.GetGeopositionAsync();
}
catch (Exception ex)
{
  // Handle errors like unauthorized access to location services or no Internet access.
}
MapIcon mapIcon = new MapIcon();
mapIcon.Image = RandomAccessStreamReference.CreateFromUri(
  new Uri("ms-appx:///Assets/PinkPushPin.png"));
mapIcon.NormalizedAnchorPoint = new Point(0.25, 0.9);
mapIcon.Location = geoposition.Coordinate.Point;
mapIcon.Title = "You are here";
myMapControl.MapElements.Add(mapIcon);

要将图像添加到您的地图控件,首先创建一个 MapIcon 对象,然后将其位置属性设置为 Geopoint,表示感兴趣在你的地图上的位置。中的代码图 2 使用用户当前的位置。Title 属性是将图像添加到地图控件上方显示的文本。

若要指定图像,将使用 RandomAccessStreamReference.CreateFromUri 方法的 MapIcon 的图像属性设置。这是 Windows.Storage.Streams 命名空间中的静态方法。图像应该是大约 50 x 50 像素 ; 如果它是更大,它将被缩减。

当您的目的是图像的指向一个特定的位置时,它是图像的最佳的做法是图像的使用 MapIcon 的 NormalizedAnchorPoint 属性。此属性允许您指示在您将放置超过指定 Geopoint 的图像内点。如果您的图像是 pin 的一个图钉,这将是 pin 的尖端。如果您正在使用一个箭头图像,这一点将箭头的提示。NormalizedAnchorPoint 属性的默认值是使用的值创建一个 Point 对象 (0,0),其中表示图像的左上角。图像的右下角是 (1,1)。此坐标系描绘在图 3 与几个采样点。

坐标系统用于 NormalizedAnchorPoint 属性
图 3 坐标系统用于 NormalizedAnchorPoint 属性

如果用作地图图标的图像包含一个指针,NormalizedAnchorPoint 属性设置不正确,地图图标将指向错误的位置,在你的地图上。如果你的形象仅仅是一个点或小圆圈,您将使用一个值 (0.5,0.5),这是中心的形象。

如果你不指定图像,个地图控制已显示的默认图像图 4,将使用。

默认地图图标
图 4 默认地图图标

正如你所看到的默认的 MapIcon 图像具有指针位于底部中心的形象。因此,规范­AnchorPoint 属性需要设置为 (0.5、 1.0)。

最后的细节需要注意有关代码是 MapIcon 对象添加到地图的 MapElements 集合中,所以可以将多个 MapIcon 添加到您的地图,如果你想。

那里是地图图标和标题将显示没有保证。如果它将掩盖其他元素或在地图上的标签,它可能被隐藏。地图缩小 (缩放级别减少) 和地图控件进行调整以适应更多的信息,这是尤其如此。

中的代码图 2 显示在地图图 5,假设你正在看一场棒球比赛在芬威公园。

地图控件显示芬威公园在波士顿,马萨诸塞州
图 5 地图控件显示芬威公园在波士顿,马萨诸塞州

将控件添加到地图控件将 XAML 添加到地图控件的控件是类似于添加图像。下面的代码创建一个椭圆、 大小转一圈、 将其添加到地图控件,和中心地理坐标:

// Create a circle
Windows.UI.Xaml.Shapes.Ellipse fence = 
  new Windows.UI.Xaml.Shapes.Ellipse();
fence.Width = 30;
fence.Height = 30;
fence.Stroke = new SolidColorBrush(Colors.DarkOrange);
fence.StrokeThickness = 2;
MapControl.SetLocation(fence, geoposition.Coordinate.Point);
MapControl.SetNormalizedAnchorPoint(fence, new Point(0.5, 0.5));
myMapControl.Children.Add(fence);

请注意必须使用地图控件的静态函数 SetLocation 将附加到椭圆对象的地理坐标。同样,SetNormalizedAnchorPoint 函数用于附加的规范化锚点属性。

从 UIElement 或 MapItemsControl 继承的任何控件可以添加到地图控件使用这些技术。

获取位置

地图控件的心是 BasicGeoposition 对象,它可以表示使用只是三个数字的地球上的每个位置:纬度、 经度和海拔高度。不只是这是划分全球范围内的有效途径,也是精确。每个位置都有其自身独特的纬度、 经度和海拔高度。没有两个的位置将有相同的组合。

不幸的是,您的应用程序去接口与人类,人类不喜欢表示以纯数字值的位置。因此,全球范围内被划分成各大洲,国家在各大洲,国家,在区域内的城镇,城镇内的街道地区 (或国家) 内。而大陆的名称、 国家名称和邮政代码是独一无二的地区有时可以包含具有相同名称的两个或多个城镇。此外,一个小镇可能有两个或多个具有相同名称的街道。底线是可能的地址必须与两个或更多位置相混淆。当处理 (离的国家名称、 邮政编码或镇) 地址不完整表示形式,这是尤其如此。

因此,如果您的应用程序要拿出更多的用户当前的位置,可以直接从设备检索,你会需要以前描述的数值和已命名的系统之间进行转换。将地址转换到地理位置的过程称为地理编码。将地理位置转换为人类可读地址的过程被称为反向地理编码。

将地址转换为地理位置 (地理) 地理编码地址需要映射的使用服务 Api — — 在 Windows.Services.Maps 命名空间中找到的类的集合。若要使用这些 Api,您需要设置您的身份验证令牌在 ServiceToken MapService 类的静态属性。下面的代码行演示如何做到这一点 (这篇文章的其余部分将假定下面这行代码是在 OnLaunched 事件中应用程序):

MapService.ServiceToken = "Place your token here";

一旦设置的身份验证令牌,您可以使用 FindLocationsAsync 静态函数,在 MapLocationFinder 类中发现。下面介绍了如何使用 FindLocationsAsync 函数:

Geolocator geolocator = new Geolocator();
Geoposition currentPosition = await geolocator.GetGeopositionAsync();

MapLocationFinderResult result = await MapLocationFinder.FindLocationsAsync(
  address, currentPosition.Coordinate.Point, 5);

FindLocationsAsync 函数的第一个参数是一个地址的字符串表示形式。任何字符串可能会传递给此函数。后端映射服务,FindLocationsAsync 函数进行通信,会尽其所能,找到一个匹配的位置。第二个参数被称为参考点,表示应开始搜索的地理位置。此参数有时被称为一个暗示。如果使用得当可大幅度提高返回的位置与速度,他们计算的关联性。例如,如果您知道用户做一个本地搜索,然后通过在用户的当前位置作为参考点。但是,如果您知道用户规划她的假期,通过在她的酒店的位置。最后一个参数是将返回的地点的最大数目。

FindLocationsAsync 函数的返回值是一个 MapLocationFinderResult 类型的对象。如果搜索是成功还是失败,它会告诉你的。如果搜索成功,MapLocation­FinderResult 对象将包含一个 MapLocation 对象的集合。中的代码图 6 检查成功的搜索,并如果它是成功的地方到 ListView 控件集合,以便可以向用户显示所有匹配的位置。MapLocation 对象包含人类可读的地址信息和可以使用的个地图控制的地理信息。中的代码图 6 可以很容易将转换为地图控件上显示图钉。

图 6 检索位置搜索的结果

if (result.Status == MapLocationFinderStatus.Success)
{
  List<string> locations = new List<string>();
  foreach (MapLocation mapLocation in result.Locations)
  {
    // create a display string of the map location
    string display = mapLocation.Address.StreetNumber + " " +
      mapLocation.Address.Street + Environment.NewLine +
      mapLocation.Address.Town + ", " +
      mapLocation.Address.RegionCode + "  " +
      mapLocation.Address.PostCode + Environment.NewLine +
      mapLocation.Address.CountryCode;
    // Add the display string to the location list.
    locations.Add(display);
  }
  // Bind the location list to the ListView control.
  lvLocations.ItemsSource = locations;
}
else
{
  // Tell the user to try again.
}

地理位置转换的地址 (反向地理编码) 映射应用程序可以提供的另一个有用特性就是能够从地图控件感动地区获得一个人类可读的地址。此功能的代码,你首先需要去感动用户控件的位置的方式。这可以通过使用地图控件的 MapTapped 事件。地图控件也具有一个螺丝的事件,在地图控件的 XAML 子被触碰时被激发。地图控件的任何直接的自来水经过 MapTapped 事件。中的代码图 7 演示一种实现的 MapTapped 事件,在 TextBlock 控件中显示的抽头位置的地址。

图 7 找到一个抽头位置在地图上的地址控制

private async void myMapControl_MapTapped(MapControl sender, 
  MapInputEventArgs args)
{
  // Find the address of the tapped location.
  MapLocationFinderResult result =
    await MapLocationFinder.FindLocationsAtAsync(args.Location);
  if (result.Status == MapLocationFinderStatus.Success)
  {
    if (result.Locations.Count > 0)
    {
      string display = result.Locations[0].Address.StreetNumber + " " +
        result.Locations[0].Address.Street;
      tbAddress.Text = display;
    }
  }
}

有几个细节值得在此实现中的 MapTapped 事件呼唤。第一,已与抽头位置相关联的 geopoint 是通过 args 传递到此事件。位置。 FindLocationsAtAsync 函数可以使用此 geopoint 获取一个人类可读的地址。此函数是静态函数在 MapLocationFinder 类中,是映射服务 API (Windows.Services.Maps 命名空间) 的一部分。此函数返回一个 MapLocationFinderResult 对象,包含一个状态属性和一个 MapLocation 对象的集合。即使返回值允许为 MapLocation 对象的集合,通常会有只有一个 MapLocation 对象。然而,可能有一高层建筑在某个地方有两个或更多的街道地址与它关联的世界。如果用户单击大场或湖,仍然会返回值。如果附近没有实际的街道地址,MapLocation 对象将包含只区域、 国家和邮政编码。

路线

映射服务可以计算从给定的起始点到给定的终结点路线。地图控件本身是能够显示的路线,可以作为驾驶路线或步行路线计算。

驾车路线顾名思义,驾驶路线为了要赶在一些车辆不会走错了路,一条单行道,必须遵守所有其他规则的道路。中的代码图 8 计算行车路线从纽约城的中央车站去中央公园。

图 8 计算开车路线

// Start at Grand Central Station.
BasicGeoposition startLocation = new BasicGeoposition();
startLocation.Latitude = 40.7517;
startLocation.Longitude = -073.9766;
Geopoint startPoint = new Geopoint(startLocation);
// End at Central Park.
BasicGeoposition endLocation = new BasicGeoposition();
endLocation.Latitude = 40.7669;
endLocation.Longitude = -073.9790;
Geopoint endPoint = new Geopoint(endLocation);
// Get the route between the points.
MapRouteFinderResult routeResult =
await MapRouteFinder.GetDrivingRouteAsync(
  startPoint,
  endPoint,
  MapRouteOptimization.Time,
  MapRouteRestrictions.None,
  290);

该代码是相当简单的。静态函数 MapRouteFinder.GetDrivingRouteAsync 传递起点、 终点、 优化参数 (稍后说明) 和一个参数,指定限制 (也稍后介绍)。第五也是最后的参数是可选的标题,它指定当前方向­的用户在 com 中­通过度 (0 度代表北,180 度代表南,等等)。路线计算用户开车时,这非常有用。当使用标题参数时,路线是基于计算用户的当前方向的旅行。路线第一几双腿将包含用户到达最佳可能路线,这是特别有用,用户可能会在单程路上的城市内的军事演习朝着错误的方向前进。更改标题为在代码中的 135 图 8 将导致路由计算等,它包含一个法律的方式来改变方向。

一旦你有一个结果从 MapRouteFinder.GetDrivingRoute­异步功能,您需要检查它。如果成功检索到一条路线,然后作为转由转向指示显示。路线也可以直观地显示在地图控件。中的代码图 9 作为转由转向指示显示从中央车站到中央公园的路线。

图 9 遍历路由的双腿和路线机动

if (routeResult.Status == MapRouteFinderStatus.Success)
{
  // Use the route to initialize a MapRouteView.
  MapRouteView viewOfRoute = new MapRouteView(routeResult.Route);
  viewOfRoute.RouteColor = Colors.Blue;
  viewOfRoute.OutlineColor = Colors.Blue;
  // Add the new MapRouteView to the Routes collection
  // of the MapControl.
  MapwithDrivingRoute.Routes.Add(viewOfRoute);
  // Fit the MapControl to the route.
  await MapwithDrivingRoute.TrySetViewBoundsAsync(
    routeResult.Route.BoundingBox,
    null,
    Windows.UI.Xaml.Controls.Maps.MapAnimationKind.Bow);
}

MapRoute 对象包含指示要遍历该路线及路线以米为单位的长度应采取多长时间的摘要信息。MapRoute 对象通过使用 MapRouteLeg 对象和 MapRouteManeuver 对象组织路由信息。MapRoute 包含一个或多个 MapRouteLeg 对象,每个 MapRouteLeg 包含一个或多个 MapRouteManeuver 对象。将向用户显示的信息是 MapRouteManeuver 对象中。MapRouteManeuver 对象包含往往­传统指令退出信息 (如果适用)、 机动和机动通知 (如果适用) 的长度。Kind 属性也是非常有用的。示例值包括左的转弯、 右转弯,等等。它可以用于将图像与每个机动相关联。Kind 属性的可能值的完整列表可以发现在 bit.ly/1nXOyi7图 10 显示在应用程序中这些方向的将外观。

的转由转动方向行驶的路线
图 10 的转由转动方向行驶的路线

路线也可以显示图形在地图控件内 (见图 11)。

图 11 显示路线,直观地在地图控件中

// Display summary info about the route.
tbTurnByTurn.Inlines.Add(new Run()
{
  Text = "Total estimated time (minutes) = "
    + routeResult.Route.EstimatedDuration.TotalMinutes.ToString("F1")
});
tbTurnByTurn.Inlines.Add(new LineBreak());
tbTurnByTurn.Inlines.Add(new Run()
{
  Text = "Total length (kilometers) = "
    + (routeResult.Route.LengthInMeters / 1000).ToString("F1")
});
tbTurnByTurn.Inlines.Add(new LineBreak());
// Display the directions.
tbTurnByTurn.Inlines.Add(new Run()
{
  Text = "DIRECTIONS"
});
tbTurnByTurn.Inlines.Add(new LineBreak());
// Loop through the legs and maneuvers.
int legCount = 0;
foreach (MapRouteLeg leg in routeResult.Route.Legs)
{
  foreach (MapRouteManeuver maneuver in leg.Maneuvers)
  {
    tbTurnByTurn.Inlines.Add(new Run()
    {
      Text = maneuver.InstructionText
    });
    tbTurnByTurn.Inlines.Add(new LineBreak());
  }
}

在地图控件中显示一条路线,首先需要创建一个视图使用 MapRouteView 类的路线。一旦你有你可以将其添加到路由集合地图控件的视图。最后,地图控件的 TrySetViewBoundsAsync 函数将地图调整控件大小这样的整条路线是可见的。此函数甚至还带有动画参数,可提供一种视觉效果,如被重绘地图控件以适应路线。图 12 显示这会像应用程序中。

行驶的路线在地图控件中
图 12 行驶的路线在地图控件中

路由优化驾驶路线可以优化时间、 距离和交通方面。MapRouteOptimization 枚举提供的值在图 13 优化计算的一条路线。

图 13 MapRouteOptimization 枚举

时间 将优化路线,这样它可以在最短的时间距离和速度限制为每个机动路线覆盖。
距离 路由将优化这样它可以覆盖在给每个机动的距离最短的距离。
TimeWithTraffic 将优化路线,这样它可以在最短的时间距离,覆盖时速限制,以及当前的交通条件,为每个机动路线。

 

路由限制驾驶路线还可以计算有限制,如"没有公路"或"不收费的公路"。MapRoute­限制枚举提供中显示的值图 14 限制的驾驶路线计算。

图 14 MapRouteRestrictions 枚举

None 没有限制应用到的一条路线计算。路径可以包含包括公路、 隧道、 渡轮、 收费公路和土路的演习。
公路 路由将不会包含任何机动的飞机,在一条公路上行走。
运输量 路由将不包含任何在收费公路上行走的演习。
渡轮 路由将不包含任何需要使用的一艘渡轮的演习。
隧道 路由将不会包含任何机动的飞机,穿过一条隧道。
DirtRoads 路由将不会包含任何机动的飞机,在一条土路上行走。

 

这些值可以进行逻辑组合。例如,下面的代码是可防止使用高速公路和收费道路的限制:

MapRouteFinderResult routeResult =
  await MapRouteFinder.GetDrivingRouteAsync(
  startPoint,
  endPoint,
  MapRouteOptimization.Time,
  MapRouteRestrictions.Highways || MapRouteRestrictions.TollRoads);

行走的路线计算行走的路线非常相似的计算的驾驶路线。下面的代码将计算基于相同的结束点驾驶路线如行走路线:

// Get the route between the points.
MapRouteFinderResult routeResult =
  await MapRouteFinder.GetWalkingRouteAsync(
  startPoint,
  endPoint);

MapRouteFinder.GetWalkingRouteAsync 用于执行此计算的静态函数。不同计算的驾驶路线的相应函数,此函数具有允许优化、 限制或当前标题没有重载。这些属性不影响行走路线计算,因为速度限制和交通情况并不是有关。此外,可以轻松地更改当前标题栏。在最短的距离计算的行走路线。在图像图 15图 16 显示行走的路线,从中央车站到中央公园。

的转由转动方向行走的路线
图 15 的转由转动方向行走的路线

行走路线地图控件所示
图 16 行走路线地图控件所示

总结

在这篇文章我展示如何使用新的地图控件的 Windows Phone 8.1,包括基本的地图显示、 添加图像和添加控件。我也介绍了基础测绘服务 API,并看了看地理编码、 反向地理编码和路由计算。

应考虑到的映射相关的其他主题的数目:贴面平铺图像、 数据绑定和管理离线地图。你可以找到有关这些主题的 Windows 开发人员中心信息 (bit.ly/X7S7ei)。


Keith Pijanowski 在软件行业有 20 多年的经验。他曾为创业公司和大公司在从编写代码对商业发展的角色。目前,他正在为自己作为一名独立顾问。Pijanowski 住在纽约市区,但永远不会有为纽约体育队欢呼。联系到他在 keithpij@msn.comtwitter.com/keithpij

衷心感谢以下 Microsoft 技术专家对本文的审阅:迈克奥马利
迈克奥马利是高级项目经理,在微软的操作系统。他一直在 Mmap-相关开发商微软三年来的经验和在生成位置感知应用程序开发提出了每年。