Simplified example of a tiled layer strategy using a map that lives inside a worker.
The tiles of the map in this example are rendered in a web worker, using OffscreenCanvas. This can be improved by using a pool of workers to enable parallel, multi-threaded rendering.
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import ImageTileSource from 'ol/source/ImageTile.js';
const worker = new Worker('./worker.js', {type: 'module'});
const tileQueue = [];
new Map({
layers: [
new TileLayer({
source: new ImageTileSource({
tileSize: 512,
loader: (z, x, y) => {
return new Promise((resolve) => {
const loadTile = () => {
const handleMessage = ({data: {action, imageData}}) => {
if (action !== 'rendered') {
return;
}
worker.removeEventListener('message', handleMessage);
resolve(imageData);
tileQueue.shift();
const loadNextTile = tileQueue[0];
loadNextTile?.();
};
worker.addEventListener('message', handleMessage);
worker.postMessage({action: 'render', tile: [z, x, y]});
};
if (tileQueue.length === 0) {
loadTile();
}
tileQueue.push(loadTile);
});
},
attributions: [
'<a href="https://www.openstreetmap.org/copyright" target="_blank">© OpenStreetMap contributors</a>',
],
}),
}),
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2,
}),
});
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Tiled Layer Rendering in an Offscreen Canvas</title>
<link rel="stylesheet" href="node_modules/ol/ol.css">
<style>
.map {
width: 100%;
height: 400px;
}
.map {
background: rgba(232, 230, 223, 1);
position: relative;
}
.map .ol-rotate {
left: .5em;
bottom: .5em;
top: auto;
right: auto;
}
</style>
</head>
<body>
<div id="map" class="map"></div>
<script type="module" src="main.js"></script>
</body>
</html>
import Map from 'ol/Map.js';
import View from 'ol/View.js';
import TileLayer from 'ol/layer/Tile.js';
import OSM from 'ol/source/OSM.js';
import {createXYZ} from 'ol/tilegrid.js';
const worker = self;
const tileGrid = createXYZ({
tileSize: [512, 512],
});
const canvas = new OffscreenCanvas(512, 512);
const map = new Map({
target: canvas,
layers: [
new TileLayer({
source: new OSM({
// No need to fade in tiles in the worker
transition: 0,
}),
}),
],
});
worker.addEventListener('message', async ({data: {action, tile}}) => {
if (action !== 'render') {
return;
}
const view = new View({
center: tileGrid.getTileCoordCenter(tile),
resolution: tileGrid.getResolution(tile[0]),
});
map.setView(view);
map.once('rendercomplete', () => {
const imageData = canvas.transferToImageBitmap();
worker.postMessage({action: 'rendered', imageData: imageData}, [imageData]);
});
});
{
"name": "tiled-layer-rendering-in-offscreen-canvas",
"dependencies": {
"ol": "10.9.0"
},
"devDependencies": {
"vite": "^3.2.3"
},
"scripts": {
"start": "vite",
"build": "vite build"
}
}