Skip to main content

Layouts and Frequently Used Visualizations

Now that I've covered the basics of programming in D3, let's take a look at some of the other cool things one can build with D3. Before jumping into the code, it's worth mentioning the resources available within the D3 community for sharing reusable code. As of writing this there are sites like VisHub or the D3 Graph Gallery which contain some commonly used D3 code snippets.

Also, this guide mostly follows version 7 as it is the latest . Sometimes libraries are slow to update documentation examples so it is important to read the change log when trying to upgrade D3.

Layouts

Layout functions format the data to include attributes that allow for drawing the data.

I found the below visualizations here and here, but note it uses D3 v3 syntax, for v4+ we would just use d3.pie() rather than d3.layout.pie()

Other layouts include:

  • d3.force() - Nodes are represented as circles and edges (connections) as lines

    image.png

    image.png

  • d3.stack() - Good for illustrating how ratios compare over time

    image.png

    image.png

Choosing the Right Data Structure

There are cases where the above functions expect data in a certain format, that being either an Array or Object (map).

image.png

The advantage with Maps is we can find a key without the index or searching the entire data structure. In D3 we often work with Arrays of objects, which is good for looping over objects but can make searching for a field within an object cumbersome. The D3 group(), or groups() function (previously known as nest()) can create a map of JS objects based on a category. 

Maps and Networks

The first step towards creating a visualization of a region is finding the map file to use, containing information about borders and locations. There several places online that can create files for this purpose, the most popular format being GeoJSON and D3.geoPath(). Another format is TopoJSON which also encodes topology, and are usually smaller than GeoJSON even though they include more data.

When building maps converting a sphere to a 2D surface can be problematic. To get the map to fit nicely to the screen we often apply some kind of projection that stretches the areas nearer to the poles, making them appear larger than they actually are. In D3 we have the flexibility to choose the projection we want to use with our maps.

    var width = 600,
        height = 400;

    var projection = d3.geoConicEqualArea()
        .scale(153) // How much to zoom in
        .translate([width / 2, height / 2]) // Center the map
        .precision(.1);

    var path = d3.geoPath()
        .projection(projection); // Define the path and attach projection

    var graticule = d3.geoGraticule(); // The coordinate lines on the map

    var svg = d3.select("body").append("svg")
        .attr("width", width)
        .attr("height", height);

   d3.json("data/world-110m.json").then(function(world) {

        svg.append("path")
     		// Land borders for the map
            .datum(topojson.feature(world, world.objects.land))
            .attr("class", "land")
            .attr("d", path);

        svg.append("path")
     		// Define borders between countries
            .datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; })) 
            .attr("class", "boundary")
            .attr("d", path);

        svg.append("path")
            .datum(graticule)
            .attr("class", "graticule")
            .attr("d", path);
    });

    d3.select(self.frameElement).style("height", height + "px");

So in the above I use d3.geoConicEqualArea() to get a cone shaped map with actual sizes, the typical projection of the standard classroom world map would be geoMercator() instead.