I’ve been using Leaflet to display raster images lately.
What I would like to do for a particular project is be able to zoom in to an image so the pixels become magnified on the screen in a sharply delineated way, such as you would get when zooming in to an image in Photoshop or the like. I would also like to retain, at some zoom level before maximum, a 1:1 correspondence between image pixel and screen pixel.
I tried going beyond maxNativeZoom as described here and here, which works but the interpolation results in pixel blurring.
I thought of an alternative which is to make the source image much larger using ‘nearest neighbour’ interpolation to expand each pixel into a larger square: when zoomed to maxNativeZoom the squares then look like sharply magnified pixels even though they aren’t.
Problems with this are:
- image size and tile count get out of hand quickly (original image is 4096 x 4096)
- you never get the ‘pop’ of a 1:1 correspondence between image pixel and screen pixel
I have thought about using two tile sets: the first from the original image up to it’s maxNativeZoom, and then the larger ‘nearest neighbour’ interpolated image past that, following something like this.
But, this is more complex, doesn’t avoid the problem of large tile count, and just seems inelegant.
So:
- Can Leaflet do what I need it to and if so how?
- If not can you point me in the right direction to something that can (for example, it would be interesting to know how this is achieved)?
Many thanks
2
Answers
One approach is to leverage the
image-rendering
CSS property. This can hint the browser to use nearest-neighbour interpolation on<img>
elements, such as Leaflet map tiles.e.g.:
See a working demo. Beware of incomplete browser support.
A more complicated approach (but one that works across more browsers) is to leverage WebGL; in particular Leaflet.TileLayer.GL.
This involves some internal changes to Leaflet.TileLayer.GL to support a per-tile uniform, most critically setting the uniform value to the tile coordinate in each tile render…
…having a
L.TileLayer
that "displays" a non-overzoomed tile for overzoomed tile coordinates (instead of just skipping the non-existent tiles)…… plus a fragment shader that rounds down texel coordinates prior to texel fetches (plus a tile-coordinate-modulo-dependant offset), to actually perform the nearest-neighbour oversampling…
…all tied together in an instance of
L.TileLayer.GL
(which syncs some numbers for the uniforms around):You can see everything working together in this demo.