Using a Service Worker to generate diagnostic XYZ tiles
Sometimes you’re developing a typical tile-based web map thing and need to sanity-check your vectors or imagery or whatevers.
In this situation, you might visually compare it to other vectors or imagery in your map. But what if you don’t have anything to compare to?
In this article, I present a quick-n-dirty “fake XYZ tile server” that you can drop into any web app, and use it to feed descriptive tiles to your map.
It looks like this:
Live action demo
The image above is from the example website here:
https://fake-xyz-tile-sw.joelmalone.com.au/
And the source code is available here:
https://github.com/joelmalone/fake-xyz-tile-sw.js
Yeh but why
The “fake XYZ tile server” is useful because:
- It will generate tile images that display the rendered X, Y and Z of the tile that was requested. Useful for debugging!
- It’s completely client-side. It’s just a service worker, so you can drop it into any web app.
- It doesn’t have any external dependencies.
- It can be consumed by any tile-based web map framework that supports URLs.
- It’s simple; you can easily tweak it to suit your needs.
This is what it looks like in action, with an OSM base map behind it to provide some context:
You could use this information to diagnose issues with your custom imagery or vector layers.
It’s even useful for continuing to work while offline, without a connection to your usual base map servers.
Yeh but how
The source code is reasonably simple, and easy to adapt. Simply drop this as a new .js file (e.g. serve-tiles-sw.js) into your web app:
/* serve-tiles-sw.js */function onFetch(event) {
const url = new URL(event.request.url)
if (url.host === 'fake-server') {
event.respondWith(
generateImage(url)
);
break;
}
}async function generateImage(url) {
const [, x, y, z] = url.pathname
.split('/')
.map(i => Number.parseInt(i)) const canvas = new OffscreenCanvas(256, 256);
var context = canvas.getContext("2d"); context.fillStyle = 'orange';
context.fillRect(8, 8, 240, 240); context.font = "20px Arial";
context.fillStyle = 'black';
context.fillText(`${x}, ${y}, ${z}`, 16, 128); const blob = await canvas.convertToBlob() return new Response(blob)
}self.addEventListener("fetch", onFetch);
And then call this (e.g. from index.html) to register the service worker:
navigator.serviceWorker.register(‘serve-tiles-sw.js’)
And then add a new XYZ layer to your map, using this as the URL, and adapting it as necessary to suit your map framework:
https://fake-server/{x}/{y}/{z}
Gotcha: WebKit doesn’t support OffscreenCanvas (or “oh right, I forgot we were doing web development”)
OffscreenCanvas is not supported on Safari iOS (nor Safari anywhere), as can be seen in the Browser Compatibility section for OffscreenCanvas on MDN:
This means that the above code won’t work in the following browsers:
- Safari on Mac (because it’s WebKit).
- Any browser on iOS (because they’re all WebKit).
Development on OffscreenCanvas in WebKit seems to have stalled around a year ago, so it doesn’t look like we’ll see it implemented any time soon.
A straight-forward alternative to OffscreenCanvas would be to find a package on NPM that allows us to draw and encode images in a pure Javascript way. But uhhhh maybe I’ll do that another day.