Windows Phone 7 Quick Tip: How to use Bing Maps in XNA games

October 22, 2010

A few days ago, a colleague of mine pointed out to this site which shows a Bing Maps usage within XNA game.


I was intrigued how to achieve it… I know that Silverlight applications for Windows Phone 7 could use Bing Maps control (see great usage tutorial here), but XNA games cannot render Silverlight controls…


To solve it, I decided to use a different approach for XNA – download image tiles and present them in XNA game.


Let’s get started. First, in order to use Bing Maps API you need a Bing Maps Account and Application Private Key.
The Bing Maps Account Center allows you to create keys to use the Bing Maps Control, Bing Maps SOAP Services, Bing Maps REST (representational state transfer) Services and Bing Spatial Data Services.


If you already have a key for using Bing, you may skip next few linesSmile.


Without a valid key, you won’t be able to see retrieve Bing Maps content from the web. To create the account use the following steps (shamelessly taken from here)



  1. Open your web browser and go to the following address: http://www.bingmapsportal.com.

  2. Click Create to create a new Bing Maps account using your Windows Live ID.

    alt


  3. On the next page, fill in your details.

    alt


  4. Once you’ve agreed the terms and registered and/or signed in, you may choose to create a new key for your application. Click the “Create or view keys” link on the left.

    alt


  5. On the next page fill in your application details and click Create Key.

    alt


  6. Below, you’ll see your new private key. Save this key, you will use it later.

    alt


Now let’s do some coding! I decided to create very simple XNA game, which uses Windows Phone 7 location services (on the device) or hardcoded location (on emulator). The game will query Bing maps services and display the received map on screen.


In addition I added my XboxXBOX Live avatar to the center of the map – this will simulate a game process.


Lastly I decided to add simple Zoom In/Out controls too.


Disclaimer: this sample is not an XNA tutorial. To learn about 2D game development with XNA game studio 4.0 for Windows Phone 7 please refer to great tutorial (which I had a great honor to be an author Winking smile) at new MSDN Apps Hub site (http://create.msdn.com/en-US/education/catalog/lab/catapult_wars).


My game will start with loading some assets at LoadContent function – it will load a font to draw text on screen, default background (to fill the screen while first map tile still downloading) and ZoomIn/ZoomOut images.


First – the references I will need in order to make whole thing work – please make sure all of the selected assemblies referenced in the project (some of them automatically referenced with new Windows Phone 7 Game project, some of them need to be referenced manually):


image


Make sure or add the following using statements at the top of the class:

using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Input.Touch;
using System.Device.Location;
using System.Net;
using Microsoft.Devices;

Let’s see the LoadContent function:

protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);

// TODO: use this.Content to load your game content here
font = Content.Load<SpriteFont>(“Font”);
mapImage = Content.Load<Texture2D>(“Nowhere”);
zoominImage = Content.Load<Texture2D>(“zoomin”);
zoomoutImage = Content.Load<Texture2D>(“zoomout”);

}


In addition I created a number of class level variables which will be used during the game:

GeoCoordinateWatcher watcher;
Texture2D mapImage;
Texture2D playerImage;
Texture2D zoominImage;
Texture2D zoomoutImage;
SpriteFont font;
GeoCoordinate co;
bool mapLoaded = false;
bool mapUpdated = false;

float baseScale = 0.04545454545454545454545454545455f;
float scale = 1f;
Vector2 zoomInPosition = new Vector2(20, 20);
Vector2 zoomOutPosition = new Vector2(20, 100);

const string bingAppKey = “MY_BING_MAPS_KEY”;

The GeoCoordinateWatcher will be used to observe location changes on the device; once location changes it will be retrieved to the GeoCoordinate variable.


Since Bing Maps uses scale between 1 and 22, I have to scale my textures accordingly.


baseScale variable is 1/22 in order to enable easy scale calculation.


bingAppKey is a private key received at step 6 during registration process.


Now game will draw a mapImage on screen and will display status text according to boolean variables introduced above:

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);

// TODO: Add your drawing code here
spriteBatch.Begin();

spriteBatch.Draw(mapImage, Vector2.Zero, Color.White);

if (mapLoaded)
DrawZoomControls();

if (null != playerImage)
{
Vector2 position =
new Vector2(GraphicsDevice.Viewport.Width / 2 –
(playerImage.Width * baseScale * scale) / 2,
GraphicsDevice.Viewport.Height / 2 –
(playerImage.Height * baseScale * scale) / 2);

spriteBatch.Draw(playerImage, position, null, Color.White, 0f,
Vector2.Zero, baseScale * scale, SpriteEffects.None, 0);
}

if (!mapLoaded || mapUpdated)
{
string text = !mapLoaded ? “Locating position…” : “Refining map…”;
Color color = !mapLoaded ? Color.White : Color.Red;
Vector2 size = font.MeasureString(text);
Vector2 position = new Vector2(10, GraphicsDevice.Viewport.Height – size.Y – 10);
spriteBatch.DrawString(font, text, position, color);
}

spriteBatch.End();
base.Draw(gameTime);
}

private void DrawZoomControls()
{
spriteBatch.Draw(zoominImage, zoomInPosition, Color.White);
spriteBatch.Draw(zoomoutImage, zoomOutPosition, Color.White);
}

The code snippet is pretty simple, yet requires some explanations. In XNA the on-screen object location and its Z-Index depends on draw order during the Draw function. In order to start drawing the function should Begin a sprite batch rendering process and End it after all objects are drawn. First, the function above draws a background texture which currently will be a “Nowhere” image and will be replaced by the map once it arrives from server. Next, if map already downloaded it will call to helper method to draw zoom controls at known fixed positions. This assures, that zoom texture will be paced above the background (Z-Index is higher). After then, if the player’s image (remember  – it will be my XBOX Live Avatar) arrived, it will display it at the center of the screen scaled accordingly to the map scale. Lastly, it will display “Loading” text if the map is not loaded yet or zoom level changed and map image being updated.


Running application at this stage produces the following result (both at emulator and the device):


image


To get map images I will use a WebClient class (same one which used also in Silverlight applications on Windows Phone 7).


Now it is a time to look at Bing Maps API; from MSDN documentation (http://msdn.microsoft.com/en-us/library/ff701716.aspx, http://msdn.microsoft.com/en-us/library/ff701724.aspx) I see, that the request URI should be build as follows:


http://dev.virtualearth.net/REST/version/Imagery/Metadata/imagerySet?key=BingMapsKey


For more info about version, Imagery, Metadata, etc. please refer to the Bing Maps APIs documentation.


In my sample I will use static image search by query on the emulator:

http://dev.virtualearth.net/REST/V1/Imagery/Map/AerialWithLabels/eiffel tower?zoomLevel=SCALE&mapSize=800,480&key=MY_BING_MAPS_KEY
and location centers at specified geographical coordinate (longitude and latitude) on the device:
http://dev.virtualearth.net/REST/V1/Imagery/Map/Aerial/LATITUDE,LONGTITUDE/SCALE?mapSize=800,480&key=MY_BING_MAPS_KEY

SCALE will be replaced by current scale and MY_BING_MAPS_KEY by my private key. On the device also LATITUDE and LONGTITUDE will be replaced by values reported by Windows Phone 7 location services.


In this quick-and-dirty sample I put the initialization code in Initialize function of XNA game class:

protected override void Initialize()
{
string requestUri = “”;
WebClient wc = null;

float currentScale = baseScale * scale * 22;

// TODO: Add your initialization logic here
if (Microsoft.Devices.Environment.DeviceType == DeviceType.Device)
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High); // Use high accuracy.
watcher.MovementThreshold = 20; // Use MovementThreshold to ignore noise in the signal.
watcher.StatusChanged += (sender, e) =>
{
if (e.Status == GeoPositionStatus.Ready)
{
mapUpdated = true;
co = watcher.Position.Location;
//watcher.Stop(); //Stop to conserve the battery – will disable the movement updates

requestUri = string.Format(“http://dev.virtualearth.net/REST/V1/Imagery/Map/Aerial/{0},{1}/{3}?mapSize=800,480&key={2}”,
co.Latitude, co.Longitude,
bingAppKey, currentScale);
wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “MAP”);
}
};
watcher.Start();
}
else
{
//Get static location (also by lat/long) on emu
requestUri = “http://dev.virtualearth.net/REST/V1/Imagery/Map/AerialWithLabels/eiffel tower?zoomLevel=”
+ currentScale.ToString() + “&mapSize=800,480&key=” + bingAppKey;
wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “MAP”);
}

//Download avatar anyway
requestUri = “http://avatar.xboxlive.com/avatar/Dark%20Virus%20ISR/avatar-body.png”;
wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “IMG”);

base.Initialize();
}


The code snippet above check the Environment variable in order to understand where it is being executed.


Note: this is not System.Environment available for all .NET applications, but Microsoft.Device.Environment specific to Windows Phone apps.


If the device is actual device (Windows Phone 7 hardware) the code snippet initializes GeoCoordinateWatcher and subscribes to StatusChanged events. Once the status turns to “Ready”, the code snippet gets current reported location and requests a map image using WebClient. It uses reported Longitude, Latitude and current zoom level; in addition it provides Bing Maps private key.


If the device is emulator (and GeoCoordinateWatcher will never return meaningful value), the code snippet uses WebClient to request a map centered at some landmark – in my case Eiffel Tower.


In both request WebClient passes desired image size 800×480 to match Windows Phone 7 landscape resolution.


Lastly, it uses another instance of WebClient to download my XBOX Live avatar.


Last parameter in all OpenReadAsync calls is a state object I’m using to identify which image is arrived.


Let’s see the wc_OpenReadCompleted method:

void wc_OpenReadCompleted(object sender, OpenReadCompletedEventArgs e)
{
if (e.Error == null)
{
if (e.UserState.ToString() == “MAP”)
{
mapImage = Texture2D.FromStream(graphics.GraphicsDevice, e.Result);
mapLoaded = true;
mapUpdated = false;
}
else
playerImage = Texture2D.FromStream(graphics.GraphicsDevice, e.Result);
}
}

Running the application at the current state produces the following output on the emulator:


image


and the following at the device:


image


Note small red rectangle I put on both images – there is a very small scaled avatar inside Smile


Now let’s enable zoom controls. First, I enabled a Tap gesture at the Game constructor:

TouchPanel.EnabledGestures = GestureType.Tap;

Next, at the Update function of the game I checked Available gestures and if available, their positions. I limited the scale between 1 and 22 (supported by Bing Maps) and if scale change detected used WebClient to get new map at desired zoom level:

protected override void Update(GameTime gameTime)
{
float prevScale = scale;

// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();

// TODO: Add your update logic here
while (TouchPanel.IsGestureAvailable && mapLoaded)
{
GestureSample sample = TouchPanel.ReadGesture();

//Prepare a rectangle around touch position to allow more comfortable touch
Rectangle touchPosition = new Rectangle((int)sample.Position.X – 5,
(int)sample.Position.Y – 5,
10, 10);

//Prepare a rectange from ZoomIn texture and on-screen position
Rectangle zoomInRect = new Rectangle((int)zoomInPosition.X, (int)zoomInPosition.Y,
zoominImage.Width, zoominImage.Height);

//Prepare a rectange from ZoomOut texture and on-screen position
Rectangle zoomOutRect = new Rectangle((int)zoomOutPosition.X, (int)zoomOutPosition.Y,
zoomoutImage.Width, zoomoutImage.Height);

//Check Touch and zoom rectangles intersection
if (touchPosition.Intersects(zoomInRect))
scale += 1;
else if (touchPosition.Intersects(zoomOutRect))
scale -= 1;

//Limit the result between 1 and 22 (scale values supported by Bing Maps
scale = MathHelper.Clamp(scale, 1, 22);
}

if (prevScale != scale)
{
string requestUri = “”;
WebClient wc = null;

float currentScale = baseScale * scale * 22;

if (Microsoft.Devices.Environment.DeviceType == DeviceType.Device)
requestUri = string.Format(“http://dev.virtualearth.net/REST/V1/Imagery/Map/Aerial/{0},{1}/{3}?mapSize=800,480&key={2}”,
co.Latitude, co.Longitude, bingAppKey, currentScale);
else
//Get static location (also by lat/long) on emu
requestUri = “http://dev.virtualearth.net/REST/V1/Imagery/Map/AerialWithLabels/eiffel tower?zoomLevel=”
+ currentScale.ToString() + “&mapSize=800,480&key=” + bingAppKey;

wc = new WebClient();
wc.OpenReadCompleted += new OpenReadCompletedEventHandler(wc_OpenReadCompleted);
wc.OpenReadAsync(new Uri(requestUri, UriKind.Absolute), “MAP”);
mapUpdated = true;
}

base.Update(gameTime);
}


Now when running the application on the device touching Zoom In/Out buttons produces the following results:


image


Note: in this image the next map image is still downloading…


image


and on the emulator:


image


From here the process is simple: add some real game logic, animations, some Bing Maps pushpins (using sample Bing Maps APIs), respond to drag/pinch gestures and voila – the XNA game for Windows Phone 7 with Bing Maps integrated is ready!


Completed sample hosted here. Note: you will need a Bing Maps Application Key to run it.


To see more of XNA 4.0 with Windows Phone 7, PC and Xbox 360 visit my session “XNA Studio 4.0: Code Once, Play Everywhere! PC, Xbox, Phone” at TechEd 2010 Israel/Development Tools & Technologies track. In this session you will see how to build the real world computer games running on all platforms together!


Stay tuned for more come!


Enjoy,


Alex

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>

6 comments

  1. trading signalNovember 11, 2011 ב 4:12

    La mejor información esencial es esto, que tiene tratar de hacer muy bien acerca de que el cuerpo de las personas sin hogar y de su mano. Estoy tan entusiasmado acerca de eso, después de leer este artículos útiles escrito sus opiniones y también que es tan atractivo para hacer algo por aquellos

    Reply
  2. ConnerAugust 7, 2012 ב 15:23

    I’m not sure exactly why but this web site is loading very slow for me. Is anyone else having this issue or is it a problem on my end? I’ll check back later on and see if the problem still exists.

    Reply
  3. PhillipsDecember 19, 2012 ב 22:32

    What’s up Dear, are you truly visiting this web page on a regular basis, if so then you will definitely get pleasant experience.

    Reply
  4. escortgirlsDecember 25, 2012 ב 3:48

    Gracias por la crítica sensata sobre blogs.microsoft.co.il. Yo y mi vecino se acaba de preparar para hacer una investigación al respecto. Tenemos un robo de un libro de nuestra biblioteca local, pero creo que he aprendido más de esta entrada. Estoy muy contento de ver la información tan grande que se comparte libremente por ahí.

    Reply
  5. escorte girlJanuary 24, 2013 ב 15:23

    Estoy muy agradecido por todo el mundo de la lectura informativa de blogs.microsoft.co.il. Yo, ciertamente, se extenderá la frase acerca de su sitio con la gente. Saludos.

    Reply
  6. BurksJanuary 27, 2013 ב 14:24

    “Maybe that’s all nonsense, who knows. They will definitely stop the hesitation and days of procrastination that have been preventing you from covering your smartphone. She was ‘discovered’ when Cond� Nast saved her from being run over in the street and offered her a job modeling for Vogue magazine.

    Reply