It seems that all WMTS service that were suspicious in my question, were right; I was wrong.
World of CRS definitions is not so simple.
Actual axis order to be used while constructing TopLeftCorner, results from CRS definition.
It has been explained e.g. at http://www.geotoolkit.org/modules/referencing/faq.html#axisOrder.
For CRS like EPSG:3857, EPSG:32633, axis order is "natural", i.e. horizontal one first, then vertical one.
For CRS like geodetic EPSG:4326, and projected EPSG:2180, EPSG:3006, axis order is "reversed", i.e. vertical one first, then horizontal one.
Despite this fact, coordinates in BoundingBox and WGS84BoundingBox are always listed in "natural" succession.
Actual order of axes can be detected by examining appropriate part of WKT representation of given CRS containing AXIS tags, e.g.:
- EPSG:3857: AXIS["X",EAST],AXIS["Y",NORTH]
- EPSG:32633: AXIS["Easting",EAST],AXIS["Northing",NORTH]
EAST first, then NORTH - no swapping required
- EPSG:2180: AXIS["x",NORTH],AXIS["y",EAST]
- EPSG:3006: AXIS["x",NORTH],AXIS["y",EAST]
NORTH first, then EAST - swapping is required
For geodetic CRS, swapping from lat, lon order to lon (horizontal), lat (vertical) is always required.
Or maybe there are other geodetic CRS that use lon, lat order? There are no AXIS tags in WKT for EPSG:4326.
Other links in this matter:
Axis Order Policy Guidance
Posts containing 'axis order' - GIS Stack Exchange
I think the issue you had was because Lantmäteriet is using SWEREF 99/EPSG:3006 projection and Leaflet is using EPSG:3857. My solution was to use proj4leaflet to transform between those projections, like this:
import L from 'leaflet';
import { CRS } from 'proj4leaflet';
const apiKey = process.env.LANTMATERIET_TOKEN;
// This is the important task, where we set the map projection to EPSG:3006
const crs = new L.Proj.CRS('EPSG:3006',
'+proj=utm +zone=33 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs',
{
resolutions: [
4096, 2048, 1024, 512, 256, 128, 64, 32, 16, 8
],
origin: [-1200000.000000, 8500000.000000 ],
bounds: L.bounds( [-1200000.000000, 8500000.000000], [4305696.000000, 2994304.000000])
});
const map = new L.Map('map', {
crs: crs,
continuousWorld: true,
});
new L.TileLayer(`https://api.lantmateriet.se/open/topowebb-ccby/v1/wmts/token/${apiKey}/?service=wmts&request=GetTile&version=1.0.0&layer=topowebb&style=default&tilematrixset=3006&tilematrix={z}&tilerow={y}&tilecol={x}&format=image%2Fpng`, {
maxZoom: 9,
minZoom: 0,
continuousWorld: true,
attribution: '© <a href="https://www.lantmateriet.se/en/">Lantmäteriet</a> Topografisk Webbkarta Visning, CCB',
}).addTo(map);
You can see my full example at https://github.com/kontrollanten/lantmateriet-leaflet
Best Answer
The problem was with the res-parameter. I had to calculate the scales based on the GetCapabilities. Here is a good guide how to do it properly https://lists.osgeo.org/pipermail/mapproxy/2013-August/001674.html
The final grid definition was