Drawing a world map with D3

- Where in the world has davo been

I’ve started playing around with geographical maps in D3.

It’s all quite new to me and there were plenty of new parts to get my head around. Of most use was Mike Bostock’s excellent tutorial Let’s make a map. It covers all the main points to make a map.

I decided to have a go at a simple task – representing the countries I’ve visited in an elegant D3 map.

The main steps that go into making a map with d3 are:

1 – Find and prepare your data

A world map is made up of a lot of data. The data is used to draw the map as a path in the browser. It will also contain other properties such as place names, population sizes etc.

First choice to get this data seems to be natural earth data. It can be downloaded in a range of different formats. I downloaded countries using the 50m resolution (as I didn’t need too much detail). This is a zip file containing various formats. The SHP (shapefile) can then be converted to geoJSON and then onto topoJSON. This is needed because D3 deals in geoJSON and topoJSON formats.

You’ll need to download GDAL, node.js, topojson. Full instructions for how to do this are here.

Note: I had to install GDAL from here because after using homebrew it wasn’t recognising the ogr2ogr command. After installing GDAL I then had to add to .bash_profile:

1
export PATH=/Library/Frameworks/GDAL.framework/Versions/1.10/Programs/:$PATH

2 – Draw the map

Once you have the data ready as geoJSON or topoJSON you can then load it into the browser.

The d3.json function takes the json file from where you have saved it on the server.

1
2
3
4
5
6
7
8
9
10
d3.json("/data/world-50m.json", function(error, world) {

var countries = topojson.feature(world, world.objects.countries).features

  svg.selectAll(".country")
      .data(countries)
    .enter().insert("path", ".graticule")
      .attr("class", function(d) { return "country " + "code" + d.id; })
      .attr("d", path);
});

I included the country id in the class as these are numeric ISO codes

This could then be used to identify countries.

3 – Decide on your projection

A map projection is a systematic transformation of the latitudes and longitudes of locations on the surface of a sphere or an ellipsoid into locations on a plane. Map projections are necessary for creating maps. All map projections distort the surface in some fashion.

D3 contains lots of different projection options. There are the standard ones.

d3.geo.albersUsa
d3.geo.azimuthalEqualArea
d3.geo.azimuthalEquidistant
d3.geo.conicEqualArea
d3.geo.conicConformal
d3.geo.conicEquidistant
d3.geo.equirectangular
d3.geo.gnomonic
d3.geo.mercator
d3.geo.orthographic
d3.geo.stereographic
d3.geo.transverseMercator

There is also an extension of different projections.

For the map above I used the baker extension. It allows you to scale and translate.

1
2
3
4
var projection = d3.geo.baker()
    .scale(122)
    .translate([width / 2, height / 2])
    .precision(.1);

4 – Styling

Styling is important to the final product of the map. Since the graphic is in SVG it can be targeted using CSS.

I have added in various basic styling. I also used CSS to colour in different countries based on the classes I had given them.

1
2
3
4
5
6
7
8
9
10
.country {
  fill: #2D7F99;
}

.country.code32 {fill: #BF3930;} /*Argentina*/
.country.code36 {fill: #BF3930;} /*Australia*/
.country.code40 {fill: #BF3930;} /*Austria*/
.country.code68 {fill: #BF3930;} /*Bolivia*/

/* etc etc */

This was a bit of a shortcut as it may have been easier to update my data in step 1 with the countries I had visited and could then just colour them as a group.

In any case I really like the finished product. Very nice. Now I clearly need to get cracking and travel to Africa and Asia. :)

Comments