Slippy map tilenames
From OpenStreetMap
This article describes the file naming conventions for the Slippy Map application.
Contents |
Zoom levels
| 0 | 1 tile covers whole world |
| 1 | 2 × 2 tiles |
| 2 | 4 × 4 tiles |
| n | 2n × 2n tiles |
| 17 | Maximum zoom for Osmarender layer |
| 18 | Maximum zoom for Mapnik layer |
X and Y
- X goes from 0 (left edge is 180 °W) to 2zoom -1 (right edge is 180 °E)
- Y goes from 0 (top edge is 85.0511 °N) to 2zoom -1 (bottom edge is 85.0511 °S) in a Mercator projection
For the curious, the num 85.0511 is the result of arctan(sinh(π)). By using this bound, the entire map becomes a (very large) square. See also the Osmarender bug.
Projection
- Project the world as Mercator.
- Divide the equator into n equal units -- those are the x values
- Locate ±85.0511° on your projection -- those are the limits of the map in the Y axis
- Divide the map's Y axis into n equal units -- those are the y values starting from 0 at the top
For those who like pseudo-code, here's some hints:
xtile = (lon+180)/360 ytile = log(tan(lat) + sec(lat))
For perl a subroutine could be:
use Math::Trig;
sub getTileNumber {
my ($lat,$lon,$z) = @_;
my $xtile = int( ($lon+180)/360 *2**$z ) ;
my $ytile = int( (1 - log(tan($lat*pi/180) + sec($lat*pi/180))/pi)/2 *2**$z ) ;
return(($xtile, $ytile));
}
In Actionscript 1:
function long2tile(lon) { return (Math.floor((lon+180)/360*Math.pow(2,_root.scale))); }
function lat2tile(lat) { return (Math.floor((1-Math.log(Math.tan(lat*Math.PI/180) + 1/Math.cos(lat*Math.PI/180))/Math.PI)/2 *Math.pow(2,_root.scale))); }
For Java that is:
public class slippytest {
public static void main(String[] args) {
int zoom = 10;
double lat = 47.968056d;
double lon = 7.909167d;
System.out.println("http://tile.openstreetmap.org/" + getTileNumber(lat, lon, zoom) + ".png");
}
public static String getTileNumber(final double lat, final double lon, final int zoom) {
int xtile = (int)Math.floor( (lon + 180) / 360 * (1<<zoom) ) ;
int ytile = (int)Math.floor( (1 - Math.log(Math.tan(lat * Math.PI / 180) + 1 / Math.cos(lat * Math.PI / 180)) / Math.PI) / 2 * (1<<zoom) ) ;
return("" + zoom + "/" + xtile + "/" + ytile);
}
}
For VB.NET a subroutine could be:
Private Function CalcTileXY(ByVal lat As Single, ByVal lon As Single, ByVal zoom As Long) As Point CalcTileXY.X = CLng(Math.Floor((lon + 180) / 360 * 2 ^ zoom)) CalcTileXY.Y = CLng(Math.Floor((1 - Math.Log(Math.Tan(lat * Math.PI / 180) + 1 / Math.Cos(lat * Math.PI / 180)) / Math.PI) / 2 * 2 ^ zoom)) End Function
And the other direction in perl:
sub Project {
my ($X,$Y, $Zoom) = @_;
my $Unit = 1 / (2 ** $Zoom);
my $relY1 = $Y * $Unit;
my $relY2 = $relY1 + $Unit;
# note: $LimitY = ProjectF(degrees(atan(sinh(pi)))) = log(sinh(pi)+cosh(pi)) = pi
# note: degrees(atan(sinh(pi))) = 85.051128..
#my $LimitY = ProjectF(85.0511);
# so stay simple and more accurate
my $LimitY = pi;
my $RangeY = 2 * $LimitY;
$relY1 = $LimitY - $RangeY * $relY1;
$relY2 = $LimitY - $RangeY * $relY2;
my $Lat1 = ProjectMercToLat($relY1);
my $Lat2 = ProjectMercToLat($relY2);
$Unit = 360 / (2 ** $Zoom);
my $Long1 = -180 + $X * $Unit;
return(($Lat2, $Long1, $Lat1, $Long1 + $Unit)); # S,W,N,E
}
sub ProjectMercToLat($){
my $MercY = shift();
return( 180/pi* atan(sinh($MercY)));
}
sub ProjectF
{
my $Lat = shift;
$Lat = deg2rad($Lat);
my $Y = log(tan($Lat) + (1/cos($Lat)));
return($Y);
}
The other way in Actionscript 1:
function tile2long(t) {
return (t/Math.pow(2,_root.scale)*360-180);
}
function tile2lat(t) {
var n=Math.PI-2*Math.PI*t/Math.pow(2,_root.scale);
return (180/Math.PI*Math.atan(0.5*(Math.exp(n)-Math.exp(-n))));
}
Tiles
- Tiles are 256 × 256 pixel PNG files
- Each zoom level is a directory, each column is a subdirectory, and each tile in that column is a file
- Filename(url) format is /zoom/x/y.png
The slippy map expects tiles to be served up at URLs following this scheme, so all tile server URLs look pretty similar. For example:
| Mapnik tiles from the 'tile' server: | http://tile.openstreetmap.org/8/127/85.png |
| Osmarender/Tiles@Home on the 'dev' server: | http://tah.openstreetmap.org/Tiles/tile.php/8/127/85.png |
| Maplint tiles: | http://tah.openstreetmap.org/Tiles/maplint.php/8/127/85.png |
| User:Firefishy's proxy: | http://osm-tah-cache.firefishy.com/~ojw/Tiles/tile.php/8/127/85.png |
Subtiles
If you're looking at tile x,y and want to zoom in, the subtiles are (in the next zoom-level's coordinate system):
| 2x, 2y | 2x + 1, 2y |
| 2x, 2y + 1 | 2x + 1, 2y + 1 |
Similarly, zoom out by halving x and y (in the previous zoom level)
Tools
References
- http://www.econym.demon.co.uk/googlemaps/reference.htm
- http://cfis.savagexi.com/articles/2006/05/03/google-maps-deconstructed
- "Google Map" projection, see Spatialreference.org [1]
- OSM mailing list refering to this page.

