Open Street Map and ESRI tiles on Bing Maps

In a previous post, I explained how to replace the base tile layer in the Bing Maps Silverlight control with an ESRI tile layer. In this post, I’ll show how to do the same but using the Bing Maps AJAX v7 control. You can use this technique to use the Bing Maps AJAX control, but replace the Bing imagery with OSM tiles or the ESRI tile layers used in the previous Silverlight post, as well as many other tile sources.

The first step is to specify not to load the default Bing Maps tile layer. Do this by specifying the mercator MapTypeId in the options passed to the constructor when you first initialise the map:

[php]mapTypeId: Microsoft.Maps.MapTypeId.mercator[/php]

The next step is to create a new TileSource. The uriConstructor of the TileSource must return the correct URI for a requested tile. If your tile provider names its tiles according to the default Bing Maps quadkey numbering system, then the uriConstructor can be a simple string using the {quadkey} placeholder. This will be replaced with the appropriate quadkey when the tile is requested:

[php]var tileSource = new Microsoft.Maps.TileSource(
{  uriConstructor: ‘http://www.microsoft.com/maps/isdk/ajax/layers/lidar/{quadkey}.png’ }
);[/php]

However, for OSM tiles, or any other tile providers that not follow the basic quadkey numbering system, we instead need a function to construct the appropriate URI for each tile. I’ll do this in a function called getTilePath, and I’ll specify this in the TileSource uriConstructor as follows:

[php]var tileSource = new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });[/php]

The getTilePath function will return a string with the appropriate URI for the requested tile. For OSM tiles, a typical tile looks like http://tile.openstreetmap.org/zoom/x/y.png. The function to create this tile is therefore:

[php]function getTilePath(tile) {
return "http://tile.openstreetmap.org/" + tile.levelOfDetail + "/" + tile.x + "/" + tile.y + ".png";
}[/php]

Put this all together and your code should look like this:

[php]<!DOCTYPE HTML PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html  xmlns="http://www.w3.org/1999/xhtml">
<head>
<title></title>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<script type="text/javascript" src="http://ecn.dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=7.0"></script>
<script type="text/javascript">
function GetMap() {
// Create a basic map
var map = new Microsoft.Maps.Map(document.getElementById("mapDiv"),
{ credentials: "YOURBINGMAPSKEYHERE",
center: new Microsoft.Maps.Location(56, 2),
zoom: 5,
// Don’t load the Bing base map tiles
mapTypeId: Microsoft.Maps.MapTypeId.mercator
});

// Create the tile source
var tileSource = new Microsoft.Maps.TileSource({ uriConstructor: getTilePath });

// Construct the layer using the tile source
var tilelayer = new Microsoft.Maps.TileLayer({ mercator: tileSource, opacity: 1 });

// Push the tile layer to the map
map.entities.push(tilelayer);
}

function getTilePath(tile) {
// Construct the URI path for an OSM tile based on tile zoom/x/y
return "http://tile.openstreetmap.org/" + tile.levelOfDetail + "/" + tile.x + "/" + tile.y + ".png";
}
</script>
</head>
<body onload="GetMap();">
<div id=’mapDiv’ style="position:relative; width:640px; height:480px;"></div>
</body>
</html>[/php]


And here’s what it looks like:

image

If you want to try some other tile providers, replace the URI constructed by the getTilePath() function with some of the following:

DeLorme World Basemap

[php]function getTilePath(tile) {
return "http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/"
+ tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}[/php]

image

ESRI World Imagery

[php]function getTilePath(tile) {
return "http://services.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/"
+ tile.levelOfDetail + "/" + tile.y + "/" + tile.x;
}[/php]

image

Using ESRI Base Map Tile Layers in Bing Maps

I received an email this morning from the ESRI announcements mailing list stating that “ArcGIS Online basemaps published and hosted by Esri are now freely available to all users regardless of commercial, noncommercial, internal, or external use.”.

This is a nice surprise – it’s always useful to have a greater choice of tile styles on which to build your map, and ESRI have a nice selection of alternative map types. So, I decided to test them out by creating some ESRI tile layers in the Bing Maps Silverlight control.

The basic method of creating a new tilelayer using the Bing Maps Silverlight control is to define a new custom class in your .xaml.cs code file that inherits from the Microsoft.Maps.MapControl.TileSource class. You set the base of this class to represent the URL template of the tiles that will be used to build this layer. Then, by overriding the GetUri() method of this class, you insert the parameters corresponding to the x, y, and zoomlevel of each requested tile image from this tilelayer. For example, the following code demonstrates how to construct a class based on the ESRI topo world map tiles:

public class ESRITileSource : Microsoft.Maps.MapControl.TileSource
{
 public ESRITileSource() : base("http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{0}/{1}/{2}")
 { }

public override Uri GetUri(int x, int y, int zoomLevel)
 {
 return new Uri(String.Format(this.UriFormat, zoomLevel, y, x));
 }
}

Then, in your .xaml file, hide the default Bing Maps base tiles by specifying the MercatorMode of the map, and create a new tilelayer based on the custom tilelayer class instead:

<m:Map x:Name=”Map1″ Grid.Row=”1″ Grid.Column=”1″ Center=”55,-2″ ZoomLevel=”6″ CredentialsProvider=”{StaticResource MyCredentials}” HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch”>
<!– Do Not Display Bing Maps base layer –>
<m:Map.Mode>
<mcore:MercatorMode></mcore:MercatorMode>
</m:Map.Mode>
<!– Add ESRI Tile Layer –>
<m:Map.Children>
<m:MapTileLayer>
<m:MapTileLayer.TileSources>
<local:ESRITileSource></local:ESRITileSource>
</m:MapTileLayer.TileSources>
</m:MapTileLayer>
</m:Map.Children>
</m:Map>

<m:Map x:Name=”Map1″ Grid.Row=”1″ Grid.Column=”1″ Center=”55,-2″ ZoomLevel=”6″ CredentialsProvider=”{StaticResource MyCredentials}” HorizontalAlignment=”Stretch” VerticalAlignment=”Stretch”> <!– Do Not Display Bing Maps base layer –> <m:Map.Mode> <mcore:MercatorMode></mcore:MercatorMode> </m:Map.Mode> <!– Add ESRI Tile Layer –> <m:Map.Children> <m:MapTileLayer> <m:MapTileLayer.TileSources> <local:ESRITileSource></local:ESRITileSource>

 </m:MapTileLayer.TileSources> </m:MapTileLayer> </m:Map.Children></m:Map>

Using this method, I tried out a few of the different ESRI styles, as shown below:

World Topography

URL Template: http://services.arcgisonline.com/ArcGIS/rest/services/World_Topo_Map/MapServer/tile/{0}/{1}/{2}

image

World Shaded Relief

URL Template: http://services.arcgisonline.com/ArcGIS/rest/services/World_Shaded_Relief/MapServer/tile/{0}/{1}/{2}

image

DeLorme World Basemap

URL Template: http://server.arcgisonline.com/ArcGIS/rest/services/Specialty/DeLorme_World_Base_Map/MapServer/tile/{0}/{1}/{2}

image

World Physical Map

URL Template: http://services.arcgisonline.com/ArcGIS/rest/services/World_Physical_Map/MapServer/tile/{0}/{1}/{2}

image

There’s plenty more map styles, including demographic maps (US only), specialist maritime maps etc. – see http://www.esri.com/software/arcgis/arcgisonline/standard-maps.html for more details.