Hillshading using the Alpha Channel of an Image

From OpenStreetMap Wiki
Jump to: navigation, search

The usual process of doing hillshading with a slippy map is creating bitmapped tiles that have the hillshading information in them as a greyscale or height-colored image, and also the roads etc. in the same tile.

There is another way to do it though. Suppose we have a "flat" tile that has the roads and everything, but no hillshading. Then, we have another tile for the same area with only the hillshading. The crucial difference is that instead of using the greyscale value for this information, this tile has the hillshading in the alpha channel of a completely black image. So you have an all-black image that is a bit transparent in places.

Now, a web browser loads both the flat and the alpha-hillshading tile, and without needing to set additional transparency on any of those tiles, it multiplies the pixels of those two tiles together. The visual result is the same as when loading a single tile that has it all "baked in".


This approach has the advantage that you can hillshade any flat tiles of different styles, and you can just switch it off if not needed.

Different to the road data, the hillshading information is pretty much static over time (after all, the earth doesn't change much), so the CPU time for calculating the shading just needs to be sent once and can be re-used many times later.

Those alpha-hillshading tiles can be paletted PNGs which have a pretty small file size. The flat tiles on the other hand don't need additional colors (that would be required for hillshading them in place), so they can be color-reduced very well and thus also have a small file size. The downside is that the browser has to load twice as many tiles, but this might be negligible.


Technical Process

  • prepare hillshaded GeoTIFFs and a Mapnik style sheet according to Hillshading with Mapnik
  • render them with Mapnik and tile them (e.g. with generate_tiles.py)
  • invert all tiles (see #Links for scripting)
  • copy the greyscale value to the alpha channel for each tile
  • (convert the RGBA PNGs to paletted PNGs)
  • success!

Example in pure Python/PIL (courtesy Nop):

from PIL import Image as PImage
from PIL import ImageOps

dim = 256
shaded = PImage.open("shade.png")

gray = ImageOps.grayscale(shaded)
neg = ImageOps.invert(gray)

black = PImage.new('RGBA', (dim, dim))
bands = neg.split()
black.putalpha(bands[0])

black.save("final.png", "png")

Acknowledgements

  • http://mapy.cz uses this alpha-hillshading approach
  • Marcin Rudowski discovered how they do it and provided ideas for the technical process
  • the guys in #mapnik offered suggestions and help


Links

Personal tools
Namespaces

Variants
Actions
site
Toolbox