Atlaslog / Field Notes / Loading & display
Field note · Loading & display
WMTS TileMatrix starts at 0, so every tile is one level off
Quick answer
GeoServer names its WMTS levels EPSG:4326:0 through EPSG:4326:21, but level 0 is 1:559082264 and covers the globe with a 2x1 grid, not the single tile that XYZ expects. The client is therefore always one zoom level out.
Symptom
- At zoom 14 the overlay shows level-15 detail and its boundaries are coarser than the basemap.
- At the smallest zoom the overlay is blank, and the request returns a 200 with an empty PNG.
- The request uses TILEMATRIX=EPSG:4326:14 while Capabilities defines EPSG:4326:0 through EPSG:4326:21.
Checklist
- Open GetCapabilities and read each TileMatrix Identifier together with its ScaleDenominator.
- Compute the level-0 resolution as ScaleDenominator * 0.00028 m/px and compare it with the lon/lat grid.
- Map the frontend z values against the Capabilities identifiers and look for a constant one-level offset.
- Request TILEMATRIX=EPSG:4326:0 manually and check whether it returns a 2x1 grid at 0.703125 degrees per pixel.
- Inspect the tileGrid: OpenLayers createXYZ() assumes one tile at z=0.
Fix
Align the client grid with Capabilities: copy the resolutions array into an explicit WMTSTileGrid, or change GeoServer's TileMatrixSet start level to 1 so it matches the XYZ convention.
Code
<TileMatrix>
<ows:Identifier>EPSG:4326:0</ows:Identifier>
<ScaleDenominator>279541132.0143589</ScaleDenominator>
<TopLeftCorner>90 -180</TopLeftCorner>
<TileWidth>256</TileWidth>
<TileHeight>256</TileHeight>
<MatrixWidth>2</MatrixWidth>
<MatrixHeight>1</MatrixHeight>
</TileMatrix>const matrixIds = [];
for (let z = 0; z <= 21; z++) matrixIds[z] = 'EPSG:4326:' + z;
L.tileLayer('http://localhost:8080/geoserver/gwc/service/wmts?SERVICE=WMTS&REQUEST=GetTile' +
'&VERSION=1.0.0&LAYER=gis:districts&STYLE=&FORMAT=image/png' +
'&TILEMATRIXSET=EPSG:4326&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}', {
tileSize: 256,
tms: false,
maxZoom: 21,
minZoom: 0,
zoomOffset: 0
}).addTo(map);
Watch out
- Using ol.tilegrid.createXYZ() defaults with a WMTS source, which produces a silent one-level shift.
- Subtracting 1 from z in the URL only, while the tile grid still computes bounds for the wrong level.
English edition written for Atlaslog from the original Chinese field note. Code is unchanged and runs as-is.
Atlaslog (2026). WMTS TileMatrix starts at 0, so every tile is one level off. https://www.jingweizhi.com/en/cookbook/wmts-tilematrix-offset.html (accessed 2026-11-03).