In the previous parts for this series we have been adding various types of vector data to our Virtual Earth application. We will now move on and start adding raster data to overlay floor-plans or even add our own aerial images. Especially aerial or satellite images can be quite large - 2GB are not so seldom - so that we have to look at a way which is still efficient on the web. Virtual Earth supports this type of overlays directly by adding custom tile layers. What exactly does that mean and how does it work?

The Virtual Earth Tile System

Let's have a quick look at the Virtual Earth tile system first. Joe Schwarz has written an excellent article which is published on MSDN. The Virtual Earth tile system is structured in a quadtree. At level 1 the whole world is split into 4 tiles. Each of those has a size of 256 x 256 pixels and their names match the quadkey as shown below. If we zoom-in one more level, i.e. to level 2, we split each of these tiles into 4 tiles and number them as shown in the example below. This rule applies for every subsequent zoom-level. Thus we can say:

  1. The number of digits in the name of the tiles is equivalent to the zoom-level
  2. There is a direct relationship between the name of the tile and the geographic area it covers.


The Virtual Earth MapControl will load the tiles from the Virtual Earth tile servers and stitch them together. The tiles will be cached for up to 7 days in the local browser cache; that is of course only if the cache doesn't flow over and you don't have a policy which flushes the cache. If you return to the same location within these 7 days the MapControl will try to find the relevant tiles in the browser cache first before it contacts the Virtual Earth tile servers.

Adding Custom Tile Layers to Virtual Earth

To add a custom tile layer to Virtual Earth we use the method AddTileLayer from the VEMap-class. This function requires a VETileSourceSpecification as input as well as a boolean value which indicates if the tile layer should be visible. The VETileSource is specified by

  • a optional parameter for the bounding box which determines the region where we want to add the custom tile layer,
  • the minimum and maximum zoom-level where we want to show the layer. These parameters are optional.
  • the opacity which is a percentage value to set translucency
  • the optional parameter z-index which is only relevant if we want to add more than one layer and want to control which layer is on top.
  • the optional parameter for the number of servers which is only relevant if we want to store the tiles on more than one server
  • the TileSource. This is the most important parameter. It determines the virtual directory where your own custom tile source is stored and it allows you to set a number of additional parameters. For a full reference follow the link to the reference but the most important one for us is now %4. This parameter will make sure, that the VE MapControl determines the quadkeys of the tiles in the current MapView. It will then cycle through the virtual directory that we specified before and search for tiles with a matching name. These tiles will be retrieved and overlaid on the position of the original Virtual Earth tiles.
function GetTiles()
var bounds = [new VELatLongRectangle(new VELatLong(90,-180),new VELatLong(-90,180))];
var tileSourceSpec = new VETileSourceSpecification("MyTileLayer", "./%4.png");
tileSourceSpec.NumServers = 1;
tileSourceSpec.Bounds = bounds;
tileSourceSpec.MinZoomLevel = 1;
tileSourceSpec.MaxZoomLevel = 6
tileSourceSpec.Opacity = 1;
tileSourceSpec.ZIndex = 100;
map.AddTileLayer(tileSourceSpec, true);

Very well, that doesn't sound too bad but how do we create the tiles in the first place? An easy to use tool which is available for free download is the MapCruncher which has been developed by the bright guys from Microsoft Research.

Creating a Custom TileSource with MSR MapCruncher

The MSR MapCruncher allows you to load various image formats as well as PDF-documents into a workbench. Let's walk through the process to create tile layers for a floor-plan. In the left hand window you have the image and in the right hand window you have Virtual Earth itself. Now you can move significant points in the image below the cross-hair in the left window and move the map-reference below the cross-hair in the right window. For fine-tuning keep the CTRL-key pressed and move the map or image with the arrow-keys.


Once you have enough map-correspondence points defined you can lock the view and the MapCruncher will re-project the image in a way that it fits to Virtual Earth.


You can now define colors which you want to make transparent...


...and the maximum zoom-level you want to render. MapCruncher allows you to go down to zoom-level 33. Which would in Reading, UK mean a scale of 0.0135 mm/pixel. For more details on 'Understanding Scale and Resolution' see this article on MSDN. We're going only down to level 21 for our floor-plans which resolves to 5.5 cm/pixel.


Once we finished the first image we can add more images into this layer and more layers to the mashup.


When we are happy with our mashup, we can start rendering. We can do that optional in a local directory or directly to Amazon S3. Amazon Simple Storage Service or short Amazon S3 is a very performant and cost efficient way to store your data.


The MSR MapCruncher will now start to cut your image in pyramid-levels for each zoom-level from 1 to the maximum level you defined and it will mosaic each pyramid level. All images will have a size of 256 x 256 pixels which matches the size of the original Virtual Earth tiles and they also follow the Virtual Earth naming convention which - as mentioned above - indicates zoom-level and the geographic area which it covers.


The MapCruncher provides a preview-function so that you can immediately admire your result and it will also create a sample web-page which implements the custom tile layer.


Tip: MSR MapCruncher also support source images directly from URI as well as scripted rendering. With these to features you could retrieve frequently changing images like weather-radar images from the internet and set up a batch-job to update your tile source periodically.
For more information see the MapCruncher documentation.

Add the Custom Tile Layer to our Sample Application

If you stored your data in Amazon S3 you can use the Virtual Directory of your bucket to directly access the data. If you store the data in one of your own servers you have to create a virtual directory in your web server which points to this physical directory.

On our web site we first add another AccordionPane. In this AccordionPane we will have 5 HTML-controls of type checkbox - one for each layer that we add. As before we attach an onclick-event to the checkboxes. This time it will the JavaScript AddTileLayer that we execute and we will do that with a number of parameters again.

<ajaxToolkit:AccordionPane ID="paneTileLayer" runat="server">
<Header>Tile LayerHeader>
<b>Microsoft Reading (19-22)b><br />
<input id="cbTVP_FP" type="checkbox" onclick="AddTileLayer('cbTVP_FP','TVP_FP',51.46282101659089 ,-0.9280979633331389,51.46076220436288,-0.9235650300979454,'',19,22,1,100)" /><a href='javascript:ShowLocation(51.46160111471333, -0.9252655506134102, 19);'>TVP Level Pa><br />
<input id="cbTVP_FG" type="checkbox" onclick="AddTileLayer('cbTVP_FG','TVP_FG',51.46282101659089 ,-0.9280979633331389,51.46076220436288,-0.9235650300979454,'',19,22,1,110)" /><a href='javascript:ShowLocation(51.46160111471333, -0.9252655506134102, 19);'>TVP Level Ga><br />
<input id="cbTVP_F1" type="checkbox" onclick="AddTileLayer('cbTVP_F1','TVP_F1',51.46282101659089 ,-0.9280979633331389,51.46076220436288,-0.9235650300979454,'',19,22,1,120)" /><a href='javascript:ShowLocation(51.46160111471333, -0.9252655506134102, 19);'>TVP Level 1a><br />
<input id="cbTVP_F2" type="checkbox" onclick="AddTileLayer('cbTVP_F2','TVP_F2',51.46282101659089 ,-0.9280979633331389,51.46076220436288,-0.9235650300979454,'',19,22,1,130)" /><a href='javascript:ShowLocation(51.46160111471333, -0.9252655506134102, 19);'>TVP Level 2a><br />
<input id="cbTVP_F3" type="checkbox" onclick="AddTileLayer('cbTVP_F3','TVP_F3',51.46282101659089 ,-0.9280979633331389,51.46076220436288,-0.9235650300979454,'',19,22,1,140)" /><a href='javascript:ShowLocation(51.46160111471333, -0.9252655506134102, 19);'>TVP Level 3a><br /><br />

The parameters which we pass to the script are:

  • the name of the checkbox. We will use this to determine if the checkbox has been activated or deactivated.
  • the name of the layer we want to add or delete
  • the bounding box of the area where we want to add the layer
  • the minimum and maximum zoom-level where we want to see our custom layer
  • the URL to the virtual directory concatenated with the parameter %4.png
  • the percentage value for the opacity and
  • the z-index

If the checkbox has been deactivated we delete the relevant tile layer. Otherwise we define the VETileSourceSpecification and add the layer to the map.

function AddTileLayer(control, layer, maxlat, maxlon, minlat, minlon, url, minlvl, maxlvl, opac, zindex)
if (document.getElementById(control).checked == false) {
else {
var bounds = [new VELatLongRectangle(new VELatLong(maxlat, maxlon),new VELatLong(minlat, minlon))];
var tileSourceSpec = new VETileSourceSpecification(layer, url);
tileSourceSpec.Bounds = bounds;
tileSourceSpec.MinZoomLevel = minlvl;
tileSourceSpec.MaxZoomLevel = maxlvl;
tileSourceSpec.Opacity = opac;
tileSourceSpec.ZIndex = zindex;

That's it already. Let's have a look at our result:


You can have a look at the application here and as always the source code is available for download from this site.*

Click here to view the article.