Google Maps for Windows Phone 7 using the Bing Maps Control
As I write this, we are less than 25 hours away from the moment the first Windows Phone 7 devices are released into the wild – in the Netherlands, that is. The marketplace is filling up nicely already. Some things are pretty obviously missing. And you wonder why, because they would be quite easy to make.
I hereby dare Google to publish a Google Maps application in the Windows Phone 7 Marketplace. Why? Because if they don’t do it, someone else probably will, and probably pretty fast, too. See the screenshot to the left. I assure you – this is no fake. What you see here is the Google Maps satellite layer with the Street layer on top of it, with a 40% opacity, showing a piece of the city center of Amersfoort, Netherlands. It took me about 90 lines of code and 18 lines of XAML.
Setting up a basic Bing Map control is easy as cake. You find the grid “Contentpanel”, and you plonk a Map in it, preferably using Blend:
<Microsoft_Phone_Controls_Maps:Map x:Name="map" CredentialsProvider="your_credentials" Mode="Road"> </Microsoft_Phone_Controls_Maps:Map>
You can change “Road” into “Aerial” and then you have Bing Satellite imagery. What’s less known is that you can actually attach your own tile layers to it. A tile is an image of 256x256 defined by it’s X and Y position in the grid forming the map of the world on a specific zoom level. All you have to to is write a little class that descends from Microsoft.Phone.Controls.Maps.TileSource in which you only have to override the following method:
Uri GetUri(int x, int y, int zoomLevel)
in which you tell which x/y/zoomlevel combination translates to which URI. For Google Maps, this turns out to be pretty easy. All information needed for can be found in the Deep Earth source code – a wee bit adapted for for the Bing Map Control. First, I made an enum that defines all the layers that Google provides
namespace LocalJoost.TileSource
{
public enum GoogleTileSourceType
{
Street,
Hybrid,
Satellite,
Physical,
PhysicalHybrid,
StreetOverlay,
WaterOverlay
}
}
Then, the actual tile calculating class:
using System;
namespace LocalJoost.TileSource
{
public class GoogleTileSource : Microsoft.Phone.Controls.Maps.TileSource
{
public GoogleTileSource()
{
UriFormat = @"http://mt{0}.google.com/vt/lyrs={1}&z={2}&x={3}&y={4}";
TileSourceType = GoogleTileSourceType.Street;
}
private int _servernr;
private char _mapMode;
private int Server
{
get
{
return _servernr = (_servernr + 1) % 4;
}
}
private GoogleTileSourceType _tileSourceType;
public GoogleTileSourceType TileSourceType
{
get { return _tileSourceType; }
set
{
_tileSourceType = value;
_mapMode = TypeToMapMode(value);
}
}
public override Uri GetUri(int x, int y, int zoomLevel)
{
{
if (zoomLevel > 0)
{
var url = string.Format(UriFormat, Server, _mapMode, zoomLevel, x, y);
return new Uri(url);
}
}
return null;
}
private static char TypeToMapMode(GoogleTileSourceType tileSourceType)
{
switch (tileSourceType)
{
case GoogleTileSourceType.Hybrid:
return 'y';
case GoogleTileSourceType.Satellite:
return 's';
case GoogleTileSourceType.Street:
return 'm';
case GoogleTileSourceType.Physical:
return 't';
case GoogleTileSourceType.PhysicalHybrid:
return 'p';
case GoogleTileSourceType.StreetOverlay:
return 'h';
case GoogleTileSourceType.WaterOverlay:
return 'r';
} return ' ';
}
}
}
As you can see, this is not quite rocket science. To make the Bing Maps control use this, you need to expand your XAML a little. First, you have to add two namespaces to your MainPage.Xaml:
xmlns:MSPCMCore="clr-namespace:Microsoft.Phone.Controls.Maps.Core;assembly=Microsoft.Phone.Controls.Maps" xmlns:LJTileSources="clr-namespace:LocalJoost.TileSource;assembly=LocalJoost.TileSource"
Then, you have to use a third mode for the Bing Map – Mercator. This basically only tells the map to operate in Mercator mode, but not to attach any default imagery. And here we go:
<Microsoft_Phone_Controls_Maps:Map x:Name="map" CredentialsProvider="your credentials" > <Microsoft_Phone_Controls_Maps:Map.Mode> <MSPCMCore:MercatorMode></MSPCMCore:MercatorMode> </Microsoft_Phone_Controls_Maps:Map.Mode> <Microsoft_Phone_Controls_Maps:Map.Children> <Microsoft_Phone_Controls_Maps:MapTileLayer> <Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources> <LJTileSources:GoogleTileSource TileSourceType="Satellite"/> </Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources> </Microsoft_Phone_Controls_Maps:MapTileLayer> </Microsoft_Phone_Controls_Maps:Map.Children> </Microsoft_Phone_Controls_Maps:Map>Presto. Google Maps for Windows Phone 7. And oh, if you want to project another layer on top of it, for instance Google Streets, like I did, you just add more tilelayers:
<Microsoft_Phone_Controls_Maps:Map x:Name="map" CredentialsProvider="your credentials" > <Microsoft_Phone_Controls_Maps:Map.Mode> <MSPCMCore:MercatorMode></MSPCMCore:MercatorMode> </Microsoft_Phone_Controls_Maps:Map.Mode> <Microsoft_Phone_Controls_Maps:Map.Children> <Microsoft_Phone_Controls_Maps:MapTileLayer> <Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources> <LJTileSources:GoogleTileSource TileSourceType="Satellite"/> </Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources> </Microsoft_Phone_Controls_Maps:MapTileLayer> <Microsoft_Phone_Controls_Maps:MapTileLayer> <Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources> <LJTileSources:GoogleTileSource TileSourceType="Street"/> </Microsoft_Phone_Controls_Maps:MapTileLayer.TileSources> </Microsoft_Phone_Controls_Maps:MapTileLayer> </Microsoft_Phone_Controls_Maps:Map.Children> </Microsoft_Phone_Controls_Maps:Map>
The Bing Maps control makes you able to zoom and pan trough all this. Okay, Google Maps does a little more than this, but this shows pretty well how darn easy it is to make a good looking, pretty fully functional mapping application showing totally different imagery.
Conclusion: using the Bing Maps Control for showing raster maps is so bloody easy that I almost start to wonder if it’s still justified to feel proud ‘being a GIS guy’. Almost ;-)