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).
Impressive demo. But please don’t use this map for a real application, because it contains errors. The original data was based on population figures and doesn’t properly discriminate among different countries.
For instance, if you select Island, then Luxembourg, Andorra, Cyprus, Malta and the Faroe Islands are selected too (same purple color in original, but not related). And the area around Kaliningrad has the same color as Russia, but they’re not selected together.
PS : it’s fun to try to select Monaco, San Marino and Vatican City (obviously :-) ). You can enlarge the image, but even then it’s difficult.
Right, I forgot to add a disclaimer that I didn’t bother making the map 100% politically correct. E.g. I simply made all the tiny countries and a bunch of islands the same color (this is not the fault of original image since I didn’t keep the colors from there, I simply didn’t want to spend too much time on it).
EDIT: Maik got there before me. I should refresh before commenting next time I leave a blog post idle in a background tab!
Isn’t this what HTML image maps are for? Certainly for the “deciding which country got clicked” bit!
As I said, this is about complex areas on an image. Anything but rectangle click areas are almost impossible with image maps. This demo however allows you to click the area of a country – with 1 pixel precision.
Cool, this is a really nice solution!
Wow, what amazing progress vendors have made in the 15 years of the web. Now we can do image maps a whole different way!
Somebody needs to take a good hard look at Mozilla and figure out why so much resources gets channeled into so little genuine innovation.
Image maps? Not really, see above. Not to mention that the main part here is image manipulation – something that wasn’t possible on the web at all until recently.
I guess because:
1. everybody is wasting resources in things that don’t make any difference for the user (but maybe they make a difference for some big firm).
2. real innovation is much more difficult than reinventing the wheel.
It took a while to understand what you’re doing. “pixel-accurate client-side region selection” would be a better title!
What WYSIWYG easy HTML authoring tool did you use to base64-encode the embedded img data? ;-)
And if you look closely at that map you will notice that it is simplified – real contours of countries don’t translate well into vectors and bitmaps embedded in an SVG file can no longer be manipulated easily.
I used http://www.software.hixie.ch/utilities/cgi/data/data for base64 encoding, why? It wasn’t really necessary to do this but I preferred to keep everything in one file.
Not just image map? I guess not, that solution would require at least an onclick attribute to replace each section with a different coloured image!
Admittedly it would be a huge PITA to get all the images right … but then, oh wait, CSS sprites would do the trick in seconds.
I really want to believe in SVG. Show me something that only Flash can do atm – like vector tweening or something – in SVG and I might start to believe the bullshit about HTML5 kicking Flash and Silverlight. Really, which idiot came up with that story? And how the hell did it get through to the Slashdot front page?
Question: how many Mozilla devs actually write web sites? Seems to me the closest they come is explaining their amazing new (but irrelevant?) code in some pre-canned web logging software like WordPress. Don’t get me wrong Wladamir, AdBlock Plus is invaluable but wouldn’t your experience coding for it have more to do with Firefox internals?
Is it not simply possible that Mozilla devs are estranged from the needs of web developers because they do not write web sites?
PD, you must FIRST agree on what a Web site is.
Cool demo Wladimir! Too bad about the troll.
Wladimir, I agree that if you’re starting with an existing image that already has the countries all as different colors an imagemap would take more work… That starting point was not obvious to me from a first glance at your demo. I agree that it’s neat that you can exploit that starting point this easily.
I saw a couple examples of maps where users could color in visited countries of the world using a Google Maps interface, but the maps were too small and the country colors all the same. So me a newbie programmer wannabe, used old style image maps and static PNG images from Wikipedia to create large maps that by default use colors from the country flags that contrast with neighboring countries.
See here: http://wherehaveibeen.info
Anyway I can see now that using SVG or Raphael would have been much simpler. I like your example but it doesn’t seem to work in Chrome.
No, as far as I know Firefox is still the only browser to support SVG filters in HTML. But you can probably implement this in a cross-browser way if you use a standalone SVG “image”. Comment 9 above mentions a working example: http://files.myopera.com/orinoco/svg/WorldCountries.svg