Huge images on small devices
Some background; a couple of weeks ago the Gothenburg City Planning Office released a result of several years research. A high res aerial footage was given an overlay of historical and future construction plans to get a grip of how the city could have looked and how it might look if all plans are realized.
For city planning geeks like me, it was a real piece of art.
Problem was that it was released in form of a 130 mb big .jpeg image. Besides with eating bandwidth while downloading, unpacking a .jpeg of this size almost totally froze my computer (i7 with 16 gb ram). Needless to say, i didn’t test on my phone.
So, we have this beautiful image, but we are unable to zoom in, pan and fully enjoy it, and in most cases are not able to even see it.
What to do, what to do?
As this is probably not the worlds only huge high res jpeg gets me to think that someone had solved this problem before. But how?
I have done a lot of work with client side map tools, and one of my favorites is leaflet. So i tried googling “big jpeg in leaflet”, and found out that you could do just that perfectly fine.
If you have a big jpeg.
My image was not just big, it was huge. Next move.
So i want to avoid downloading the whole image, but instead just the bounds i want to see with the right resolution for said bounds.
Sounds like tiles, right?
So, what about some googling for “create tiles from image for leaflet”?
Bingo:
So, if you have a big jpeg that you want to chop down in tiles this is the tool for you.
Installed and ran, it chugged along for a minut and produced about 6000 256px x 256px .png:s, neatly stacked in a pyramid folder structure.
This pyramid structure is something that i never actually reflected over but is pretty obvious how it works when you see it on your own hard drive.
Most js-mapping librarys request images in a pattern like:
// Leaflet.js
L.tileLayer('http://{s}.tile.osm.org/{z}/{x}/{y}.png', { attribution: '© <a href="http://osm.org/copyright">OpenStreetMap</a> contributors' }).addTo(map)
where
{z}/{x}/{y}.png
is the path to request the images. z is zoomlevel, x and y is 2d-coordinates.
So, if you zoom in to zoom level 5, the images is now fetched from folder
{5}/{x}/{y}.png
in top level folder 5 there are a number of ‘x’-folders, named e.g. 1–22, and in each of these folders there are about equal number of images named after ‘y’-coordinates. Neat.
Then i just made a simple leaflet page with raster-coords plugin, added leaflet.permalink for a better sharing ux, tested locally and uploaded this and all 6000 images in their folders to my server. Uploading took a while…
The index.js looks something like this:
The final application is here:
The complete code (except images, and the python script) are available here:
Pro tip
There is at least one more tweak that you can do for even better performance; run gdal2tiles-leaflet to produce a folder of the images in the more performant .webP-format, then in the app detect support for this on load and serve those instead of the .png:s.