Using RaphaelJS To Create A Map
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.
1 2 |
<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:
1 2 |
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.
There is an alternative though. Eric in the comments to this tutorial has kindly pointed out that there is a small web app that will take your SVG file & pump out the needed RaphaelJS code to make it. Kinda cool!
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:
1 2 3 |
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.
1 2 3 4 5 6 7 8 |
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:
1 2 3 4 5 6 7 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
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.
1 2 3 |
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.
1 2 |
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.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
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.
115 Comments
Luca
Hi, thanks for your tutorial.
It’s the first one I found on this subject that is actually easy to follow.
I have a couple of questions:
1) would it be possible to make the map responsive? At the moment, if the size of my map is bigger than a smartphone screen, part of the map is chopped off. Is there a way to set the width to be 100%?
2) How can I turn off the pop up for some elements?
I am using this to show a floor map. I don’t need to show a popup for the walls… 😉
Cheers
Paul Robinson
Hi Luca,
I don’t think so. I wouldn’t say it isn’t possible, but I think it’s unlikely due to the way it is made. As far as I know SVG doesn’t support percentage based sizes. You could use the viewport meta tag:
to tell your smartphone how to scale the page. That might at least help a little.
To disable the popup on certain elements you could add a flag into your data, the data that defines you paths etc, and then check for it using a conditional in your code. Take a look at my init.js file used for my demo, there is a switch in it that should point you in the right direction, if you are still having trouble drop me an email or another comment & I’ll see how I can help.
Fred
I’m trying to use rectangles instead of paths to have the same effect for a site I’m making. I can’t seem to figure out exactly what the issue is, but it’s not working for me.
I have a feeling it’s because this code in the init.js file, which links to the paths.js file and reads my rectangle parameters (x, y, width, height) as a path string.
I’m also not sure if this code has something to do with it either:
I tried changing the r.path to r.rect, but to no avail… do you know a workaround for this?
Thanks so much.
Paul Robinson
Hi Fred,
The console code is just to log to the browsers console (F12 in most browsers) in the demo page for those who want to see the positioning info & path info while the demo page is running.
I’m not sure I understand. The
rect
method creates a single rectangle from 4 or 5 parameters (x, y, w, h, r). That’s X pos, Y pos, Width, Height, and Radius for rounded corners (if applicable). Thepath
method takes a string in SVG format and creates a path element from it.Dean Nicholas
With regards to the SVG files… are the ones available on, for example, Wikipedia useable with Raphael? I have one but, once downloaded, it refuses to open in any text editor.
Paul Robinson
Hi Dean,
Yep should work no problem. The map I was given by the client was actually taken from Wikipedia/Wikimedia Commons.
Craig
Great tutorial.
One question (probably of many). How do you make the URL go to a new target, specifically target=”_blank”?
Thanks
Paul Robinson
Hi,
You would replace
location.href
with the standard command to open a new windowwindow.open(URL,name,specs,replace)
. I’ve never tried it but don’t see why it shouldn’t work.capri6
Hello. Great tut. Thanks for sharing with us.
I have a question, I have to make an interactive floor plan and was thinking of using Rafael. Then I tried above and exported my SVG file & tried to make all the paths, however I seem to have problem with showing all the images//slices from the floow plan.
I believe this is the same principle as using Rapahel for interactive maps.
If this is the case, do you think then I can use jquery when I click on say one colour Red from the legend of the floorplan, to show colour Red at the same time on the plan – or probably fade out all the other colours.
I am quite new so far I’ve managed, but I think I wil need you advise.
Could you please help me
thank you
PS I think I am also exporting the pdf wrongly from Illustrator….(it’s not only the save as SVG option….)
Hubyx
Many thanks! It realy works! This was killing me and I didn’t know how to make it work. Thanks again.
Chae
Thanks for the good information.
BTW, How can I resize the entire svg map?
I added “scale:1.5” to attributes in “init.js” .. it seemed working.. but every province got larger and overlaped with each other. It would be wonderful it I can use this map to fit various layouts in any size.
Thanks again!
Paul Robinson
Hi,
My knowledge of SVG & Raphael is quite limited. Unfortunately I haven’t had much more of a chance to work with it since this project. My best guess would be that you’d need to make the original SVG bigger, or you would have to scale them then offset them by the amount they have been scaled… If that makes any sense. Again unfortunately my knowledge of this is quite limited. Sorry. 🙁
anso
May be a bit late but this can be useful (extract from http://webdesignerwall.com/tutorials/interactive-world-javascript-map where no jQuery is involved)
in the init you can do
Hope this helps
Cheers
JP Bantigue
Thank you for this tutorial! It really helps on what I’ve been working on. But is there a way so that initially, the “map items” are invisible, and will only become visible when the mouse hovers on it? And when the mouse leaves again, it becomes invisible (or opacity: 0) once again?
JP Bantigue
Oops! I got it!
I inserted [code]opacity: ‘0’[/code] in the second function inside [code]p.hover[/code]. I’m still an amateur in jQuery, but from my understanding, the 2 elements inside hover would be the: if true, and if false statements? I’m just guessing.
JP Bantigue
Back again! I’m having similar issue as Fred.
What if the SVG file I created generated a rectangle whereas the code is in the form of rect and not a path? How will I input this into paths.js if paths.js reads paths and not rect coordinates?
Is there a way to convert rect coordinates to path coordinates?
Thanks!
Paul Robinson
Hi,
As far as I know it has to be path coordinates rather than rect. Raphael can take rect coods (I believe) but the method used here can’t accept those coordinates.
I believe you can convert them, but I don’t know SVG well enough to tell you how to do it. Sorry. 🙁
JP Bantigue
Hi, Paul, I was able to do a simple RECT-PATH coordinates converter in MS Excel, which I could send you and maybe you could upload it on this site so it could be of use to those who need it too. You can email me personally and I’ll just send you the file.
By the way, would you happen to know why when this site: http://tinyurl.com/bmnbk33 is opened in Chrome or Firefox, the highlight labels work properly; but when opened in IE they do not show. However, when testing your example in different browsers, they work perfectly.
I’m just not sure why I experienced it, even though I have just used the script/code you used as reference.
Thanks, in advance!
Eric
I had some issues converting my Inkscape SVG files to Raphael. Copying and pasting the paths directly did not work. The countries were rendering outside of the Raphael div. I tried flipping the SVG vertically in Inkscape and using those paths, but that didn’t work either.
Fortunately, I found Ready Set Raphael and it solved the issues. http://readysetraphael.com/
Paul Robinson
Thanks Eric, that is a very awesome tool. I’ll add it to the article so people can find it easily.
Indira
I’m having trouble getting my map to show up. I went through the entire tutorial and then uploaded the files to the root of my WordPress site. Can you tell me how I can get the map to shop uw on a WordPress page?
Paul Robinson
Hi Indira,
You shouldn’t really have many problems with it (normally). The best way to debug problems like this are to triple check that you have the name of your element correct (and make sure it is an ID not a class). Also if possible use a JavaScript console such as included with Chrome or as an add-on for Firefox (FireBug). They help a lot with JavaScript errors.
bagas
Great tutorial
thanks a lot
i’ll try this tut
Thanks 😀
marwa
Heya,
so i’ve been trying these steps over and over again for the past day, but i keep getting an error in the paths.js file on the path:”” line. This is what i’ve been writing (I also have another question at the end of this comment):
Question 2: Is there somewhere where i have to link the SVG image im using to the file?
Paul Robinson
Hi Marwa,
The error may be because there are returns in your Path data. The path data needs to be all on one line, or have ‘\’ at the end of each line to tell JS to ignore the return. Also if you copied anything from my code please make sure that your double quotes are indeed double quotes. Sometimes copying from a WordPress blog you end up with the unicode version of the quote, which causes errors in coding.
Also you have double quotes in the hyperlink you are using, this will be opening and closing the quotes used to hold the data and can cause errors. Make sure to either backslash your inner quotes or change the outer double quotes to single quotes instead.
To answer your second question. No. The idea is that Raphael creates your SVG image using the path data from it. So no, you don’t need to attach the original SVG image.
Michael
I have followed the instructions and got nowhere after several hours.
I found the following error in the console and have no idea what it means?
Error: Problem parsing d=”L-5,0L-5,0A5,5,0,0,1,-10,-5L-10,-5L-15,-10L-10,-15L-10,-15A5,5,0,0,1,-5,-20L-5,-20L0,-20L5,-20L5,-20A5,5,0,0,1,10,-15L10,-15L10,-10L10,-5L10,-5A5,5,0,0,1,5,0L5,0Z”
Michael
Odly enough I deleted the JS for the popup in the init file and it worked. I then put the popup bit back in and it seems to work with the same error however. Perhaps there is a problem with the version of Raphael I downloaded today.
Paul Robinson
Hi Michael,
It is possible that new versions of Raphael are not compatible with the popup code. Someone on the Google Group for Raphael mentioned having success with adding gRaphelJS (http://g.raphaeljs.com/) along with Raphael which apparently has the popup code inside. Try those two together instead of the popup code I provide, and hopefully that will help.
Michael
Sadly it is not that simple and involves re-working the init code somehow…
Paul Robinson
From what I can see the gRaphaelJS code uses the same init for the popup as used in the snippet in the tutorial, but it is possible that it does need some tweaking.
Dani
Hm, this tutorial worked for me almost a year ago, but revisiting my code today, I’m getting the same error message as Michael. Honestly, it looks like everything is working, but the console is erroring. And when I add gRaphaelJS, the map renders without errors, until you hover and the console explodes. For the life of me, I can’t figure out what this is.
Paul Robinson
Hi Dani,
Sorry about the tardiness in replying. Things are a little crazy with work so I haven’t had much time to check comments.
I’m sorry, I haven’t a clue at the moment. I haven’t needed to use Raphael for a while, but I have a project coming soon where I’ll need it so I’m planning to refresh this tutorial once I figure out what is going on.
Probably not much help if you need this now, but if you are able too come back soon & hopefully I’ll have the answer.
eddie
Hi Paul,
Thanks a million for the great tutorial, it`s help me a lot. BTW how to add/append ID and URL to SVG path ?
Paul Robinson
Hi Eddie,
You would just add it as one of the object items in your paths file, along with the other data I add in the tutorial. You would also access it in the same manner used in the tutorial too.
Simone
Great tutorial, this is the first comprehensive guide to an interactive map realisation I’ve ever read!
Asking how to add some feature, is it possible to show an svg element (e.g. an half circle) on the hover event listener? The main issue would be to show an half circle on hover that will be proportionate to a list of data for each region of the map. For example, on Wales it should show an half circle with radius = 5 and on Scotland the radius would be = 7.
Would it be too much complicated or am I making a storm in a teacup?
Lilographik
Great tuto !
Can I change the color of the small pop-up
Thank you Paul
Paul Robinson
Hi,
You certainly can. If you look at the popup code you have to use (starts with
Raphael.fn.popup
) you should be able to findfill '#333333':
andstroke: '#333333'
. Just change those to the hex color you would like & it should change the color for you.Lilographik
@Paul Robinson
I opened the “raphael-min.js” file and I changed the color of the label. it works.
Thank you for your help Paul!
Ravi
Hi Paul,
Thanks for sharing such nice and usefull article and informaiton.
Can you please provide help in creating “Isle of man” path code or if we need to add more sections to your image, how I can do this?
Thanks again!
Rk
Lilographik
Hi Paul
On your map, when I do a mouseover on Northern Ireland I try to assign the same color on Republic of Ireland with a decrease opacity.
I put on the tooltip: Northern Ireland (return) – IRELAND –
You have a brilliant idea to do that?
thank you Paul
Emily
Hey Paul!
Thanks for the tutorial! Quick question though, I’ve created a map of America svg with separate state paths, but how would I link the url of the different states to a div on the same page instead of a separate url?
I want to populate the div with PHP from the database which will display the different state information when clicked. I’m pretty new to Java and everything so finding this very confusing! Your help will be very much appreciated, thank you!
Emily
My hover over state is also displaying ‘undefined’ instead of the names of the States and I’ve followed the tutorial exactly, does anyone know what the problem might be?
UPDATE
Nevermind got the hover working silly me! I’d like to add a photo and some more information in the hover box however, would you have any tips on achieving this? Sorry to be bombarding with questions!
Paul Robinson
Hi Emily,
Sorry for not getting back to you sooner, been very busy with work recently.
Unfortunately I’m loathed to admit that adding a photo into the popup is completely beyond me. However an untested guess is that you may be able to add an image by altering the PopUp code (You’ll need to beautify it: http://jsbeautifier.org/) then add in the image along side the text.
Going through the code here would make things huge, but you are most likely looking to add in:
The parameters are: Image Path, Left Pos, Top Pos, Width, Height.
Honestly I haven’t been able to find time to play around with Raphael for a long time, but I’ll certainly try my best to help out if you’d like me to.