openlayers

Migrate from deprecated XYZ with tileUrlFunction to TileImage with tileLoadFunction


The latest Openlayers documentation states that tileUrlFunction is deprecated in XYZ source. I am trying to migrate to TileImage with tileLoadFunction, but nothing is showing.

The old version is working:

Number.prototype.mod = function (n) {
    return ((this%n)+n)%n;
};

var mylayer = new TileLayer({
    source: new XYZ({
        tileUrlFunction: function (tileCoord, pixelRatio, projection) {
            console.log("tileUrlFunction invoked");
            if (tileCoord === null) {
                return undefined;
            }

            var z = tileCoord[0];
            var x = tileCoord[1];
            var y = tileCoord[2];
            var zfactor = Math.pow(2, z);
            const url = "https://tile.openstreetmap.org/" + z + "/" + x.mod(zfactor) + "/" + y.mod(zfactor) + ".png";
            return url;
        },
        tileSize: 256,
        minZoom: 0,
        maxZoom: 19,
        attributions: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
    })
});

This is not working (tileLoadFunction is never invoked):

Number.prototype.mod = function (n) {
    return ((this%n)+n)%n;
};

var mylayer = new TileLayer({
    source: new TileImage({
        tileGrid: createXYZ({
            tileSize: 256, // Standard tile size for OpenStreetMap
            minZoom: 0,
            maxZoom: 19, // OpenStreetMap supports zoom levels 0-19
        }),
        tileLoadFunction: function (imageTile, src) {
            console.log("tileLoadFunction invoked");
            const tileCoord = imageTile.getTileCoord();
            if (!tileCoord) {
                console.log("Tile coordinate is null");
                imageTile.getImage().src = ''; // Handle undefined tile coordinate
                return;
            }

            const z = tileCoord[0];
            const x = tileCoord[1];
            const y = tileCoord[2];
            const zfactor = Math.pow(2, z);
            const url = "https://tile.openstreetmap.org/" + z + "/" + x.mod(zfactor) + "/" + y.mod(zfactor) + ".png";

            console.log("Loading URL for coordinates - " + z + "," + x + "," + y + " --> " + url);
            imageTile.getImage().src = url;
        },
        attributions: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
    }),
});

JSFiddle: https://jsfiddle.net/yertp50n/


Solution

  • The docs suggest using an ImageTile, not TileImage source.

    An ImageTile source uses a loader similar to a DataTile source (see https://openlayers.org/en/latest/examples/pmtiles-elevation.html) not a tileLoadFunction:

    function loadImage(src) {
      return new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = 'anonymous';
        img.addEventListener('load', () => resolve(img));
        img.addEventListener('error', () => reject(new Error('load failed')));
        img.src = src;
      });
    }
    
    var mylayer = new TileLayer({
        source: new ImageTile({
            tileGrid: createXYZ({
                tileSize: 256, // Standard tile size for OpenStreetMap
                minZoom: 0,
                maxZoom: 19, // OpenStreetMap supports zoom levels 0-19
            }),
            loader: async function (z, x, y) {
                console.log("loader invoked");
                const zfactor = Math.pow(2, z);
                const url = "https://tile.openstreetmap.org/" + z + "/" + x % zfactor + "/" + y + ".png";
                const image = await loadImage(url);
                return image;
            },
            attributions: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap contributors</a>'
        }),
    });
    

    https://jsfiddle.net/yt93g5za/1/