Tiling schemes
Tiling schemes or tile matrix sets are used in tiled map services to index and reference fixed size map tiles in different zoom levels (map scales).
🗄️ Scalable coordinates
As already introduced in the chapter about
coordinates
scalable coordinates are coordinates associated with a level of detail (LOD)
or a zoom
level. They are used for example by tiling schemes to represent
pixels or tiles in tile matrices.
As a recap the Scalable2i
class represents projected x
, y
coordinates at
zoom
level, with all values as integers. The following sample represents
pixels.
// A pixel with a zoom level (or LOD = level of detail) coordinates. const pixel = Scalable2i(zoom: 9, x: 23, y: 10);
// Such coordinates can be scaled to other zoom levels. pixel.zoomIn(); // => Scalable2i(zoom: 10, x: 46, y: 20); pixel.zoomOut(); // => Scalable2i(zoom: 8, x: 11, y: 5); pixel.zoomTo(13); // => Scalable2i(zoom: 13, x: 368, y: 160));
Also references to tiles can be represented with similar objects. This shall be clarified on descriptions of two tiling schemes in this chapter. At first let’s see how the most common Web Mercator scheme is built.
🗺️ Web Mercator Quad

WebMercatorQuad
is a “Google Maps Compatible” tile matrix set with tiles
defined in the WGS 84 / Web Mercator projection (“EPSG:3857”).
Using WebMercatorQuad
involves following coordinates:
- position: geographic coordinates (longitude, latitude)
- world: a position projected to the pixel space of the map at level 0
- pixel: pixel coordinates (x, y) in the pixel space of the map at zoom
- tile: tile coordinates (x, y) in the tile matrix at zoom
See below how to calcalate between geographic positions, world coordinates, pixel coordinates and tile coordinates:
// "WebMercatorQuad" tile matrix set with 256 x 256 pixel tiles and with // "top-left" origin for the tile matrix and map pixel space const quad = WebMercatorQuad.epsg3857();
// source position as geographic coordinates const position = Geographic(lon: -0.0014, lat: 51.4778);
// get world, tile and pixel coordinates for a geographic position print(quad.positionToWorld(position)); // ~ x=127.999004 y=85.160341 print(quad.positionToTile(position, zoom: 2)); // zoom=2 x=1 y=1 print(quad.positionToPixel(position, zoom: 2)); // zoom=2 x=511 y=340 print(quad.positionToPixel(position, zoom: 4)); // zoom=4 x=2047 y=1362
// world coordinates can be instantiated as projected coordinates // x range: (0.0, 256.0) / y range: (0.0, 256.0) const world = Projected(x: 127.99900444444444, y: 85.16034098329446);
// from world coordinates to tile and pixel coordinates print(quad.worldToTile(world, zoom: 2)); // zoom=2 x=1 y=1 print(quad.worldToPixel(world, zoom: 2)); // zoom=2 x=511 y=340 print(quad.worldToPixel(world, zoom: 4)); // zoom=4 x=2047 y=1362
// tile and pixel coordinates with integer values can be defined too const tile = Scalable2i(zoom: 2, x: 1, y: 1); const pixel = Scalable2i(zoom: 2, x: 511, y: 340);
// tile and pixel coordinates can be zoomed (scaled to other level of details) print(pixel.zoomIn()); // zoom=3 x=1022 y=680 print(pixel.zoomOut()); // zoom=1 x=255 y=170
// get tile bounds and pixel position (accucy lost) as geographic coordinates print(quad.tileToBounds(tile)); // west: -90 south: 0 east: 0 north: 66.51326 print(quad.pixelToPosition(pixel)); // longitude: -0.17578 latitude: 51.50874
// world coordinates returns geographic positions still accurately print(quad.worldToPosition(world)); // longitude: -0.00140 latitude: 51.47780
// aligned points (world, pixel and position coordinates) inside tile or edges print(quad.tileToWorld(tile, align: Aligned.northWest)); print(quad.tileToPixel(tile, align: Aligned.center)); print(quad.tileToPosition(tile, align: Aligned.center)); print(quad.tileToPosition(tile, align: Aligned.southEast));
// get zoomed tile at the center of a source tile final centerOfTile2 = quad.tileToWorld(tile, align: Aligned.center); final tile7 = quad.worldToTile(centerOfTile2, zoom: 7); print('tile at zoom 2: $tile => center of tile: $centerOfTile2 ' '=> tile at zoom 7: $tile7');
// a quad key is a string identifier for tiles print(quad.tileToQuadKey(tile)); // "03" print(quad.quadKeyToTile('03')); // zoom=2 x=1 y=1 print(quad.quadKeyToTile('0321')); // zoom=4 x=5 y=6
// tile size and map bounds can be checked dynamically print(quad.tileSize); // 256 print(quad.mapBounds()); // ~ west: -180 south: -85.05 east: 180 north: 85.05
// matrix width and height tells number of tiles in a given zoom level print('${quad.matrixWidth(2)} x ${quad.matrixHeight(2)}'); // 4 x 4 print('${quad.matrixWidth(10)} x ${quad.matrixHeight(10)}'); // 1024 x 1024
// map width and height tells number of pixels in a given zoom level print('${quad.mapWidth(2)} x ${quad.mapHeight(2)}'); // 1024 x 1024 print('${quad.mapWidth(10)} x ${quad.mapHeight(10)}'); // 262144 x 262144
// ground resolutions and scale denominator for zoom level 10 at the Equator print(quad.tileGroundResolution(10)); // ~ 39135.76 (meters) print(quad.pixelGroundResolution(10)); // ~ 152.87 (meters) print(quad.scaleDenominator(10)); // ~ 545978.77
// inverse: zoom from ground resolution and scale denominator print(quad.zoomFromPixelGroundResolution(152.87)); // ~ 10.0 (double value) print(quad.zoomFromScaleDenominator(545978.77)); // ~ 10.0 (double value)
// ground resolutions and scale denominator for zoom level 10 at lat 51.4778 print(quad.pixelGroundResolutionAt(latitude: 51.4778, zoom: 10)); // ~ 95.21 print(quad.scaleDenominatorAt(latitude: 51.4778, zoom: 10)); // ~ 340045.31
// inverse: zoom from ground resolution and scale denominator at lat 51.4778 print( quad.zoomFromPixelGroundResolutionAt( latitude: 51.4778, resolution: 95.21, ), ); // ~ 10.0 (double value) print( quad.zoomFromScaleDenominatorAt( latitude: 51.4778, denominator: 340045.31, ), ); // ~ 10.0 (double value)
🌐 Global Geodetic Quad

GlobalGeodeticQuad
(or “World CRS84 Quad” for WGS 84) is a tile matrix set
with tiles defined in the Equirectangular Plate Carrée projection.
At the zoom level 0 the world is covered by two tiles (tile matrix width is 2 and matrix height is 1). The western tile (x=0, y=0) is for the negative longitudes and the eastern tile (x=1, y=0) for the positive longitudes.
// "World CRS 84" tile matrix set with 256 x 256 pixel tiles and with // "top-left" origin for the tile matrix and map pixel space const quad = GlobalGeodeticQuad.worldCrs84();
// source position as geographic coordinates const position = Geographic(lon: -0.0014, lat: 51.4778);
// get world, tile and pixel coordinates for a geographic position print(quad.positionToWorld(position)); // ~ x=255.998009 y=54.787129 print(quad.positionToTile(position, zoom: 2)); // zoom=2 x=3 y=0 print(quad.positionToPixel(position, zoom: 2)); // zoom=2 x=1023 y=219 print(quad.positionToPixel(position, zoom: 4)); // zoom=4 x=4095 y=876
// world coordinates can be instantiated as projected coordinates // x range: (0.0, 512.0) / y range: (0.0, 256.0) const world = Projected(x: 255.99800888888888, y: 54.78712888888889);
// from world coordinates to tile and pixel coordinates print(quad.worldToTile(world, zoom: 2)); // zoom=2 x=3 y=0 print(quad.worldToPixel(world, zoom: 2)); // zoom=2 x=1023 y=219 print(quad.worldToPixel(world, zoom: 4)); // zoom=4 x=4095 y=876
// tile and pixel coordinates with integer values can be defined too const tile = Scalable2i(zoom: 2, x: 3, y: 0); const pixel = Scalable2i(zoom: 2, x: 1023, y: 219);
// get tile bounds and pixel position (accucy lost) as geographic coordinates print(quad.tileToBounds(tile)); // west: -45 south: 45 east: 0 north: 90 print(quad.pixelToPosition(pixel)); // longitude: -0.08789 latitude: 51.41602
// world coordinates returns geographic positions still accurately print(quad.worldToPosition(world)); // longitude: -0.00140 latitude: 51.4778
// tile size and map bounds can be checked dynamically print(quad.tileSize); // 256 print(quad.mapBounds()); // west: -180 south: -90 east: 180 north: 90
// matrix width and height tells number of tiles in a given zoom level print('${quad.matrixWidth(2)} x ${quad.matrixHeight(2)}'); // 8 x 4 print('${quad.matrixWidth(10)} x ${quad.matrixHeight(10)}'); // 2048 x 1024
// map width and height tells number of pixels in a given zoom level print('${quad.mapWidth(2)} x ${quad.mapHeight(2)}'); // 2048 x 1024 print('${quad.mapWidth(10)} x ${quad.mapHeight(10)}'); // 524288 x 262144
// arc resolutions and scale denominator for zoom level 10 at the Equator print(quad.tileArcResolution(10)); // ~ 0.175781 (° degrees) print(quad.pixelArcResolution(10)); // ~ 0.000686646 (° degrees) print(quad.scaleDenominator(10)); // ~ 272989.39
// inverse: zoom from scale denominator at the Equator print(quad.zoomFromScaleDenominator(272989.39)); // ~ 10.0 (double value)