Creating an image map from SVG

I was asked how I made the map in my examples earlier.

I wrote a small script to do it. (The script is quite limited — I only made it complete enough to handle the SVG files I was using. Others might break it. Also, it requires pyparsing… and hoo-boy is that slow.)

Example!

Wikipedia is good for this, and has provided me with the example file I’ll use, a map of the USA. If you have some GIS data already, I believe that ArcGIS 9.2 has native SVG support, or it looks like you can convert ESRI shapefiles with shp2svg.

My example file is filled with all sort of crud that isn’t a definition of state boundaries, though, so I need to get just that. Perusing it (in a text editor or a SVG editor like Inkscape) reveals that all the state borders are in a group named “States”. Helpful!

So I run my script: svg2imagemap.py Map_of_USA_with_state_names.svg 960 593 States

(The “960 593″ is the size of the image I’m creating from the SVG file.)

This creates an html file named [svg name].html, so Map_of_USA_with_state_names.html. It only contains the area tags, so I dump them into an image map in a page set up like the one in the other examples…

And we get: A map of the USA.

Just to disclaim again: That script is unlikely to be immediately useful for any particular SVG image. You would almost certainly need to tweak it significantly to make it work for your purposes. But it’s a good start, at least.

Comments (10)

  1. brahim wrote::

    Hi David (not the director),

    Great tutorial !

    What group name did you use for countries in the world wikipedia svg map ?

    Thanks in advance !

    Friday, March 28, 2008 at 11:17 am #
  2. David wrote::

    The world map was very complicated to do — part of the reason I chose the USA map for the example was that it didn’t involve getting bogged down in painful meddling in the internals of an SVG file.

    Firstly, the link on the world map demo page is going to the core map — I actually used this variant: http://en.wikipedia.org/wiki/Image:BlankMap-World6-Equirectangular.svg

    I edited the SVG file in Inkscape and removed a lot of the tiny islands. I also had to repeatedly change the scale of the image here, in order to make the output line up with the final image. This was very trial-and-error.

    Then I had to use an extremely long command line, because there is no single group id that includes all countries. Instead, each country has its two-letter ISO code as an id. So it looked something like:

    python svg2imagemap.py BlankMap-World6-Equirectangular.svg 1000 507 se id ye mg …[etc]… jp au th ve

    In retrospect it might have been easier to edit the SVG file to just remove everything that wasn’t a country.

    Finally, I hand-edited the output to include better titles than, say, “path5325″.

    Complicated and painful. In all honesty, if you need a world map imagemap, it would probably be a lot easier to just rescale the one used on my demo. (It’s far simpler to take the already-rewritten map and just scale all the coordinates than it is to deal with the SVG again.)

    Monday, March 31, 2008 at 12:21 pm #
  3. Paul McGuire wrote::

    David -

    I looked at your code a bit, I think you might be spending a lot of time in CaselessPreservingLiteral. Since you only use this class in Command, and all command args are single characters, could you try this?

    def Command(char):
    “”" Case insensitive but case preserving”"”
    # return CaselessPreservingLiteral(char)
    return oneOf([char.upper(),char.lower()])

    You might also get some speed boost from these Regex forms of floating and non-neg numbers:
    floatingPointConstant = Regex(r”[-+]?\d*\.?\d*([Ee][-+]?\d*)?”)
    nonnegativeNumber = Regex(r”\d*\.?\d*([Ee][-+]?\d*)?”)

    Otherwise, without some real in-depth profiling, I don’t see any likely places for improving performance.

    Nice utility!
    – Paul

    Monday, March 31, 2008 at 9:15 pm #
  4. Caranorn wrote::

    Hello David,

    I just found this post googling for a way to create an imagemap out of an SVG file. Unfortunately I have 0 programming skill. I have no clue how to run a python script (just downloaded python, but have found no way to run your script with the correct command line). Do you plan on creating an application (.exe) for this? Or could you explain to a dummy like me how to use your script?

    I’m working on a map (currently a large .psd file) of a Fantasy world which I’d like to turn into several navigable client side imagemaps (.png’s of world, continents, regions, countries, counties). From the low scale world map you could navigate to a large scale county map and get more details etc.

    Thursday, April 17, 2008 at 6:13 am #
  5. Caranorn wrote::

    Think I finally got it to work, was pretty simple too:-(. At least I have a test map that’s working.

    Thanks a lot for this script.

    Thursday, April 17, 2008 at 9:00 am #
  6. giovanni wrote::

    Hi, I’m tryng your python script, but I’m experiencing some problems.
    When I first run the script I got this error:
    C:\Python25>python.exe svg2imagemap.py Italia.svg 1000 1000 regioni
    Traceback (most recent call last):
    File “svg2imagemap.py”, line 49, in
    raw_width = float(svg.getAttribute(’width’))
    ValueError: invalid literal for float(): 553.423px

    I have a tryed editing line 49 e 50 from:

    raw_width = float(svg.getAttribute(’width’))

    to

    raw_width = float(svg.getAttribute(’width’).replace(’px’, ”))

    But now no errors are displayed, but the result file is 0 byte.

    If can be useful for debug the doctype of the file is:

    Any help appreciated, thank you

    Thursday, June 5, 2008 at 12:56 pm #
  7. muyi wrote::

    Hi,

    I tried using your svg2imagemap.py script to rescale the Wikipedia BlankMap-World6-Equirectangular.svg map, but the script doesn’t seem to work. Did you use it in your example? The width_ratio and height_ratio formulas don’t seem to do anything to my image, and the path coordinates come out way higher than the image size that I gave on the command line. I’m trying to get an image that is 630px wide.

    Thanks for any help / comments.

    Saturday, June 21, 2008 at 6:02 pm #
  8. Paul McGuire wrote::

    David -

    I noticed that your image map of the world contains many doubled coordinates, that is, consecutive coordinates that are the same x-y pair (probably due to float-to-integer roundoff).

    Here (http://pyparsing.pastebin.com/f6d3558d9) is a pyparsing script to post-process your generated image map - on your map of the world, it removes over 7700 duplicate coords. The maximum number of duplicates was 14 on line 108.

    – Paul

    Friday, September 26, 2008 at 5:57 am #
  9. Alpv wrote::

    Hello David,

    Great examples. FYI, you mispelled Ecuador (you wrote Equador).

    Sunday, October 19, 2008 at 5:56 am #
  10. Jiri Barton wrote::

    Interestingly, I want to do the same thing for my django-worldmap application - http://lurkingideas.net/worldmap/.

    Having spent the several hours looking for a solution, it seems the only way to do it is to parse the svg and draw the map again somehow - like you have done.

    Thanks, at least I know there is probably no other way :)

    Tuesday, October 21, 2008 at 2:24 pm #