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