Bring your own Tile Provider to Virtual Earth, OpenStreetMap

The Virtual Earth JavaScript control has supported tile overlays for some time. A tile layer is set of 256px square images matching the Mercator projection of Virtual Earth. Tile layers are usually layered on top of Virtual Earth, the control supports transparency and an opacity of the entire layer. With the latest release of Version 6.2 of the control you can now disable the base tile layers allowing for greater performance when your tiles are all you need. Lets look at what is required to load OpenStreetMap tiles into Virtual Earth.

dgbg Developer



OpenStreetMap is a free editable map of the whole world. It is made by people like you. OpenStreetMap allows you to view, edit and use geographical data in a collaborative way from anywhere on Earth. OpenStreetMap’s hosting is kindly supported by the UCL VR Centre and bytemark.

I am very impressed with how far OSM has come in the last year and highly recommend it as an open project (unlike another major online mapping company that wants you to contribute but then own the data themselves). So lets look at just how simple it is to get OSM into Virtual Earth.

See the working page here: (View source for the full code sample)

The first thing we need to do for this example is to disable some of the core Virtual Earth functionality we don’t need here.

var map = null;
function loadmap() {
    map = new VEMap('myMap');
    map.onLoadMap = onloadmap;
    var mapOptions = new VEMapOptions();
    mapOptions.LoadBaseTiles = false;
    mapOptions.EnableBirdseye = false;
    mapOptions.EnableDashboardLabels = false;
    map.LoadMap(new VELatLong(-27.47, 153.03), 16, VEMapStyle.Hybrid, false, VEMapMode.Mode2D, true, 0, mapOptions);

We change to the tiny dashboard, and pass into the LoadMap() method the optional final parameter of VEMapOptions. In Version 6.2 this includes the ability to disable the base VE tiles using LoadBaseTiles = false and you can also disable Birdseye as I do here. I set the initial view of the map over Brisbane, Australia and hide the scalebar. These operations are not mandatory, in your application you may want to keep some of these features.

Next we subscribed to the onLoadMap event before we called VELoadMap. This event fires once the map has loaded, a perfect time to add our new tile layer.

function onloadmap() {
    var bounds = [new VELatLongRectangle(new VELatLong(90, -180), new VELatLong(-90, 180))];
    var tileSourceSpec = new VETileSourceSpecification("OSM", "", 1, bounds, 1, 18, getTilePath, 1.0, 100);
    map.AddTileLayer(tileSourceSpec, true);

To add a tile layer to Virtual Earth you have two options, either create your tiles in the same naming convention as Virtual Earth or use your own convention. We add the tile layer to VE using the VEMap.AddTileLayer method passing in a Tile spec and whether we want the layer immediately visible. The Tile spec, VETileSourceSpecification, has a bunch of properties for you to set:

Name Description


An array of VELatLongRectangle Class objects that specifies the approximate coverage area of the layer


The unique identifier for the layer. Each tile layer on a map must have a unique ID.


The location of the tiles.


The number of servers on which the tiles are hosted.


The minimum zoom level at which to display the custom tile source.


The maximum zoom level at which to display the custom tile source.


When viewing a map in 2D mode, the function that determines the correct file names for the tiles.


Specifies the opacity level of the tiles when displayed on the map.


Specifies the z-index for the tiles.

Most of these are straight forward but the two we will look at are TileSource and getTilePath. The TileSource is when you have a tile set that follows the conventions from Virtual Earth. You supply a URL with some special tokens to be replaced:

  • %1—this parameter specifies the map style to associate with your customer tiles. For example, if your tile file names begin with “r”, the custom tiles will only be displayed if the map style is set to “road”. Valid values are r, h, and a.
  • %2—if you are using more than one server for load-balancing, this parameter is used in conjunction with the VETileSourceSpecification.NumServers Property to cycle through the tile servers.
  • %4—this variable cycles through the file names.
  • An example would be: http://TileServer%2/MyTiles/%1%4.png

    This works in 2D and 3D modes with the exception of using multiple servers for load balancing that was removed from 3D in the last version. I hope this makes a come back soon.

    GetTilePath on the other hand allows for you to write your own JavaScript function to return the correct tile. This is where it gets interesting as many other tile providers out there also use the Mercator projection and 256px tiles, for example OpenStreetMap. OSM uses the following naming convention:

    http: //{z}/{x}/{y}.png

    Where z is the zoom level, and x and y are the index of the tile from the top left. This makes our implementation trival as the getTilePath function we defined is passed these exact values in a tileContext parameter:

    function getTilePath(tileContext) {
        return "" + tileContext.ZoomLevel + "/" + tileContext.XPos + "/" + tileContext.YPos + ".png";

    All we need to do is return the correct url by replacing the values as required. It couldn’t be more simple.

    View it online here: 

    So we get the 2D Virtual Earth control with only our custom tile layer. 3D mode will throw an exception as it will look for the TileSource property. We could of left the base Virtual Earth imagery on and overlaid OSM, even made it slightly opaque. An interesting modification is to leave the dashboard, hide the base tiles and put a switch statement in the getTilePath javascript function. The final property of the tileContext is MapStyle, we could use OSM for road data but fall back to Virtual Earth for Aerial Images.

    Let me know what you come up with.