Using RaphaelJS To Create A Map

/ Javascript, jQuery / by Paul Robinson / 115 Comments
This post was published back on June 27, 2011 and may be outdated. Please use caution when following older tutorials or using older code. After reading be sure to check for newer procedures or updates to code.

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.

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:

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:

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.

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:

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.

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.

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.

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.

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

Author’s gravatar

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.

Reply
Author’s gravatar author

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.

Author’s gravatar

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();
}

Reply
Author’s gravatar author

Hi,

I’m not 100% but you might be able to do it by using the native Raphael text method.

So maybe something like:

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.

Author’s gravatar

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:

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”

Reply
Author’s gravatar author

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. 🙂

Author’s gravatar

Yeah, unfortunatly obj.getBBox() gave an error, I also thought it worked. tried it multiple times before sticking to the solution above.

Reply
Author’s gravatar author

Oh well, the solution you’ve used looks okay to me. I can’t see any problems with it.

Hopefully it should be okay. 😉

Author’s gravatar

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.

Reply
Author’s gravatar author

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.

Author’s gravatar

What a client wants isn’t always what they need. Add value 🙂

Reply
Author’s gravatar author

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. 😉

Author’s gravatar

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

Reply
Author’s gravatar author

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.

Author’s gravatar

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?

Reply
Author’s gravatar author

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?

Author’s gravatar author

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.

Author’s gravatar

Ahhh awesome, thank you. Now I just need to play around with it to remove the tooltip hover box.

Thanks much!

Author’s gravatar author

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.

Author’s gravatar

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!

Author’s gravatar

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 ?

Reply
Author’s gravatar author

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. 😉

Author’s gravatar

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 !

Reply
Author’s gravatar

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 !

Author’s gravatar author

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):

Converted for this tutorial you would have:

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.

Author’s gravatar

In some cases, one more svg element not work with mousehover, mouseout.
Before, i scale svg file to fixed with my viewport

Reply
Author’s gravatar author

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.

Author’s gravatar

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.

Reply
Author’s gravatar author

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?

Author’s gravatar

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

Reply
Author’s gravatar author

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. 🙁

Author’s gravatar

Hi, newbie to Javascript and spent literally HOURS looking for tutorials on how to do something like this, and yours was the only one that really explained it and made sense, so thanks a lot for putting this together! Quick question though:

In the init.js I understand that {text: paths[arr[this.id]].name} makes the “name” of each country display when that path is hovered over. I want to add another line of text after the name that I define in the paths.js file (for example, language spoken in that country).

So in paths.js it’d be:
[jQuery]
ENG: {
name: “England”,
language: “English”,
path: “…Path Code Here..”
}
[/jQuery]

How do I add that to the init.js text line so that it also displays the language?

Thanks so much for this tutorial again!

Reply
Author’s gravatar author

Hi Liz,

Thank you. Glad the tutorial was described well enough even for a person new to Javascript to understand.

As far as I know if you added the language like you have in your example you would do something like:

That should do it. I think. Unfortunately I haven’t been able to test it, but if Raphael continues to follows the rules of standard JS that should be the way to get it working.

Author’s gravatar

Thanks so much, works perfectly!

One follow-up question: I’ve been playing around with changing the font style in the .attr() element, but is there any way to style 1 line of text different from another? I.e., the “name” to be bolded and “language” to be not? I tried using .bold() but it just shows the tags in the popup. Is this possible?

Thanks again for your time!

Author’s gravatar author

Hi Liz,

I’m not sure. Since it is SVG I doubt adding HTML into it would work. There is a font-weight parameter in Raphael, but I don’t think you could only apply it to one line since the label raphael plugin takes all the text together. You can certainly play around using any of the parameters you can find here to change the styling, or at least I would imagine you could. I’ve never tried it so I can’t be sure.

Sorry if that hasn’t been much help.

Author’s gravatar

Great tutorial!

It’s refreshing to see something that in theory is so simple put into practice using raphael and explained in a way that doesn’t leave you scratching your head.

I have one question though… if I wanted to make a region stay highlighted once it’d been selected how would I achieve this? Ultimately, I’d like all of the regions to be mutually exclusive so that you can only select one at a time and it stays selected but I’m finding it tough to add this functionality within the obj.click as it seems that it only affects each region individually.

Any help much appreciated 🙂

Reply
Author’s gravatar author

Hi Suths,

That would be tricky. I guess you’d attach whatever you’d want to do to the click event & remove the hover if you’ve no need for it. So if you want it to highlight when clicked move the code inside the hover event into the click event instead.

It might be tricky to explain & I’m really short on time to explain it properly or even test it, but you could try something like:

Again I haven’t had a chance to test it & my Raphael is a little rusty, but give it a shot & let me know what happens. If you have any trouble I should be finished with my work by Thursday so I can see if I can help you out then. 😉

Author’s gravatar author

Hey Suths,

Ignore my comment & take a look at this example I just quickly knocked together.

To see what is going on take a look at the source code & if possible use a browser with a console (Chrome, Firefox with Firebug) and open it then try clicking a region. It should explain what is going on if you need the help. If it doesn’t help that much please let me know & I’ll try and explain it when I get a few moments.

Author’s gravatar

Hi Paul,

That’s great and works perfectly 🙂

My raphael isn’t advanced but I can see exactly what you’ve done here and it makes perfect sense.

One thing I have noticed is that the tooltip region names don’t show up in IE at all. I’ve found that it’s because of IE’s lack of console support but there is a fix. Add the following code into the beginning of the function in the init.js file:

I’m going to try and add a grow or pulsate effect on hover so if you have any clue how to achieve this I’d love to get your opinion 🙂

Reply
Author’s gravatar

Sorry, forgot to encapsulate my code with tags :S

Reply
Author’s gravatar author

Fixed the code tags for you.

Ahh, yes. I keep forgetting since my version of IE supports the console. I add those into the file to stop it from erroring for everyone with older versions of IE.

Good luck on that, I’ve never been able to figure it out as it involves messing around with the SVG data & my Math sucks. 🙁 If you do figure it out please, if you get a chance, drop by and let us know how to do it. 🙂

Author’s gravatar

Love the tutorial! Got a question for you (i’m a java noob) I would like to add a color and hover color to each individual country, how could I implement this?

Reply
Author’s gravatar author

Hi Matt,

That’s actually more difficult that you might first think. I think the best way would be to use a switch like you can see in the Javascript source for my demo. It would become incredibly messy doing it like that though so you might consider a function like this:

Place this in the bottom of your init file. Remember to change the case names (and add more if needed) to your slugs, or you can match an id or whatever you like as long as it is a piece of data contained in the paths file that raphael is given.

Then you would need to add it in various places of the main init code. Like this:

Then:

Finally:

We could just use attributes.fill since it’s been changed, but best to ask again to make sure we get the right result.

I think that should work. Sadly I’m unable to test it at the moment, but let me know if you have any trouble & I’ll see if I can find any problems for you.

Author’s gravatar

No dice. I do appreciate you trying. Can I contact you directly through email?

Reply
Author’s gravatar author

Sure. Drop me a line & I’ll see how I can help.

Author’s gravatar

Hi Paul,
I know it’s been forever ago since this tutorial was posted but it was incredibly helpful for me just recently, so thank you!
I actually had the same question as Matt, about wanting to fill each country with different colors and I managed to do so with the code you provided for him, using the switch. However, the thing I can’t figure out is how to leave the color of each country just as it was before the hover action. As it is right now: country1 is originally yellow, I hover and change it to blue and it doesn’t return to yellow after leaving the mouseover, it just takes the default color value of the switch. Do you think you could help me with that? Thank you again!

Author’s gravatar

This is a great tutorial, but i have a question.
how would you target a path from outside the svg.
say you have a list of countries and want the country to highlight when you click a country name.

Reply
Author’s gravatar author

Hi Marc,

You would either have to set up the click events for those links when the code relating to the SVG is ran (probably the easiest way), or you’d have to keep an array of the IDs of the SVG elements as they are created & use Raphael’s paper.getById() method to get a reference to the element you are wanting.

Does that make sense?

Author’s gravatar

Hi Paul,
thanks for your reply, to answer your question, yes and no.
where am I setting the id?
would you have a code snippet that may work on your map example above?
i’m usually good once i’ve seen a working example, it gives me my aha moment
thanks for your help

Reply
Author’s gravatar author

I’m not sure I could provide a working example. Basically you can either add it into the code that actually adds the hover/click events to the SVG map, or you would have to grab the id’s of the elements as they are created by Raphael. The ID is found in element.id however in the example I assigned element to obj. You can then pass that ID to paper.getById() to get access to the element again.

I guess an example based on my demo could be:

Then when you want access to the element again the code might be:

Would give you the element for the republic of Ireland. You would use the key used in the paths file as that is what the id was stored under in the array. Does that help? It’s really complicated & I’m kinda struggling to explain it, lol.

Author’s gravatar

http://return-true.com/examples/map-orig.html
Not showing up for me:(

Reply
Author’s gravatar author

Hi Tristen,

I apologise for that. I’ve fixed the problem. For some reason a couple of ampersands had been turned into their HTML encoded versions in the JS. Not sure why. Anyway it’s fixed now so give it another look. 😉 Don’t forget to do a Ctrl+F5 to clear your browser cache if it isn’t showing still.

Author’s gravatar

Hi Paul,
Great tutorial. I found it yesterday and saw that the demo wasn’t working, but decided to go ahead and work through it since it was so thoroughly written. Unfortunately, I’ve gone through and think I have it all right, but mine looks exactly the same as yours currently does. Which is to say, a grey screen. Any suggestions?

Reply
Author’s gravatar author

Hi Dani,

I’m not seeing any problems with the demo any more after correcting the last problem that was reported. Tested in IE 7, 8, 9, Chrome (latest), FF (latest) and Safari (latest). What browser are you using so I can give it another check.

On your page I’m getting an error in your copy of the RaphaelJS script, there is something strange going on in the file if you view it in the browser. You also have too many commas in your paths.js file. You don’t need the last two as that can cause Internet Explorer to go mad.

If you’re still having trouble after looking at those please feel free to get back to me & I’ll do everything I can to help you get it working. 😉

Author’s gravatar

First of all, thanks for taking a look. I removed the Raphael header (not sure why it was saving that way on my server…) and then removed those extra commas (a little too liberal with the copying/pasting, apparently), and it’s working! I really appreciate your help, and your tutorial was seriously amazing.
Oddly, however, I can’t get your map to work for me. I tried it on Chrome, Firefox and Safari on my Mac and then tried Firefox and IE on my PC, and I have yet to see your map in action. However, my friend in Colorado was able to see it without a problem. So I’m stumped.
Thanks a ton, Paul!!

Author’s gravatar author

Awesome stuff, glad you got it working. I’ve never seen anything like the header before, was very strange. I can only think it was some sort of character encoding problem?

Not sure why my demo won’t work. Could you give it another go. I’ve told my CDN that I use to clear the cache for the file just in case it was still serving the broken version in some regions. If not, I’m stumped too.

No worries, glad to help. 🙂

Author’s gravatar

Ah, it’s working on my personal computer at home. That’s weird that I couldn’t see it while I was at work since our network doesn’t have any filters or blocking. But it looks like it was definitely an issue on my end and not something you should worry about. Thanks again!

Author’s gravatar author

Ahh, well good news it isn’t my demo, but not sure why it wouldn’t work on the work PC. 🙁

Author’s gravatar

var bB=b0.rect(74.5,150,807,350).attr({href:”http://google.com/”,target: “top”,”stroke-width”:0,fill:”url(images/m.jpg)”,stroke:”none”});bB.id=”vlogitBackground”;
can you please tell how to add iframe tag in above format . i am using rapheal js.
thanks in advance 🙂
reply me on shikha4sumone@gmail.com

Reply
Older Comments

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

I'll keep your WordPress site up-to-date and working to its best.

Find out more