javascriptleafletgisproj4js

Vertical alignment of TMS tiles in Leaflet using EPSG:25832 projection


I am using Leaflet with Proj4Leaflet to work with tiles in 25832. The application is fairly simple: I am trying to overlay tiles in EPSG:25832 onto a omniscale basemap. I have copied the individual resolutions and origin from the tilemap meta information. The problem I am facing is that the map is not aligned and once I zoom in the tiles are not placed in the correct order. I'd appreciate any kind of support here (by the way, this is a working example which is using openlayers).

I guess I am doing something wrong here:

// Set resolutions
var resolutions = [156367.7919628329,78183.89598141646,39091.94799070823,19545.973995354114,9772.986997677057,4886.4934988385285,2443.2467494192642,1221.6233747096321,610.8116873548161,305.40584367740803,152.70292183870401,76.35146091935201,38.175730459676004,19.087865229838002,9.543932614919001,4.7719663074595005,2.3859831537297502,1.1929915768648751];

// Define CRS
var rs25832 = new L.Proj.CRS(
    'EPSG:25832',
    proj4rs25832def, 
    {
        origin: [ 273211.2532533697, 6111822.37943825 ],
        resolutions: resolutions
    }
);

...using the tiles information from https://mapproxy.bba.atenekom.eu/tms/1.0.0/privat_alle_50_mbit/germany .

Afterwards I add a tile layer

var url = 'https://mapproxy.bba.atenekom.eu/tms/1.0.0/privat_alle_50_mbit/germany/{z}/{x}/{y}.png';  

var tileLayer = L.tileLayer(
    url, 
    {
        tms: true,
        crs: rs25832,
        continuousWorld: true,
        maxZoom: resolutions.length
    }
);

And add them to the map..

// Setup map
var map = L.map('map', {
    crs: rs25832,
    center: [ 50.8805, 7.3389 ],
    zoom:5,
    maxZoom: resolutions.length,
    layers: [ baseWms, tileLayer ]
});

The bare minimum of code can be found here: https://jsfiddle.net/6gcam7w5/8/

incorrect alignment


Solution

  • This boils down to how the Y coordinate of TMS tiles is inverted (it becomes higher when going north, as opposed to default TileLayers, in which the Y coordinate becomes larger when going south).

    Having a look on the Leaflet code that takes care of this specific feature will shed some light on the issue:

        if (this._map && !this._map.options.crs.infinite) {
            var invertedY = this._globalTileRange.max.y - coords.y;
            if (this.options.tms) {
                data['y'] = invertedY;
            }
            data['-y'] = invertedY;
        }
    

    There are two things critical to calculating the right Y coordinate for your tiles here:

    Long story short, your CRS should be defined with known bounds. For this particular case, taking information from the TMS capabilities document...

    <BoundingBox minx="273211.2532533697" miny="5200000.0" maxx="961083.6232988155" maxy="6111822.37943825"/>
    

    ...and turned into a L.Bounds definition when defining the Leaflet CRS, like...

    // Define CRS
    var rs25832 = new L.Proj.CRS(
        'EPSG:25832',
        proj4rs25832def, 
        {
            origin: [ 273211.2532533697, 6111822.37943825 ],
            resolutions: resolutions,
            bounds: [[273211.2532533697, 5200000],[961083.6232988155, 6111822.37943825]]
        }
    );
    

    Stuff should just work (with no need to pass the CRS to the tilelayers, since they will all use the map's), as in this working example.