65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 Windows 8 Metro 应用的 Bing Maps - C# / JavaScript

starIconstarIconstarIconstarIcon
emptyStarIcon
starIcon

4.97/5 (42投票s)

2012年6月22日

CPOL

8分钟阅读

viewsIcon

157777

downloadIcon

5327

使用 Bing Maps 控件构建 Windows 8 Metro 应用程序。

介绍 

Bing Maps SDK 可用于 Visual Studio 2012 RC 和 Windows 8 Release Preview,Bing Maps SDK 是一款易于使用、快速且炫酷的控件,您可以在您的 Metro 应用中使用它。

适用于 Metro 风格应用的 Bing Maps SDK (Release Preview) 结合了 Windows 8 和 Bing Maps™ 的强大功能,为 Metro 风格应用提供了增强的地图体验。该 SDK 包括适用于使用 JavaScript 构建的应用以及使用 C#、C++ 和 Visual Basic 构建的应用的控件。

本文将介绍如何为 C# 和 JavaScript Metro 应用程序使用 Bing Maps SDK。

步骤 1:地图开发者账户

在使用 Windows 8 Metro 应用的 Bing SDK 之前,您需要一个地图开发者账户,请访问 http://www.bingmapsportal.com/,使用现有 Windows Live 账户创建或登录。选择“创建或查看密钥”并为您的应用程序创建新密钥。

C#

复制并保存该密钥,在 App.xaml 中创建一个名为 BingCredentials 的资源,其值为该密钥。

<x:String 
  x:Key="BingCredentials">AnJwk78cjoJfBMQXAxC85FwLiLPwmy6
         paQ1TsTJVg_-62hNraRRUzXRz1RELKfHa</x:String>   

步骤 2:创建 Bing Maps Metro 应用程序

  1. 下载最新版本 - 适用于 Metro 风格应用的 Bing Maps SDK
  2. 打开 Visual Studio 2012 RC,并创建一个空白项目。(C# 或 JavaScript)

JavaScript

“添加引用” -> 选择“Bing Maps for JavaScript (Beta)……”并单击确定。

C#

“添加引用” -> 选择“Bing Maps for C#……”并单击确定。

在将 Bing Maps 引用添加到我们的项目后,您将看到 Bing 二进制文件上出现一个“警告”图标,原因是 Bing Maps 不支持“Any CPU”配置。

您需要更改项目配置,如下文所述 - http://msdn.microsoft.com/en-us/library/hh855146.aspx

  • C#、Visual Basic:ARM、x86 或 x64
  • C++:ARM、Win32 或 x64

阅读参考 API 文档:Bing Maps SDK 

步骤 3:添加地图控件

C#

现在打开“MainPage.xaml”,只需从工具箱中拖动“Map”控件,或添加以下 XAML 代码:

xmlns:Maps="using:Bing.Maps"
<Maps:Map Credentials="{StaticResource BingCredentials}" /> 

运行应用程序,使用触摸或鼠标,您可以移动地图并进行缩放。

JavaScript 

default.html 中,我们在 body 下添加了一个 div 元素,这将是地图的容器。

<body>
     <div id="mapdiv"></div>
</body> 

现在我们需要加载地图到该控件中,在此之前我们需要加载地图模块,为此我们将调用 Maps.loadMoudle 并设置一个回调函数到另一个名为 initMap 的方法。

app.onactivated = function (args) {
    if (args.detail.kind === activation.ActivationKind.launch) {
        if (args.detail.previousExecutionState !== activation.ApplicationExecutionState.terminated) {
        } else {
        }
        args.setPromise(WinJS.UI.processAll().then(function () {
            Microsoft.Maps.loadModule('Microsoft.Maps.Map', { callback: initMap });
        }));
    }
};  

在地图模块加载后,initMap 函数将地图放置在 mapdiv 元素中,下面的函数定义了几个默认值,例如:

  • Bing Map 凭证
  • 中心位置
  • 地图类型
  • 缩放级别

最后,我们获取 mapdiv 元素并定义一个名为 map 的新对象,该对象由 new Map 定义,接收 div 和选项。

function initMap() {
    try {
        var mapOptions =
        {
            credentials: "AvOW5Fz4QTsubTTdmaVnseeZnAQ0JYwbx_6zdMdgHk6iF-pnoTE7vojUFJ1kXFTP",
            center: new Microsoft.Maps.Location(50, 50),
            mapTypeId: Microsoft.Maps.MapTypeId.road,
            zoom: 5
        };
        var mapDiv = document.querySelector("#mapdiv");
        map = new Microsoft.Maps.Map(mapDiv, mapOptions);
    }
    catch (e) {
        var md = new Windows.UI.Popups.MessageDialog(e.message);
        md.showAsync();
    }
}  

 

步骤 4:基础知识

C# + JavaScript

在本节中,我们将使用 Geolocator,它需要“Location”权限,因此请确保将“Location”启用为应用程序功能。

我在右侧添加了几个按钮,以帮助我们控制地图视图。

C#

  • Zoom – 地图控件有一个“ZoomLevel”双精度属性,缩放范围为 0-20。
  •  

    private void btnZoomOut_Click(object sender, RoutedEventArgs e)
    {
        var zoom = map.ZoomLevel - 2;
        map.SetZoomLevel(zoom < minZoom ? minZoom : zoom);
    }
    
    private void btnZoomIn_Click(object sender, RoutedEventArgs e)
    {
        var zoom = map.ZoomLevel + 2;
        map.SetZoomLevel(zoom > maxZoom ? maxZoom : zoom);
    }
  • MapType – 有三种地图类型:Aerial、Road 和 Birdseye。
  • private void btnChangeMapType_Click(object sender, RoutedEventArgs e)
    {
        switch (map.MapType)
        {
            case Bing.Maps.MapType.Aerial:
                map.MapType = Bing.Maps.MapType.Birdseye;
                break;
            case Bing.Maps.MapType.Birdseye:
                map.MapType = Bing.Maps.MapType.Road;
                break;
            default:
                map.MapType = Bing.Maps.MapType.Aerial;
                break;
        }
    }  

     

  • 设置当前位置 – 使用 Geolocator 查找当前位置,然后使用地图 SetView 将地图居中于新位置。
  • private async void btnSetLocation_Click(object sender, RoutedEventArgs e)
    {
        Geolocator geolocator = new Geolocator();
        var pos = await geolocator.GetGeopositionAsync();
        Location location = new Location(pos.Coordinate.Latitude, pos.Coordinate.Longitude);
             
        //Center map view on current location.            
        map.SetView(location, 15.0f);
    } 

JavaScript

  • 缩放 – JS 地图控件内置了缩放功能,但您仍然可以从代码中更改缩放级别。
  • var zoomValue = map.getZoom();
    map.setView({ zoom: zoomValue + 2 }); 
  • 地图类型 - 有三种地图类型:Aerial、Road 和 Birdseye。
  • function changeMapType() {
        var type = map.getMapTypeId();
        switch (type) {
            case Microsoft.Maps.MapTypeId.aerial:
                type = Microsoft.Maps.MapTypeId.road;
                break;
            case Microsoft.Maps.MapTypeId.road:
                type = Microsoft.Maps.MapTypeId.birdseye;
                break;
            default:
                type = Microsoft.Maps.MapTypeId.aerial;
                break;
        }
        map.setView({ center: map.getCenter(), mapTypeId: type });
    } 

  • 设置当前位置 – 使用 Geolocator 查找当前位置,然后使用地图 SetView 将地图居中于新位置(确保将“Location”启用为应用程序功能)。
  • function centerPosition() {
        var geolocator = new Windows.Devices.Geolocation.Geolocator();
        geolocator.getGeopositionAsync().then(function (loc) {
            var mapCenter = map.getCenter();
            mapCenter.latitude = loc.coordinate.latitude;
            mapCenter.longitude = loc.coordinate.longitude;
            map.setView({ center: mapCenter, zoom: 18 });
        });
    }

步骤 5:图钉 (Push Pin)

在步骤 4 中,我们使用“Geolocator”查找了当前位置,然后将地图居中于新位置。

我们希望在我们的位置上直接添加一个 PushPin,而不仅仅是居中地图。

C#

首先,我们将“PushPin”控件添加为 Map 的子项,您也可以为此创建任何用户控件(如果您想要新图像等)。

<Maps:Map x:Name="map" Margin="60,0,0,0" 
          Credentials="{StaticResource BingCredentials}" 
          ShowScaleBar="True" ZoomLevel="5" 
          Grid.Row="1" ShowTraffic="False" >
    <Maps:Map.Children>
        <Maps:Pushpin x:Name="pushPin" />
    </Maps:Map.Children>
</Maps:Map>

现在,为了将 PushPin 放置在所需位置,您需要使用 MapLayer.SetPosition,传入图钉控件和位置值。

private async void btnSetLocation_Click(object sender, RoutedEventArgs e)
{
    Geolocator geolocator = new Geolocator();
    var pos = await geolocator.GetGeopositionAsync();
    Location location = new Location(pos.Coordinate.Latitude, pos.Coordinate.Longitude);
    //Center map view on current location.
    MapLayer.SetPosition(pushPin, location);
    map.SetView(location, 15.0f);
} 

JavaScript 

我们需要创建一个新的 PushPin 对象,并指定图钉应放置在地图上的位置。

function addPushPin(location) {
    map.entities.clear();       
    var pushpin = new Microsoft.Maps.Pushpin(location, null);
    map.entities.push(pushpin);
}
function centerPosition() {
    var geolocator = new Windows.Devices.Geolocation.Geolocator();
    geolocator.getGeopositionAsync().then(function (loc) {
        var mapCenter = map.getCenter();
        mapCenter.latitude = loc.coordinate.latitude;
        mapCenter.longitude = loc.coordinate.longitude;
        map.setView({ center: mapCenter, zoom: 15 });
        addPushPin(mapCenter);
    });
}  

 

步骤 6:点击和图钉 

我希望添加的另一项功能是“Tap”,这意味着用户点击地图(或鼠标单击),这将在用户点击的位置放置一个图钉。

C#

首先,我创建了一个名为 CustomPin 的新用户控件,其中仅包含星形 XAML。

 

<Canvas>
    <Path x:Name="border" 
       Stretch="Fill" 
       Data="F1 M 0,217.042L 227.5,217.042L 297.875, 0L 367.542,217L 
            595.542,217L 410.208,353.667L 480.708, 569.667L 297.208,
            436.667L 116.208,568.167L 185.708,352.667L 0,217.042 Z" 
       Width="25" Height="25"  Margin="-12.5,-12.5,0,0"
       Fill="Yellow" Stroke="Red">
    </Path>
</Canvas> 

MainPage 构造函数中,我们将新用户控件添加到 Map 的子集合中,我们也可以直接从 XAML 添加它。

private CustomPin pin;

public MainPage()
{
   this.InitializeComponent();
   pin = new CustomPin();
   map.Children.Add(pin);
} 

我们需要为 Map 控件添加一个“Tapped”事件。

<Maps:Map x:Name="map" Margin="60,0,0,0" 
        Credentials="{StaticResource BingCredentials}" 
        ShowScaleBar="True" ZoomLevel="5" 
        Grid.Row="1" ShowTraffic="False" 
        Tapped="map_Tapped">
    <Maps:Map.Children>
        <Maps:Pushpin x:Name="pushPin" />
    </Maps:Map.Children>
</Maps:Map> 

当触发 Tapped 事件时,我们可以使用 Tapped 事件参数通过调用 GetPosition 方法来获取点击的相对位置,这将给出地图控件上的像素位置,而不是实际位置。因此,我们需要将像素位置转换为实际位置。

我们可以通过调用地图控件的 TryPixelToLocation 方法来实现这一点,然后在我们 收到 用户点击的位置后,我们可以添加一个图钉并将地图居中于新位置。

private void map_Tapped(object sender, TappedRoutedEventArgs e)
{
    var pos = e.GetPosition(map);
    Location location;
    map.TryPixelToLocation(pos, out location);
    MapLayer.SetPosition(pin, location);
    map.SetView(location);
} 

JavaScript 

我希望添加的另一项功能是“Tap”,这意味着用户点击地图(或鼠标单击),这将在用户点击的位置放置一个图钉。

我不使用标准的图钉图像,而是想加载自己的图像作为图钉,因此我向 images 文件夹添加了一个“star.png”文件。我还为 mapDiv 元素添加了新的“click”事件侦听器,调用一个名为 tapped 的新函数。

var mapDiv = document.querySelector("#mapdiv"); 
mapDiv.addEventListener('click', tapped, false);
map = new Microsoft.Maps.Map(mapDiv, mapOptions);

tapped 事件触发时,我们可以使用 Tapped 事件参数通过调用地图控件的 tryPixelToLocation 方法来获取点击的相对位置,然后,在我们收到用户点击的位置后,我们可以添加一个图钉并将地图居中于新位置。

function tapped(location) {
 var mapCenter = map.getCenter();
 var pos = map.tryPixelToLocation(new Microsoft.Maps.Point(location.clientX, location.clientY),
                                      Microsoft.Maps.PixelReference.control);
    mapCenter.latitude = pos.latitude;
    mapCenter.longitude = pos.longitude;
    map.setView({ center: mapCenter });
    var pin = new Microsoft.Maps.Pushpin({ 
        latitude: pos.latitude, longitude: pos.longitude },     {
        icon: '/images/star.png',
        anchor: new Microsoft.Maps.Point(8, 8)
    });
    map.entities.clear();
    map.entities.push(pin);
} 

步骤 7 - 应用自定义图块叠加层 - Google Maps

我收到一位朋友的提问,他想用 Google Maps 图块替换 Bing Maps 图块。

您可能会问为什么?如果您想要 Google 图块,只需替换 Bing 控件并使用 Google Maps……在这种情况下,我想使用 Bing Map 控件,因为它在 Windows 8 的 Metro 应用程序中为 C#、C++、VB.NET 和 JavaScript 提供了优势。

而且我还需要 Google Maps 的语言支持。

目前 Bing Maps 不支持除英语以外的任何语言,而我想用用户母语显示地图。

在开始解决方案之前,让我们先谈谈 Bing 和 Google Maps 的共同点。

  • 每个图块由 256 x 256 像素组成。
  • 每个后续缩放级别将地图划分为 4 的 N 次方个图块,其中 N 指的是缩放级别。例如:
    • 在缩放级别 1,每张地图将世界划分为 2x2 网格,总共 4 个图块;
    • 在缩放级别 2,每张地图将世界划分为 4x4 网格,总共 16 个图块,依此类推。
    • (来自“Tile Coordinates” Google API) – 缩放级别 2 (4x4)
    • (来自“Tile Coordinates and Quadkeys” MSDN) – 缩放级别 3 (8x8)

为了替换图块,我们需要一个 Google 地图 URL,该 URL 将根据 X、Y 和缩放级别返回特定图块 – 我们在这里找到了它:

Google Maps – 调用“https://mts0.google.com/vt/hl=he&src=api&x=2&s=&y=1&z=3”将返回:(X = 2, Y = 1, Zoom = 3)

那么问题是什么? 问题是,为了在 Bing Maps 中替换图块,您需要传递“QuadKey”值。 - MapTileLayer Class

从下面的代码示例可以看出,TileSource 获取自定义图块的 URL,该 URL 带有名为 {quadkey} 的参数。

C#

MapTileLayer tileLayer = new MapTileLayer();
tileLayer.TileSource = 
  "http://www.microsoft.com/maps/isdk/ajax/layers/lidar/{quadkey}.png";
map.TileLayers.Add(tileLayer); 

JavaScript

function addTileOverlay() {
    var options = { uriConstructor: 
        "http://www.microsoft.com/maps/isdk/ajax/layers/lidar/{quadkey}.png"};
    var tileSource = new Microsoft.Maps.TileSource(options);
    var tilelayer = new Microsoft.Maps.TileLayer({ mercator: tileSource });
    map.entities.push(tilelayer);
} 

什么是 QuadKey? - Quadkey 唯一标识特定详细级别下的单个图块。

为了优化图块的索引和存储,二维图块 XY 坐标被组合成一维字符串,称为四叉树键(或简称“quadkeys”)。每个 quadkey 唯一标识特定详细级别下的单个图块,并可用作常见数据库 B 树索引中的键。要将图块坐标转换为 quadkey,我们会交织 Y 和 X 坐标的位数,并将结果解释为基数为 4 的数字(保持前导零)并转换为字符串。例如,给定级别 3 的图块 XY 坐标 (3, 5),quadkey 确定如下:

  • tileX = 3 = 011 2
  • tileY = 5 = 101 2
  • quadkey = 100111 2 = 213 4 = “213”

Quadkeys 有几个有趣的特性。首先,quadkey 的长度(数字的位数)等于相应图块的详细级别。其次,任何图块的 quadkey 都以其父图块(上一级别的包含图块)的 quadkey 开头。如以下示例所示,图块 2 是图块 20 到 23 的父级,图块 13 是图块 130 到 133 的父级:

现在我们理解了 QuadKey 是什么,我们就明白我们需要将其值转换为 Google Map 的 X、Y 和缩放级别。我们可以使用以下方法将 QuadKey 转换为 x、y 和级别值。

C#

public static void QuadKeyToTileXY(string quadKey, 
              out int tileX, out int tileY, out int levelOfDetail)
{
    tileX = tileY = 0;
    levelOfDetail = quadKey.Length;
    for (int i = levelOfDetail; i > 0; i--)
    {
        int mask = 1 << (i - 1);
        switch (quadKey[levelOfDetail - i])
        {
            case '0':
                break;
            case '1':
                tileX |= mask;
                break;
            case '2':
                tileY |= mask;
                break;
            case '3':
                tileX |= mask;
                tileY |= mask;
                break;
            default:
                throw new ArgumentException("Invalid QuadKey digit sequence.");
        }
    }
}   

但是您不需要这样做,因为 quadkey 会自动转换为这些值,因此,我们不是为 TileOverlay 设置静态 URL,而是设置一个名为 convert 的函数,该函数接收 quadkey 对象。

JavaScript

function convert(quadkey) {
    return "https://mts0.google.com/vt/hl=he&src=api&x=" + quadkey.x + 
      "&s=&y=" + quadkey.y + 
      "&z=" + quadkey.levelOfDetail;
}
function addTileOverlay() {
      var options = { uriConstructor: convert };
    var tileSource = new Microsoft.Maps.TileSource(options);
    var tilelayer = new Microsoft.Maps.TileLayer({ mercator: tileSource });
    map.entities.push(tilelayer);
} 
不带 Google 图块的 Bing Maps

带有 Google 图块的 Bing Maps

© . All rights reserved.