Create any map of the world in SVG

There is nothing cooler than having a map fully powered by data. D3.js does a great job at this by accepting geoJSON data and converting that into SVG. The thing is how the hell do we acquire a map that is not the one of the USA (which is the most typical example). There are many tools out there that do svg maps and they are quite good at it, but this tutorial is going to focus on D3.js, .shp files and geoJSON.

Two of my favorite resources to obtain maps are Natural Earth Data and Diva. From what I’ve seen Natural Earth Data does a good job for country layouts (just the outer border) and Diva for details within a country (regions, lakes, rivers, train tracks etc). For this example we are going to use Natural Earth Data because it already provides us with a map of the world.

Step One

  • Go to Natural Earth Data
  • Select Downloads
  • We want a good detailed map so select from Large scale data, 1:10m the cultural option
  • Focus on Admin 0-Countries from the list and click Download Countries (this will give you a full map of the world with all of the countries in it)

Step Two

Install QGIS

If you are using a mac make sure to install all of the dependencies first ( GDAL Complete and GSL )

Step Three

Manipulate map with QGIS

Extract the zip file downloaded from Natural Earth Data. Open the .shp file with QGIS. You should see a map of the world like so

After opening on the left hand side make sure that you have enabled the layers panel, right click on the layer you are viewing and select Toggle Editing. You should see something like this:

If you are unable to see the layers panel bar you can show it by clicking on View > Panels >Layers. That should enable the panel.

Now with our map ready to be edited go to the move feature  located in the tool bar, select it by clicking it and then point at the country you want to use. For this example we are going to focus on Madagascar

Click on Madagascar and move it away from the rest of the world. Say some where up North near Greenland or something. This is just to create enough space in order to remove the rest of the world’s vectors from our map.

Once moved you should end up with something like this:

Next we will remove the rest of the world by selecting the “Select Features by Rectangle” option . If you have a small screen like I do right now it might be hidden behind the “Identify Features” icon . Select the chevron and that will show you the “Select Features by Rectangle” option.

With the “Select Features by Rectangle” option selected we proceed to select the rest of the world by clicking, dragging and surrounding the world EXCLUDING MADAGASCAR.

Your world aside from Madagascar should have turned into some kind of highlighted color (varies every time). Click the Delete option  and remove all of the vertices that are selected.

Use the “Pan Map” option  to center Madagascar right in the middle. Next zoom in by scrolling up/down in order to see better our final Madagascar.shp file.

You should end up with something like so

This map is now free from all the crap that it doesn’t need. Still it might be a little bit to heavy if say for instance you want all of Australia. For scenarios where we are using to many vertices there is a tool called <a href=”http://mapshaper.com/test/MapShaper.swf”>Map Shaper</a> that allows you to upload a .shp file and minimize the number of vertices that it contains as well as export back into .shp format. This will come in handy so keep it in mind! =)

Next we proceed to save our .shp file into geoJSON format and we do so by right clicking on the Layers panel the only layer we have and choosing save as

This will pop up a window. Choose GeoJson from the Format drop down like so

And voila! we now have Madagascar in geoJSON format.

Next we will use the power of d3.js to convert our geoJSON data into an SVG map.

Step Four

If you don’t have d3.js head over to its site and download the minified or full versions. We are going to need also jquery in order to do some DOM manipulation; so add that into the equation as well.

Lately I’ve been doing a lot of coffee script and thanks to my good friends Alex and Thomas I am now an auto proclaimed coffee script evangelist, which is why our d3 code will be written in coffee script. Both Alex and Thomas have some really good posts on maps and d3. Make sure to check out this post from Thomas Positioning and Scaling maps in d3 and this one from Alex Interactive Maps with d3 and ember. You will find a lot of valuable cool stuff in them!

We will need a base index.html file to gather everything together so create one like so

<!doctype html>
<head>
	<meta charset="utf-8">
</head>
<body>
	<div id="vis"></div>
	<script src="jquery.js"></script>
	<script src="d3.js"></script>
	<script src="script.js"></script>
</body>
</html>

We now proceed to fill out script.js with our Coffee script code as follows:

xy = d3.geo.mercator().scale(8500).translate([0,-1200])
path = d3.geo.path().projection(xy)

vis = d3.select("#vis").append("svg:svg").attr("width", 960).attr("height", 600)

d3.json "madagascar.json", (json) ->
 vis.append("svg:g").attr("class", "tracts")
 .selectAll("path").data(json.features)
 .enter().append("svg:path").attr("d", path)
 .attr("fill-opacity", 0.5)
 .attr("fill", "#85C3C0")
 .attr("stroke", "#222")

or the javascript version if you prefer

var path, vis, xy;

xy = d3.geo.mercator().scale(8500).translate([0, -1200]);

path = d3.geo.path().projection(xy);

vis = d3.select("#vis").append("svg:svg").attr("width", 960).attr("height", 600);

d3.json("madagascar.json", function(json) {
  return vis.append("svg:g").attr("class", "tracts").selectAll("path").data(json.features).enter().append("svg:path").attr("d", path).attr("fill-opacity", 0.5).attr("fill", "#85C3C0").attr("stroke", "#222");
});

There are a few interesting parts in this code. Notice the projection (mercator) the scale(8500) and the translate([0,-1200]). To be 100% honest I’m not totally sure of what these values do exactly but I know that tweaking the scale and the translation accordingly will enlarge (scale) and center (translate) your svg object within its dimensions (960 width and 600 height). The projection mercator is in reference to the entire world, so Madagascar is located not so far from the center. You will most likely end up doing trial and error until you get the right scale of the country you want. I recommend starting with a small scale, say 200 and a translation of [0,0] so that you start from the top left corner and they progress your way.For more details on this please refer to d3.js

After compiling this coffee script code into JavaScript code you should end up with something like this

That’s all for this post and if you have any cool idea of how the projection, translation and scaling exactly works or if there is an algorithm to it, so that we can know what values to put in there instantly please drop a comment and let me know and I will gladly add it in your name!

Good luck with your hacks!

Some useful links

Big Thinking – strange maps

34 thoughts on “Create any map of the world in SVG

  1. Min partner og jeg snublede over her forskellige website og tænkte jeg might tjek ting.
    Jeg kan godt lide hvad jeg ser så i er bare efter dig.
    Ser frem til går over websiden yet again.Jeg like hvad du fyre
    tend for at være op også. Denne form of Smart arbejde og coverage!
    Holde op wonderful anlæg fyrene ive du fyre til min egen personal blogroll.

    Min ægtefælle og I snublede over her coming fra a forskellige page og tænkte jeg might som well tjek ting.
    Jeg kan godt lide hvad jeg ser så nu jeg am efter dig.

    Ser frem til finding ud about websiden yet again.
    Alle elsker hvad du fyre are op også. Such Smart arbejde og exposure!
    Holde op superb anlæg fyrene ive du fyre til blogroll.

    1. Hey Solbriller ray ban, thank your for your comment! I used a bit of google translate to understand danish and this is what it came up to.

      “My partner and I stumbled over here different website and thought I might check things out.
      I like what I see so is just for you.
      Looking forward to going to the website yet again.Jeg like what you guys
      off, then to be left also. This kind of smart work and coverage!
      Keep up the wonderful facilities guys ive you guys to my personal blogroll.

      My spouse and I stumbled over here coming from a different page and thought I might as well check things out.
      I like what I see so now i am following you.

      Looking forward to finding out about the website yet again.
      Everyone loves what you guys are up too. Such smart work and exposure!
      Keep up the superb facility guys ive you guys to blogroll.”

      Hope its as accurate as can be. Thanks for the good vibes!

  2. Hello, very nice tutorial. I was researching for some possibilities to use a standalone vector map in my web app for firefox OS. I might need actually more precise data and I would like to use Diva. First of is that process of extractin geoJSon is the same for Diva maps? I see there are multiple maps of roads, railways, elevation etc. Is there a way to display multiple svg maps on top of each other. Having some kind of layering with d3.js Also I guess since we will use svg, it is possible to change styles of some paths, with d3 or css right?
    Best regards

    1. Yes DIVA is an outstanding source for map data. I prefer it over the rest. You can always use QGIS to overlay the data from DIVA. That way you would end up with one big geoJSON that contains all of the necessary parts of the map (roads, rivers, borders etc). Because SVG is part of the DOM you can always use CSS to style it and by that I mean you can put the containers of the svg elements into absolute positions and then modify their z-indexes to overlay each different svg element. So both would work.
      I would only keep an eye on the DOM and make sure to use as little paths as possible. Make sure to check out mapshaper in order to minimize the number of polygons in your map. Either that or if possible use topoJSON instead of geoJSON. It weighs exponentially less.

      1. I have problems seeing the elevation with coloration, it’s just grd and gri files in the zip, also form where i can get cities and other names of geographical places

      2. both file formats are compatible with QGIS. Are you creating svg paths for that data? It might need something else. Found this link that might help you out http://www.theatlanticcities.com/technology/2013/02/mapping-data-dense-cities-if-they-were-mountains/4818/
        All the city information is inside of the geoJSON. If you compress it to much with map shapper you will lose this data. I would recommend console.log’ing it and you will see that the geoJSON object has a an array of features and each feature has a geometry (holding in the data to create a path) and a properties property that contains info like region name, id, type etc

  3. Hi

    I followed the steps you described….almost. Thank you in the first place for all the groundwork and useful links.
    I installed QGIS on OSX 10.8.4 and it think it is a slightly more recent version than the one you described.
    Anyway, some feedback that may be handy:
    1. QGIS didn’t recognise the .shp file. You have op de file by dropping it on the QGIS icon and then it works.
    2. I tried the Plugin-menu and was abole to install a Simple SVG plugin. This obviously facilitates everything, because I was able to export the whole world map to a single SVG file, in one step!

    Thanks again!
    Hans Erik

    1. Sure, you need to find the right geoJson file that contains the states. The article mentions 2 sources, check both of them out, one of them I’m almost positive contains the states of India.

  4. Hello! Oscar
    I’m trying to learning what you taught on your website.
    I lost all pictures you put on the website, it maybe caused by the network.
    I did all things just by words, but my English is poor so my understanding is not accurate. Finally it didn’t work out. My screen is blank.I think I lost some important information contained in pictures.
    Can you post me a duplicate of this passage by email.
    I just find another question.there is nothing in the element .
    Can it be caused by file of geojson?
    Want your reply
    a learner from china

      1. Thank you! Oscar.But unfortunately it didn’t work, Chinese government limits the connections to foreign countries, it sucks – -#
        But I think i find the question,QGIS produce the file which format is geojson rather json.Can it be the reason?

      2. Damn… sorry to hear about that. Yes QGIS will produce geojson. But you can still ajax it in via .geojson. Are you running a localhost?
        Do you see the geoJSON file? Is QGIS converting the .shp file correctly?

  5. My brother suggested I might like this website.
    He was totally right. This post actually made my day. You cann’t imagine just how much time I had
    spent for this info! Thanks!

  6. Hi Oscar, I followed all you steps, I’ve use the country spain and I produced geojson successfully. But when i code it. Map is not showing in the browser, when i check the console. There was an error “Invalid value for then the whole coordinates” so i decided to copy as is your code but still the same. can you help me.

      1. Really thankful for you reply, I can’t really access the link the you have provided. If it’s possible, is it okay if you’ll send it via email. Here’s my email

        BTW, this blog site is really helpful, thanks for you effort explaining things like this.

    1. OK sent it to your email. Going to delete your comment so that your email is not exposed to whom ever reads these comments =) I’ve also posted a new link to the file. Either should work

Leave a reply to gopesh Cancel reply