Click here to Skip to main content
Click here to Skip to main content
Go to top

Using Bing Maps for Windows 8 Metro Apps - C# / JavaScript

, 8 May 2013
Rate this:
Please Sign up or sign in to vote.
Building Windows 8 Metro applications using Bing Maps control.

Introduction 

Bing Maps SDK is available for Visual Studio 2012 RC and Windows 8 release preview, Bing Maps SDK is easy fast and cool control that you can use in your Metro App.

Bing Maps SDK for Metro style apps (Release Preview) combines the power of Windows 8 and Bing Maps™ to provide an enhanced mapping experience for Metro style apps. This SDK includes controls for apps built using JavaScript, as well as apps built using C#, C++, and Visual Basic. 

In this article we'll see how to use Bing Maps SDK for C# & JavaScript metro applications. 

Step 1: Maps Developer Account 

Before you can use Bing SDK from Windows 8 Metro App you need Maps Developer Account, open http://www.bingmapsportal.com/, create or enter with existing Windows Live account. Choose “Create or view keys” and create new Key for your application. 

C#

Copy and save that key, create a resource in App.xaml called BingCredentials with the key value. 

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

Step 2: Create Bing Maps Metro Application 

  1. Download the latest bits - Bing Maps SDK for Metro style apps
  2. Open Visual Studio 2012 RC, and create a Blank Project. (C# or JavaScript)  

JavaScript

“Add Reference” –> select “Bing Maps for JavaScript (Beta)….” and click OK.

C#

“Add Reference” –> select “Bing Maps for C#….” and click OK. 

After adding the Bing Maps reference to our project you will see a “Warning” icon on the Bing binary, the reason is Bing Maps doesn't support “Any CPU” configuration. 

You need to change the project configuration as describe here - http://msdn.microsoft.com/en-us/library/hh855146.aspx 

  • C#, Visual Basic: ARM, x86 or x64
  • C++: ARM, Win32 or x64

Read the reference API docs: Bing Maps SDK 

Step 3: Add Map Control

C#

Now open “MainPage.xaml” and just drag the “Map” control from ToolBox or add the following XAML code:

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

Run the application, using Touch or Mouse you can move the map and zoom in and out. 

JavaScript 

Inside default.html we add a div element under the body, this will be the map holder.

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

Now we need to load the map into that control, be just before we need to load the map module, in order to do that we’ll call Maps.loadMoudle and set a callback to another method called 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 });
        }));
    }
};  

After the Maps module is loaded the initMap function will place the Map into the mapdiv element, the function below defines several default values such as: 

  • Bing Map credentials
  • Center location
  • Map type
  • Zoom 

Finally we obtain the mapdiv element and define a new object called map defined from new Map that receives the div and the options.

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();
    }
}  

 

Step 4: Basics

C# + JavaScript

In this section we'll use Geolocator that require "Location" permissions, so make sure you enable “Location” as application capability. 

I’ve added couple of buttons on the right side to help us controlling the map view. 

C#

  • Zoom – the map control have a “ZoomLevel” double property, the zoom range is 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 – there are three types of maps: Aerial, Road and 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;
        }
    }  

     

  • Set Current Location – Using Geolocator to find current location, then using map SetView to center map based on the new location.
  • 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

  • Zoom – The JS map control has built in Zoom in and Zoom out but you can still change the zoom from code:
  • var zoomValue = map.getZoom();
    map.setView({ zoom: zoomValue + 2 }); 
  • Map Type - there are three types of maps : Aerial, Road and 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 });
    } 

  • Set Current Location – Using Geolocator to find current location, then using map SetView to center map based on the new location (Make sure you enable “Location” as application capability.) 
  • 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 });
        });
    }

Step 5: Push Pin 

In step 4 we used “Geolocator” to find our current position, then we center the map based on the new position.

Instead of just centering the map we’ll like to add a PushPin directly on our location. 

C#

First we add the “PushPin” control as a child of the Map, you can also create any user control for that (if you want new image etc…) 

<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>

Now, in order to place the PushPin on the desire location you need to use the MapLayer.SetPosition, passing the pushpin control and the location value. 

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 

We need to create new PushPin object and assign the location where the pushpin should be placed on the map. 

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);
    });
}  

 

Step 6: Tap and Pin  

One more functionality I’ll would like to add is “Tap”, this mean the user click tap on the map (or mouse click) and this will place a Push Pin where the user clicked.  

C#

First I’ve create a new User Control called CustomPin that only contains XAML for star shape.

 

<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> 

In the MainPage constructor we add the new User control inside the Map children collection, we can also add this directly from XAML.

private CustomPin pin;

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

We need to add a “Tapped” event for Map control  

<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> 

When tapped event is fired we can use the tapped event arguments to get the relative position of the tap position by called the GetPosition method, this will give us the pixel position on the Map control not the actual location. So we need to translate the  pixel position to location.

We can do that by calling the TryPixelToLocation method from the map control, then after we recived the location where the user clicked we can add a push pin and center the map based on the the new location. 

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 

One more functionality I’ll would like to add is “Tap”, this mean the user click tap on the map (or mouse click) and this will place a Push Pin where the user clicked.

Instead of using the standard PushPin image I want to load my own image as pin, so I add a “star.png” file to images folder. Also I add new event listener for "click" on the mapDiv element calling new function called tapped.

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

When the tapped event is fired we can use the tapped event arguments to get the relative position of the tap position by calling the tryPixelToLocation method from the map control, then after we received the location where the user clicked we can add a push pin and center the map based on the the new location. 

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);
} 

Step 7 - Apply Custom Tile Overlay - Google Maps 

I got a question from a friend who wanted to replace Bing Maps Tiles with Google Maps Tiles. 

You might ask yourself why? If you want Google Tiles just replace Bing Control and work with Google Maps…..  In this case I want to work with Bing Map vontrol because the benefits I get in Metro Applications in Windows 8 for C#, C++, VB.NET, and JavaScript.  

And I also want Google Maps language support. 

Currently Bing Maps doesn't support any language except English, and I want to display the map with the user natural language. 

Before we jump to the solution let’s talk about the common ground between Bing and Google Maps. 

  • Each tile consists of 256 x 256 pixels
  • Each succeeding zoom level divides the map into 4 N tiles, where N refers to the zoom level. For example:
    • at zoom level 1,each map divides the world up into a 2x2 grid for a total of 4 tiles;
    • at zoom level 2, each map divides up the world into a 4x4 grid for a total of 16 tiles, etc 
    • (From “Tile Coordinates” Google API) – Zoom Level 2 (4x4)
    • (From “Tile Coordinates and Quadkeys” MSDN) – Zoom Level 3 (8x8)

I order to replace the tile we need to have Google map url that will return the specific tile based on the X, Y, and Zoom level – and we have it here:

Google Maps –> calling "https://mts0.google.com/vt/hl=he&src=api&x=2&s=&y=1&z=3” will return: (X = 2 , Y = 1 , Zoom = 3) 

So what is the problem? The problem is that in order to replace Tiles in Bing Maps you need to pass the “QuadKey” value. - MapTileLayer Class 

As you can see from the code example below the TileSource gets the URL of the custom tile with a parameter called {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);
} 

What is QuadKey? - A quadkey uniquely identifies single tiles at particular levels of detail

To optimize the indexing and storage of tiles, the two-dimensional tile XY coordinates are combined into one-dimensional strings called quadtree keys, or “quadkeys” for short. Each quadkey uniquely identifies a single tile at a particular level of detail, and it can be used as an key in common database B-tree indexes. To convert tile coordinates into a quadkey, the bits of the Y and X coordinates are interleaved, and the result is interpreted as a base-4 number (with leading zeros maintained) and converted into a string. For instance, given tile XY coordinates of (3, 5) at level 3, the quadkey is determined as follows: 

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

Quadkeys have several interesting properties. First, the length of a quadkey (the number of digits) equals the level of detail of the corresponding tile. Second, the quadkey of any tile starts with the quadkey of its parent tile (the containing tile at the previous level). As shown in the example below, tile 2 is the parent of tiles 20 through 23, and tile 13 is the parent of tiles 130 through 133: 

So now that we understand what is QuadKey we understand that we need to translate this value into Google Map X,Y and Zoom, we can use the following method to convert the QuadKey into x, y, and level values. 

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.");
        }
    }
}   

But you don’t need to do that because the quadkey is automatically translated into those values, so instead of setting a static URL for TileOverlay we will set a function called convert that received the quadkey object. 

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);
} 
Bing Maps without Google Tiles

Bing Maps with Google Tiles

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

Share

About the Author

Shai Raiten
Architect Sela
Israel Israel
Shai Raiten is VS ALM MVP, currently working for Sela Group as a ALM senior consultant and trainer specializes in Microsoft technologies especially Team System and .NET technology. He is currently consulting in various enterprises in Israel, planning and analysis Load and performance problems using Team System, building Team System customizations and adjusts ALM processes for enterprises. Shai is known as one of the top Team System experts in Israel. He conducts lectures and workshops for developers\QA and enterprises who want to specialize in Team System.
 
My Blog: http://blogs.microsoft.co.il/blogs/shair/
Follow on   Twitter

Comments and Discussions

 
QuestionThe download links for the Javascript and Custom Tiles appear to be identical. Pinprofessionalroscler14-Jan-14 2:49 
GeneralUsSing Google Maps tiles on Bing Map is not allowed Pinmemberrbrundritt12-Jul-13 14:30 
QuestionIs it possible to publish this Bing Maps on Windows Store? PinmemberMember 999719927-Apr-13 5:38 
SuggestionAdding different tiles PinmemberBruceAutomatica28-Nov-12 6:53 
GeneralMy vote of 5 Pinmemberdelibey26-Nov-12 14:04 
QuestionFind the best zoom Level according to Pushpin PinmemberTheKornelis19-Nov-12 1:15 
AnswerRe: Find the best zoom Level according to Pushpin PinmemberTheKornelis19-Nov-12 23:58 
GeneralMy vote of 5 Pinmembercsharpbd18-Nov-12 7:37 
QuestionThe latest Bing SDK bits have moved to a new URL Pinmemberwolfstevent14-Nov-12 19:15 
AnswerRe: The latest Bing SDK bits have moved to a new URL PinmvpShai Raiten14-Nov-12 22:46 
QuestionI'm getting an error in VS 2012 Express for Windows 8 - Exception loading the design-time dll Pinmemberwolfstevent14-Nov-12 19:10 
QuestionVenue Maps Pinmemberjohwat26-Oct-12 2:32 
QuestionErrors on debugging Javascript app [modified] PinmemberNuthanSantharam11-Oct-12 19:29 
AnswerRe: Errors on debugging Javascript app PinmvpShai Raiten14-Nov-12 22:45 
Questionadd a user control in map layer of bing control Pinmemberutsavvishnoi3-Sep-12 20:05 
AnswerRe: add a user control in map layer of bing control PinmvpShai Raiten14-Nov-12 22:49 
QuestionNice but seems we need more advanced tips PinmemberKenny Woo9-Aug-12 5:51 
AnswerRe: Nice but seems we need more advanced tips PinmvpShai Raiten14-Nov-12 23:09 
GeneralMy vote of 5 PinmemberMihai MOGA14-Jul-12 20:12 
GeneralRe: My vote of 5 PinmvpShai Raiten26-Jul-12 21:10 
GeneralMy Vote of 5 PinmemberSZenah11-Jul-12 2:42 
GeneralRe: My Vote of 5 PinmvpShai Raiten11-Jul-12 5:18 
GeneralMy vote of 4 Pinmemberravithejag5-Jul-12 21:08 
GeneralRe: My vote of 4 PinmvpShai Raiten8-Jul-12 23:01 
GeneralMy vote of 5 PinmvpMarcelo Ricardo de Oliveira5-Jul-12 6:52 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web01 | 2.8.140926.1 | Last Updated 8 May 2013
Article Copyright 2012 by Shai Raiten
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid