Using RaphaelJS To Create A Map
You may have seen one or two tutorials around on this subject, but I was recently asked to make a Map of the UK using RaphaelJS and thought I would make my own tutorial going over how I did it. Hopefully it will help you avoid the same problems I had when making it.
The brief was actually quite simple. Create an interactive map of the United Kingdom split into regions that when hovered would show a small pop-up containing the name of the region, and when clicked would take you to a specific page. In this case it was to be a term archive page for a specific taxonomy inside WordPress, but it could really be anything (within reason).
I have received permission from the client I did the work for to use the map in this tutorial so here is a demo to show you what the finished product looks like.

Happy? Good. Now let’s start with what could be the most important part of this tutorial.
Prerequisites
You need to grab yourself an image of the map, item, or whatever it is you want to create with RaphaelJS. Not just any image though, it must be an SVG or a Scalable Vector Graphics file. For those that haven’t heard of an SVG before, it is basically an image that is created using Mathematical equations meaning it can be scaled to any size without degradation since all the computer has to do is recalculate the results to the equations.
This can be exceptionally confusing to some so here is a visual example of how vector graphics, like SVG, differ from raster graphics, like JPG or Bitmap.

As you can see, when you scale up a raster image you start to lose detail & the image starts to degrade quite badly. However the vector image stays extremely detailed and doesn’t degrade at all.
I can’t share the image I used to make the map, but there are a lot of SVG maps & other SVG images available from Stock Photo sites such as IStockPhoto.com.
HTML
Now we can move on to some actual code. First the HTML used.
<div id="map"> </div>
Ahem. Yep, that’s it. No other HTML code is needed. The rest is all RaphaelJS.
RaphaelJS
First thing, we need to make sure jQuery is attached to the page. RaphaelJS doesn’t need jQuery, but it does help a lot to have the jQuery core functions available when using Raphael. I always use the Google CDN but you can attach it by any method you like. You also, obviously, need RaphaelJS attached to your page. You can download it from the RaphaelJS website. You will also need a small patch/plugin for RaphaelJS that creates a Pop-Up using vector graphics. I can’t remember where I found this but here is the code:
Raphael.fn.popup=function(x,y,text,size){txtattr={font:"12px Helvetica, Arial, sans-serif"}
size=size||5;text=text||"";var res=this.set(),d=3;res.push(this.path().attr({fill:"#333333",stroke:"#333333"}));res.push(this.text(x,y,text).attr(txtattr).attr({fill:"#fff","font-family":"Helvetica, Arial"}));res.update=function(X,Y,WIDTH){X=X||x;Y=Y||y;var mmax=Math.max,mmin=Math.min,bb=this[1].getBBox(),w=bb.width/2,h=bb.height/2,dir=(X-bb.width<15)?3:1,X=(dir==1)?X:X+WIDTH,dx=[0,w+size*2,0,-w-size*2],dy=[-h*2-size*3,-h-size,0,-h-size],p=["M",X-dx[dir],Y-dy[dir],"l",-size,(dir==2)*-size,-mmax(w-size,0),0,"a",size,size,0,0,1,-size,-size,"l",0,-mmax(h-size,0),(dir==3)*-size,-size,(dir==3)*size,-size,0,-mmax(h-size,0),"a",size,size,0,0,1,size,-size,"l",mmax(w-size,0),0,size,!dir*-size,size,!dir*size,mmax(w-size,0),0,"a",size,size,0,0,1,size,size,"l",0,mmax(h-size,0),(dir==1)*size,size,(dir==1)*-size,size,0,mmax(h-size,0),"a",size,size,0,0,1,-size,size,"l",-mmax(w-size,0),0,"z"].join(","),xy=[{x:X,y:Y+size*2+h},{x:X-size*2-w,y:Y},{x:X,y:Y-size*2-h},{x:X+size*2+w,y:Y}][dir];xy.path=p;this.attr(xy);return this;};return res.update(x,y);};
It has been minified, all you need to do is paste it onto the bottom of your RaphaelJS file that you downloaded from the RaphaelJS website, or you can put it in another JS file and attach it separately. I leave it to you.
Okay. First we need to make a path file. This file will contain the path data to create our map. Where do you get this data from? Well your just open up the SVG file in your favorite text editor (I use Notepad++). SVG files are actually just XML files, since the SVG file format is XML based. If the image has had each part grouped together when it was created you should be able to easily find the different parts you need. Here is an example of how my SVG file looked.

The highlighted areas are the important parts. The first is the name of group, this helps you identify which part of the image this code draws. The second highlighted part is the path data which is what you are looking for. You will need to copy the information between the d="". Remember though that you need the data (d=”") from every path element inside that group to create that group. This may involve joining lots of path elements together. To do that just join them together & leave a single space between each one.
Now let’s get to what you need to do with that code. The easiest way to build a large image such as a map is to store the path data in a separate file as an object.
So create a file called path.js and place this inside:
var paths = {
}
This is the start of your path data file. You should have your first piece of path data in clipboard, so let’s use it.
var paths = {
item: {
path: "Paste Path Data Here"
},
item2: {
path: "Paste Second Path Data Here"
}
}
So in our example here item would use the path data from the first group (<g id="">) element in our SVG file, and item2 the second and so on. That would normally be it, but we want to have a popup display something & we want links that go somewhere when clicked. To do that we store our data inside this JS file. So here is an example:
var paths = {
item: {
info: "Something For The PopUp To Show",
url: "http://link.com",
path: "Path data still here"
}
}
As you can see, info is the text the popup will show, URL is where the area of the map goes to when clicked, and path is still just the path data.
Now you just need to fill up the path file with all your path data. It can take a while but stick at it. Once you’ve done that we can continue on to our initialization code.
I keep the initialization code inside a file called init.js (original, I know) but you can easily just place it on the HTML page if you like. Let’s take a look at it before we go through it.
jQuery(function($){
var c = Raphael('map', 400, 502);
c.safari();
var label = c.popup(0, 0, "").hide();
attr = { fill: '#898989', stroke: '#FFFFFF', 'stroke-width': 2, 'stroke-linejoin': 'round' }
arr = new Array();
for (var item in paths) {
var p = c.path(paths[item].path);
arr[p.id] = item;
p.attr(attr);
p.hover(function(){
this.animate({
fill: '#8fbf27'
}, 200);
bbox = this.getBBox();
label.attr({text: paths[arr[this.id]].name}).update(bbox.x, bbox.y + bbox.height/2, bbox.width).toFront().show();
}, function(){
this.animate({
fill: attr.fill
}, 200);
label.hide();
})
.click(function(){
location.href = paths[arr[this.id]].url;
})
}
});
I know it looks complicated, but let’s try and go through it piece by piece and see if we can’t make sense of it. First off we’ll ignore the jQuery DOM ready, I’m using the No Conflict version since this was created for a WordPress based website.
var c = Raphael('map', 400, 502);
c.safari();
var label = c.popup(0, 0, "").hide();
Okay. First we create the canvas or paper, the parameters are the ID of the element you would like the image to be created in, and the width & height of the image. The width & height can be found in the top of the SVG file (when it’s open in a text editor) or by opening it in your favorite vector illustration program such as InkScape or Illustrator. Next we run a special fix built into Raphael to counteract a rendering problem sometimes apparent in Safari, and finally we create a popup and hide it straight away. We will use the popup a little later.
attr = { fill: '#898989', stroke: '#FFFFFF', 'stroke-width': 2, 'stroke-linejoin': 'round' }
arr = new Array();
These two lines are fairly simple. The first sets up our fill color, stroke color, stroke width & how the stroke lines should be joined. The second just creates an empty array to be used a little later.
for (var item in paths) {
var p = c.path(paths[item].path);
arr[p.id] = item;
p.attr(attr);
p.hover(function(){
this.animate({
fill: '#8fbf27'
}, 200);
bbox = this.getBBox();
label.attr({text: paths[arr[this.id]].info}).update(bbox.x, bbox.y + bbox.height/2, bbox.width).toFront().show();
}, function(){
this.animate({
fill: attr.fill
}, 200);
label.hide();
})
.click(function(){
location.href = paths[arr[this.id]].url;
})
}
});
Okay this last section can get complicated. First we start a loop that goes through each item inside our path object (remember from our path.js file). We then create a path using the path data, this is stored in a variable called p. Now we store a reference to the current item inside that empty array using the ID of the current path as the key, this is to help us gain access to the data (info, url etc) we stored in the path.js file. Next we tell our newly created path what color it should be using the attributes we set earlier.
Next we add some event listeners to our path. The first is hover. On hover we animate our new color in, you could do anything you like at this point but this is the effect my client was after so I stuck with it. Next we grab the width & height information for the current path and store it in bbox. Now we setup our popup, first we set the text using the data from our path.js file, then update the position using the paths ‘x’ position & it’s ‘y’ position plus half it’s height. This will make the popup appear to the left horizontally but central vertically when it is displayed on the map. We then set it to be in front of everything & to finally show. The last part of the hover is to restore the original color & hide to popup when the mouse leaves. The click function is optional and sends the browser to the URL you set inside the path.js file.
Despite the code not being very large, to those who haven’t used Javascript much I can imagine it is daunting to look at. I’ve tried to explain it as best I can without it becoming confusing, but I doubt I’ve answered every possible question you have. If you do have a question or you need me to explain something in a little more detail, please let me know via the comments below. Also I’m am currently available to do small jobs in WordPress and sometimes other small jobs (jQuery, PHP etc) please send me an email via the contact form or via admin [at] gmail [dot] com if you are interested, no large scale jobs at the moment please as I just don’t have the time available at the moment to commit to them.

Discussion: 32 Comments » add a comment
So liberating to be able to use this sort of thing on a site without the dreaded Adobe Flash. Love the new site design too.
Hi Dean,
I must admit it was a pleasure to build something as complex as that without touching Adobe Flash.
I used to work with Flash way back in ActionScript version 1/2 and I hated every moment of it. I don’t want Flash to die, but I think HTML 5, CSS & Javascript can challenge it in a few areas.
Hi Paul. This tutorial really helped me a lot with my map of South Africa! Looks pretty awesome.
But how would I display normal text (or “div”) in the center of each country when the map is created? (no events, no popups)
Will it be something like this? Can you maybe point me in the right direction?
Thanks!
David
//—————————————
//help here please with plaintext:
var plaintext = c.*plaintext*.hide();
for (var item in paths) {
var p = c.path(paths[item].path);
arr[p.id] = country;
bbox = this.getBBox();
//not sure what syntax should look like?
plaintext.attr({text: paths[arr[this.id]].name}).update(bbox.x, bbox.y + bbox.height/2, bbox.width).toFront().show();
}
Hi,
I’m not 100% but you might be able to do it by using the native Raphael text method.
So maybe something like:
for(var item in paths) { var p = c.path(paths[item].path); arr[p.id] = country; bbox = this.getBBox(); var text = c.text(bbox.x, bbox.y + bbox.height/2, paths[arr[this.id]].name).toFront().show(); }I’m unable to test it, but I would think it should be something like that. The positioning of the text may be off if it works. You’ll have to play about with the sum to get it right.
Unfortunately it isn’t as simple as making a popup, as the popup is a plugin built for raphael that allows you to make them quickly.
Thanks for the help Paul! your code almost worked… but I had trouble accessing the “this.getBbox”, it only worked within an event. So I set the bbox variable as follows.
seems a bit sloppy though..
Anyway, here is the altered working code, with text almost in center, guess it depends on map size:
for (var country in paths) { var obj = r.path(paths[country].path); obj.attr(attributes); arr[obj.id] = country; bbox = obj.getBBox(paths[country].path); var text = r.text(bbox.x + (bbox.width / 2) -10, bbox.y + (bbox.height / 2) +20 , paths[country].name).toFront().show(); }Thanks for the help! Really appreciated.
David
PS. in your original post you have the line : “arr[p.id] = country;” .
Country should be replaced with “item”
Hi
Thanks for spotting that error. I used the original code from the client to make the tutorial & forgot to change that.
I’m not sure it would work but have you tried just
obj.getBBox()? If that doesn’t work then it should be okay the way you have it.I found Raphael was a little picky when I was using it, and things that you thought would work, didn’t.
I’m glad I was able to help you a little though.
Yeah, unfortunatly obj.getBBox() gave an error, I also thought it worked. tried it multiple times before sticking to the solution above.
Oh well, the solution you’ve used looks okay to me. I can’t see any problems with it.
Hopefully it should be okay.
Completely missing the Isle of Man in the middle of the Irish Sea there. If you’re including the Republic of Ireland, then the good old Isle of Man has even more right to be included on the map.
As much as I love the Isle of Man for giving us the Isle of Man TT, it’s not on because it wasn’t required by the client who is ultimately the guy I’m getting paid by.
Jersey isn’t on, Guernsey isn’t on, again they weren’t needed on the map. So ‘a right’ or not, the client didn’t require them so they aren’t there.
What a client wants isn’t always what they need. Add value
The request was very specific in this case so unfortunately there was no room for added extras this time. Must try and push to get all the islands in the next map I make though.
Great tutorial, I’m completely new at this and was able to get things working easily, thank you! Being a bit of a novice coder, can anyone explain to me how to make the onclick event go to a url, instead of opening a popup?
Thank you
Hi Phendyr,
the code provided here should already make it go to a url when clicked. If you take a peek at the demo I’ve provided you can see when you click a ‘#’ appears on the end of the URL. That’s because the URL I provided was just a ‘#’ since I didn’t want it to go anywhere when the areas were clicked.
Let me know if you are still having problems & I’ll see if I can help you out.
well I see in the article where you list the code for the paths.js file and the url: tag.. I tried adding that, but that doesn’t work, I just end up with a /#statename in the URL path.
newhampshire: {
name: ‘New Hampshire’,
url: ‘http://google.com’,
path: ‘M 880.79902,142.424…
any thoughts? what am I missing?
Hmm, not sure. Any chance you could post your main code (not the paths.js) into something like pastebin.com so I can take a closer peek?
sure:
init.js: http://pastebin.com/C7iQzScd
paths.js: http://pastebin.com/fQyEHvv7
Ahhh, that would explain it.
It’s all down to this line:
it is set to change your hash, instead of the url. You just need to change it to what the code in the tutorial has in:
Hopefully that should do it.
Ahhh awesome, thank you. Now I just need to play around with it to remove the tooltip hover box.
Thanks much!
No problem.
The popup is probably something to do with the bit of code referring to the class point inside the click event. If you play around with removing that it may get rid of it for you.
Thanks Paul, that did the trick, I’ve got things working great now. Once complete, I’ll post a link to the finished piece. Thanks again for your great & fast feedback!
Nice tutorial !
I used http://www.readysetraphael.com to get the paths from the SVG map (wikipedia), but I have no idea where I should put the JS code that it gives me.
Any idea ?
Hi Phil,
Didn’t know about that site, it’s awesome. Wish I’d known about it when I did the site for that client, lol.
The code doesn’t exactly match what you need for this tutorial, but it’s near enough. You’d need to take the information in
.path("")parts of the code and use that in the path part in this tutorial.Hopefully that will help. If not let me know & I’ll see if I can help you out some more.
Well, I’m trying to make it work since 2 hours now (I’m a beginner in JS…), I followed everything step by step, but I don’t see where I can link my .svg file to the script so it can work ?
And also, my .svg file has this structure :
And there’s only one tag (http://upload.wikimedia.org/wikipedia/commons/9/9a/SWE-Map_L%C3%A4nsr%C3%A4tter.svg)
I even tried to take example on your demo page, but I can’t find the access to your .svg map of england either !
I finally cleaned up my mind (sorry, english isn’t my mother tongue), and understood why .svg doesn’t appear
And yes, readysetraphael is really helpful !
No problem Phil,
For anyone else reading. You don’t like the SVG file. You open it, or put it through http://readysetraphael.com and paste the long strings of numbers inside the parenthesis of
.path()into quotes after a path item in the object the tutorial asks you to create. For example readysetraphael.com might give you (truncated to save space):var path_c = rsr.path("M22.637,269.46c0.21-0.174,0.384-0.239,0.379-0.543 c-0.007-0.365-0.365-0.611-0.006-0.936c0.312-0.281,0.886-0.242,1.178-0.569c0.145-0.163,0.134-0.555,0.342-0.654...Converted for this tutorial you would have:
var paths = { item: { info: "Something For The PopUp To Show", url: "http://link.com", path: "M22.637,269.46c0.21-0.174,0.384-0.239,0.379-0.543 c-0.007-0.365-0.365-0.611-0.006-0.936c0.312-0.281,0.886-0.242,1.178-0.569c0.145-0.163,0.134-0.555,0.342-0.654.." } }Except you would need all of the path code not just the truncated bit I’ve shown here. The ellipsis (…) show where the path code has been cut short to save space.
Hope that helps anyone else who may be a little confused about this.
In some cases, one more svg element not work with mousehover, mouseout.
Before, i scale svg file to fixed with my viewport
Hi,
I’m not sure what you mean? I’ve never had any problems with mouse events in with RaphaelJS all the way back to IE6. However if you can explain a little more about the problem I’d love to take a look.
Losing considerable time, i discover that svg element is scaled, property realpath=null. The element have realpath=null, mouse event not work. Please help me.
Hi,
I’m still not sure what you mean. Is Raphael generating a SVG with a realpath parameter inside, or does your original SVG file have it?
If the latter is true, it doesn’t matter as you only need the info held in the
d=""attribute to get Raphael to work.Also have you checked for Javascript errors using Chrome dev tools or Firebug?
Hello. By default, the popover appear left horizontally. In your demo page, I found that some popover appear right horizontally. How to do that? Can I make it pop to the top or to the bottom also? Sorry for my bad english.. Waiting for your reply
Thanks
Hi Prima,
I believe that is done automatically by the small plugin that is used to create the popup. It displays to the left/right depending on how much space is available in the canvas. If there is not enough space to display it to the left it will display to the right & vice-versa. Sadly that means I’m not sure how to make it appear to the top or bottom.
Sorry, I’m not sure I’ve been much help there.