Since everybody is talking about Firefox 3.5 demos these days I though that I would dig up one that I created myself in November. It allows selecting areas of complex shape on an image — e.g. countries on a map. This idea didn’t end up being used for anything but somebody else might find it useful.
Ten years ago I already had to solve this problem. How do you present the user with a map and let him choose a country? Back then I ended up using Win32 API and two bitmaps — one to display to the user and a second invisible bitmap to let the application translate clicks into actual countries by checking the color corresponding to the click position. The visible bitmap was static meaning that it wasn’t possible to show the selected country on the map. But that wasn’t necessary anyway back then. And now I had to solve the same problem, this time for the Mozilla platform.
My idea was: wouldn’t it be possible to use only one image and apply some transformations to it? I would like to encode each country with a different color on the image and transform these colors as necessary. Turns out, this is possible with the feComponentTransfer SVG filter, particularly by using discrete translation functions. Let’s give an example:
<feComponentTransfer> <feFuncR type="discrete" tableValues="1 0.75 0.5 0"/> <feFuncG type="discrete" tableValues="1 0 1 0"/> <feFuncB type="discrete" tableValues="0 1 0 1"/> </feComponentTransfer>
feFuncR defines the translation for the red color channel. Since I put four values in there, the entire red color space will be divided into four equal parts. Red values from 0 to 63 will be translated into 255. Red values from 64 to 127 will be translated into 191. And so on, the value in the table defines the color value after transformation (using 0 for “no red”, 1 for “maximal red value” and values in between for all red color gradations).
Similarly, feFuncG and feFuncB define the translations for the green and blue colors. If you use different shades of gray you will hit the same column for all color channels and you will get exactly the color back that is encoded in this column. E.g. #000000 will be translated into #FFFF00 (corresponds with 1,1,0 in the first column), #404040 will be translated into #BF00FF (corresponds with 0.75,0,1 in the second column) and so on. That’s what I use in my demo — all countries are encoded with different shades of gray, each hitting one of the 64 columns in the translation table. And the best of it: tableValues attributes can be changed dynamically which allows me to change the color assigned to each country at will. In this demo I assign the color #006666 (0,0.4,0.4) to countries that are not selected and #66CC66 (0.4,0.8,0.4) to countries that are selected.
But how to determine which map the user clicked on to select? Here I use a hidden canvas element that I copy the original image into. When the user clicks somewhere on the map I get the pixel with the same position from the canvas. From its color I can determine which column in tableValues attribute to change.
If you want to see the untransformed grayscale image, simply right-click the map in the demo and choose “View image”. Or view the demo in a browser that doesn’t support SVG filters for HTML elements (any browser but Firefox 3.5 right now).