Visualising GitHub repo data with d3.js
I have been spending some time learning d3.js (Data-Driven Documents), a data visualisation library for javascript. I used d3 to create a bubble chart of all programming languages I have used throughout my public GitHub repositories, using data from the GitHub API.
Backend
I added a function to my site’s custom build plugin to output the language statistics to a json file. It uses octokit to interact with GitHub and the JEKYLL_GITHUB_TOKEN
I had previously configured for the github-metadata plugin.
def _get_repo_languages(site)
client = Octokit::Client.new({:auto_paginate => true, :access_token => ENV['JEKYLL_GITHUB_TOKEN']})
language_totals = Hash.new
client.list_repos.each do |repo|
languages = client.languages(repo['full_name'])
language_totals.merge!(languages.to_h) {|k, new, old| new + old}
end
# Write json file in the format: {"language_name": total_bytes, "C": 4567, ...}
File.open('data/language-totals.json', 'w') do |f|
f.write(language_totals.to_json)
end
end
This approach does of course mean that I have to manually perform a rebuild of my site locally to update the data. I also made the data update an optional part of the build process to avoid hitting the GitHub API and slowing down the build each time a rebuild is triggered.
Frontend
I used d3’s circle packing algorithm (d3.packSiblings
), which packs circles based on their radii. To calculate the radii corresponding to each language I used the following formula:
This is to prevent individual bubbles from becoming too large or small.
The bubbles are then displayed in an SVG using the following:
d3.json("/data/language-totals.json", function (error, data) {
if (error) throw error;
// Add groups for each bubble
var nodes = svg.selectAll('.bubble')
.data(to_circles(data))
.enter().append('g')
.attr('class', 'bubble')
.attr('transform', function (d){
return "translate(" + d.x + "," + d.y + ")";
});
// Add the circle elements
nodes.append("circle")
.attr("r", function (d) { return d.r; })
.style("fill", function (d) { return d.color })
.style("stroke", "white")
.style("stroke-width", 2);
// Add the circle text
nodes.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function (d) { return d.name + " " + d.percentage.toPrecision(3) + "%"; })
// Hide text for small bubbles to avoid overlaps
.classed("hide", function (d) { return (this.getBBox().width > (2.5 * d.r)); });
...
The full source is available in this site’s repository.
Languages Used in my Public Repos
Note: Mouse hovering doesn’t currently work correctly in IE due to this bug.