Tile Mode

Author:Paul Ramsey
Contact:pramsey at cleverelephant.ca
Revision:$Revision$
Date:$Date$
Last Updated:2008/04/30

Introduction

MapServer can feed tile-based map clients directly using the CGI “tile mode”. Tile-based map clients work by dividing the map of the world up into a discrete number of zoom levels, each partitioned into a number of identically sized “tiles”. Instead of accessing a map by requesting a bounding box, a tile client builds a map by accessing individual tiles.

Configuration

Tile requests are handled by the ‘mapserv’ CGI program. In order to return tiles in the correct projection, MapServer must be built with the –use-proj option turned on. You can check if your version of ‘mapserv’ has projection support by running it with the ‘-v’ option and looking for ‘SUPPORTS=PROJ’.

Example 1. On Unix:

$ ./mapserv -v
MapServer version 4.6.1 OUTPUT=GIF OUTPUT=PNG OUTPUT=JPEG OUTPUT=WBMP OUTPUT=PDF
OUTPUT=SWF OUTPUT=SVG SUPPORTS=PROJ SUPPORTS=FREETYPE SUPPORTS=WMS_SERVER
SUPPORTS=WMS_CLIENT SUPPORTS=WFS_SERVER SUPPORTS=WFS_CLIENT SUPPORTS=WCS_SERVER
INPUT=JPEG INPUT=POSTGIS INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE DEBUG=MSDEBUG

Example 2. On Windows:

C:\apache\cgi-bin> mapserv -v
MapServer version 4.6.1 OUTPUT=GIF OUTPUT=PNG OUTPUT=JPEG OUTPUT=WBMP OUTPUT=PDF
OUTPUT=SWF OUTPUT=SVG SUPPORTS=PROJ SUPPORTS=FREETYPE SUPPORTS=WMS_SERVER
SUPPORTS=WMS_CLIENT SUPPORTS=WFS_SERVER SUPPORTS=WFS_CLIENT SUPPORTS=WCS_SERVER
INPUT=JPEG INPUT=POSTGIS INPUT=OGR INPUT=GDAL INPUT=SHAPEFILE DEBUG=MSDEBUG

MapServer requires that each LAYER in your map file have a valid PROJECTION block to support reprojection. Because the tile mode uses reprojection, you will have to ensure each LAYER has a valid PROJECTION block.

Configuration checklist:

  • MapServer compiled with PROJ support
  • Map file with a PROJECTION defined for every LAYER

As of MapServer 6.0, there are two extra parameters available for configuring tile mode.

  • tile_map_edge_buffer renders the tile into a buffered rendering frame, then clips out the final tile. This will reduce edge effects when large symbols or wide lines are drawn. Recommended value: the size of the largest symbol or line width in your map file.
  • tile_metatile_level renders the tiles into a fixed metatile, then clips out the final tile. This will reduce label repetition, at the expense of much higher rendering cost. Recommended value: 1 if you are doing labelling of large features in your layer. 0 otherwise.

If you use both tile_map_edge_buffer and tile_metatile_level at the same time, the buffer will be applied at the meta-tile level.

Utilization

The MapServer tile support adds three new directives to the CGI interface:

  • mode=tile tells the server to generate tiles based on the other tile mode parameters
  • tilemode=gmap tells the server use the Google Maps tile scheme for the tiles
  • tile=x+y+z tells the server what tile you want to retrieve, using the Google Maps tile addressing system
  • tilemode=ve tells the server use the Virtual Earth tile naming scheme for the tiles
  • tile=10231 tells the server what tile you want to retrieve, using the Virtual Earth tile addressing system

About Spherical Mercator

Spherical Mercator (also called “web mercator” by some) is a world projection. All the major tile-based map interfaces (Google Maps, Microsoft Virtual Earth, Yahoo Maps, OpenLayers) use the spherical mercator system to address tiles.

A spherical mercator set of tiles has the following properties:

  • The map has been reprojected to mercator using a spherical mercator algorithm
  • There is one tile in the top zoom level, zoom level zero
  • Each successive zoom level (z) has 2^z tiles along each axis
  • Tiles are 256x256 in size

Google Maps and Virtual Earth both use spherical mercator as their underlying tile projection, but use different formats to address the individual tiles.

Google Maps uses an “x”, “y”, “zoom” format. The zoom indicates which level to pull tiles from, and the “x” and “y” indicate while tile in that zoom level to pull.

Virtual Earth uses a single string to address each tile. The top zoom level in Virtual Earth has four tiles (equivalent to Google’s zoom level 1). The top left tile in the Virtual Earth top zoom level is addressed as “0”, top right as “1”, bottom left as “2” and bottom right as “3”. Each tile the next level is addressed by first referencing the top level tile that contains it, then its address relative to that tile. So the top left tile in the second zoom level is “00” and the bottom right one is “33”. See the Virtual Earth site for more details: http://msdn.microsoft.com/en-us/library/bb545006.aspx

Using Google Maps

The Google Maps API includes support for using alternative tile sets as overlays, or as alternate base maps. Here is an example of an GTileLayerOverlay

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google/MapServer Tile Example</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=[YOUR KEY HERE]"
        type="text/javascript"></script>
<script type="text/javascript">

function load() {
  if (GBrowserIsCompatible()) {
    var urlTemplate = 'http://localhost/cgi-bin/mapserv?';
        urlTemplate += 'map=/var/map.map&';
        urlTemplate += 'layers=layer1 layer2&';
        urlTemplate += 'mode=tile&';
        urlTemplate += 'tilemode=gmap&';
        urlTemplate += 'tile={X}+{Y}+{Z}';
    var myLayer = new GTileLayer(null,0,18,{
                                 tileUrlTemplate:urlTemplate,
                                 isPng:true,
                                 opacity:1.0 });
    var map = new GMap2(document.getElementById("map"));
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.setCenter(new GLatLng(35.35, -80.55), 15);
    map.addOverlay(new GTileLayerOverlay(myLayer));
  }
}

</script>
</head>
<body onload="load()" onunload="GUnload()">
  <div id="map" style="width: 500px; height: 500px"></div>
</body>
</html>

Note the format of the tileUrlTemplate: a valid URL, with {X}, {Y} and {Z} substitution tokens that Google Maps will replace with the tile coordinates and zoom level on the fly to retrieve tiles from your server.

You can also use a MapServer tile layer as an alternate base map:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<!DOCTYPE html
  PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN"
  "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="content-type" content="text/html; charset=utf-8"/>
<title>Google/MapServer Tile Example</title>
<script src="http://maps.google.com/maps?file=api&v=2&key=[YOUR KEY HERE]"
        type="text/javascript"></script>
<script type="text/javascript">

function load() {
  if (GBrowserIsCompatible()) {
    var urlTemplate = 'http://localhost/cgi-bin/mapserv?';
        urlTemplate += 'map=/var/map.map&';
        urlTemplate += 'layers=layer1 layer2&';
        urlTemplate += 'mode=tile&';
        urlTemplate += 'tilemode=gmap&';
        urlTemplate += 'tile={X}+{Y}+{Z}';
    var myLayer = new GTileLayer(null,0,18,{
                                 tileUrlTemplate:urlTemplate,
                                 isPng:true,
                                 opacity:0.3 });
    var map = new GMap2(document.getElementById("map"));
    map.addControl(new GLargeMapControl());
    map.addControl(new GMapTypeControl());
    map.setCenter(new GLatLng(35.35, -80.55), 15);
    var myMapType = new GMapType([myLayer], new GMercatorProjection(18), 'MapServer');
    map.addMapType(myMapType);
  }
}

</script>
</head>
<body onload="load()" onunload="GUnload()">
  <div id="map" style="width: 500px; height: 500px"></div>
</body>
</html>

The only change from the previous example is that we don’t create a GTileLayerOverlay, we create a GMapType, and use addMapType(), instead of addOverlay().

Using Virtual Earth

The Virtual Earth API also includes support for using alternative tile sets as overlays, or as alternate base maps. Here is an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8"/>
  <title>Virtual Earth Example</title>
  <script type="text/javascript" src="http://dev.virtualearth.net/mapcontrol/mapcontrol.ashx?v=6.1"></script>
  <script type="text/javascript">

    var map = null;

    function OnLoadMap () {
      map = new VEMap("myMap");
      map.LoadMap();

      var url = "http://localhost/cgi-bin/mapserv?";
      url += "map=/var/map.map&";
      url += "mode=tile&";
      url += "layers=layer1 layer2&";
      url += "tilemode=ve&";
      url += "tile=%4";

      var tileSourceSpec = new VETileSourceSpecification( "myLayer", url );
      tileSourceSpec.Opacity = 0.3;
      map.AddTileLayer(tileSourceSpec, true);
    }

  </script>
</head>
<body onload="OnLoadMap();">
  <div id="myMap" style="position:relative; width:500px; height:500px;"></div>
</body>
</html>