Converting TMS Tile Coordinates to Google/Bing/OSM Tile Coordinates

In addition to referencing tiles by quadkey, Bing Maps also refers to tiles by x, y, and z coordinates. This is the way you reference tiles when overriding the GetUri() method of the Silverlight Microsoft.Maps.MapControl.TileSource class, for example, and the x, y, z properties of a tile are passed to the uriConstructor function specified in the options for an AJAX v7 Microsoft.Maps.TileSource.

In the x, y, z tile identification system:

  • z is the zoom level of the grid
  • x and y are the column and row number at which this tile should be placed, measured from an origin at the top left of the map.

Thus, at zoom level 1, you can describe the four tiles required to make a complete Bing Map using x, y, z coordinates as follows:

image

Bing Maps is not entirely unusual in this respect – this exact same referencing system is used by Google Maps, Open Street Maps, ESRI, and many others. You’d therefore be forgiven for thinking that it was some sort of standard.

In fact, this tile numbering sounds a lot like (and is often incorrectly described as) a  TMS index. The Tile Map Service (TMS) Specification defined by the Open Source Geospatial Foundation is a standard for serving map tiles that, like the system described above, places tiles on a grid and refers to their position using x, y, and z coordinates, where z is the zoom level, and x and y refer to column and row positions. However, according to the TMS specifications, “The x-coordinate of the tile numbers increases with the x-coordinate of the spatial reference system, and the y-coordinate of the tile numbers also increases with the y-coordinate of the spatial reference system.”. In other words, tile (0, 0), at any zoom level should always be placed at the bottom left of the map, not the top left.

Using the TMS system, the tile indexes at zoom level 1 become as follows:

image

The problem is that, since these systems are identical in almost every other respect, people tend to assume that the system used by Google/Bing/OSM et al. is the standard and systems that output tiles using it sometimes mistakenly refer to it as the TMS format. Then,  every now and again, you come across a piece of software or service that genuinely outputs tiles numbered according to the TMS standard and, if you don’t correct the tile origin, your tiles all end upside-down on the wrong side of the world :(

Fortunately, it’s a very simple correction to make. At any given map zoom level, zoom, you can invert the y index of a tile from TMS to Google/Bing as follows:

var ymax = 1 << zoom;
var y = ymax - y - 1;

Assuming that you have a set of TMS tiles stored in a subdirectory structure that follows the pattern /z/y/x.png (as described in the Tile Resources section of the OSGeo TMS standard), then here’s a full example showing how to add a tilelayer of TMS tiles to Bing Maps v7:

<!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">
  var map = null;
  function GetMap() {
    // Create a basic map
    map = new Microsoft.Maps.Map(document.getElementById("mapDiv"),
      { credentials: "ENTERYOURBINGMAPSKEY",
        center: new Microsoft.Maps.Location(52.6, 1.26),
        zoom: 12
      });

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

    // 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 getTMSTilePath(tile) {

    var x = tile.x;
    var z = tile.levelOfDetail;
    // Invert tile y origin from top to bottom of map
    var ymax = 1 << z;
    var y = ymax - tile.y - 1;

    return location.href.substring(0, location.href.lastIndexOf('/')) + "/" + z + "/" + x + "/" + y + ".png";
  }
</script>
</head>
<body onload="GetMap();">
  <div id='mapDiv' style="position:relative; width:1024px; height:768px;"></div>
</body>
</html>

As a final note, just to add to the confusion, the Web Map Tile Service (WMTS) recently published by the OGC provides an alternative standard to TMS, but, like Google/Bing, uses an origin at the top-left hand corner.  For a discussion of the differences between TMS and WMTS, see http://2010.foss4g.org/presentations/3653.pdf

About these ads
This entry was posted in Bing Maps, Spatial and tagged , . Bookmark the permalink.

6 Responses to Converting TMS Tile Coordinates to Google/Bing/OSM Tile Coordinates

  1. rbrundritt says:

    Awesome, just saved me a lot of work!

  2. Mike says:

    Wondering where you got the tile parameter in the getTMSTilePath function. Where does that value come from?

    • alastaira says:

      It’s not documented in the API, but if you open up veapicore.js and look at the getUriConstructor method, you’ll see that Bing Maps sends a tile object parameter to any custom tile handler function, which has properties x and y to represent the row and column at which that tile should be placed and a levelOfDetail representing its zoom.

  3. James Sun says:

    Thanks for you very helpful article. Before I stumbled across your blog post, I wasn’t able to wrap my head around the difference in indexing between Google Maps and TMS.

  4. Jan says:

    This is the key for using AS3 ModestMaps and MapTiler, thank you very much!

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s