Understanding Tile-Based Mapping Systems
A tile-based mapping system stitches to gether small image tiles to display a complete map. Each tile is typically 256x256 pixels, and tiles are fetched from a server using a URL with parameters like x, y, and z. For example, a tile URL might look like: http://mt2.google.cn/vt/lyrs=m@167000000&hl=zh-CN&gl=cn&x=420&y=193&z=9&s=Galil.
Key URL parameters include:
- x: The horizontal tile index, starting at 0 from the leftmost tile and increasing rightward.
- y: The vertical tile index, starting at 0 from the topmost tile and increasing downward.
- z: The zoom level, where 0 is the most zoomed-out view, increasing for more detailed levels.
Maps use the Web Mercator projection, which transforms the Earth's surface into a square plane. The map bounds in meters are [-20037508.3427892, -20037508.3427892, 20037508.3427892, 20037508.3427892], with the center at (0,0). Each zoom level n has a resolution calculated as:
resolution = 20037508.3427892 * 2 / 256 / (2^n)
This resolution represents the geographic length per pixel at that level.
Given a map center point, zoom level, and viewport size, the visible bounds can be computed:
leftBound = centerX - resolution * viewportWidth / 2
bottomBound = centerY - resolution * viewportHeight / 2
rightBound = centerX + resolution * viewportWidth / 2
topBound = centerY + resolution * viewportHeight / 2
At zoom level n, the world map is divided into 4^n tiles, each 256x256 pixels. For example, level 0 uses 1 tile, level 1 uses 4 tiles, and so on.
To render tiles, calculate the indices of tiles within the viewport. Let mapBounds be the full map bounds, viewBounds the visible bounds, res the resolution, and tileSize as 256. The tile indices are:
startTileX = floor((viewBounds.left - mapBounds.left) / res / tileSize)
startTileY = floor((viewBounds.top - mapBounds.top) / res / tileSize)
endTileX = floor((viewBounds.right - mapBounds.right) / res / tileSize)
endTileY = floor((viewBounds.bottom - mapBounds.bottom) / res / tileSize)
The first tile URL is constructed with x=startTileX, y=startTileY, z=zoomLevel. Tiles between the start and end indices are fetched by iterating over x and y.
To position tiles on the screen, compute the geographic coordinates of the top-left corner of the starting tile:
tileCornerX = mapBounds.left + (startTileX * tileSize * res)
tileCornerY = mapBounds.top - (startTileY * tileSize * res)
Then, calculate the pixel offsets from the viewport's top-left corner:
offsetX = (viewBounds.left - tileCornerX) / res
offsetY = (tileCornerY - viewBounds.top) / res
The starting tile is placed at coordinates (-offsetX, -offsetY) relative to the viewport origin. Subsequent tile are positioned by adding multiples of tileSize to these offsets.
This algorithm applies to tile-based maps using Web Mercator projection, such as those from Google, Bing, and similar services.