My aim here was to play around with the d3.js force graph layout using the train network in Berlin.
Firstly, I scraped the data from the train website. It had a list of nearby stations but no actual network. I then constructed a graph structure by assiging edges between nearby train stations. My hope was that the force structure would converge on an actual map of Berlin (though maybe a little optimistic given the result).
d3.json('/data/bvg-graph.json', function(json){
var width = 740,
height = 600;
var svg = d3.select('svg')
.style("display", "block")
.attr('width', width)
.attr('height', height);
// draw the graph edges
var link = svg.selectAll("line.link")
.data(json.links)
.enter().append("line")
.style('stroke','black');
// draw the graph nodes
var node = svg.selectAll("circle.node")
.data(json.nodes)
.enter()
.append("circle")
.attr("class", "node")
.style("fill",function(d) {
// custom colours based on train type
if (d.name.match(/^S\+U/)) {
return 'rgb(215,49,58)';
}
else if (d.name.match(/^U/)) {
return 'rgb(0,114,171)';
}
else {
return 'green';
}
})
.attr("r", 12);
var nodename = svg.selectAll("text.nodename")
.data(json.nodes)
.enter()
.append("text")
.text(function(d) {return d.name});
// create the layout
var force = d3.layout.force()
.charge(-250)
.linkDistance(function(d,e) {
var distance = 1.5*parseFloat(d.distance)/10 - 5;
return (isNaN(distance)) ? 10 : distance; })
.size([width, height])
.nodes(json.nodes)
.links(json.links)
.start();
// define what to do one each tick of the animation
force.on("tick", function() {
link.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
nodename.attr("x", function(d) { return d.x - 3; })
.attr("y", function(d) { return d.y + 4; });
});
// precalculate force layout ticks for more natural appearance
var k = 0;
while ((force.alpha() > 1e-2) && (k < 40)) {
force.tick(),
k = k + 1;
}
// bind the drag interaction to the nodes
node.call(force.drag);
// misc stuff for auto resizing svg
function updateWindow(){
$("svg").width($(".site").width());
}
window.onresize = updateWindow;
if ($(".site").width() < 740) {
updateWindow();
}
});