Tor is a program for anonymising one’s internet traffic. It does this through funnelling traffic through a number of Tor relays. Data is based on a snapshot from Onionoo on the 23rd of May.
I wanted to have a look at these relays and map out their geographic distribution. Using d3.js and the d3.js hexbin plugin, I grouped the relays by location, with the size of the hexbin representing the number or relays. I then coloured the relays by speed, with redder hexbin representing groups with faster relays.
As slow Tor relays are generally due to people hosting them on their own home network, a dark red cluster would generally represent a set of dedicated servers running Tor. It is interesting to see that Athens runs mainly fast Tor relays, and also the speed and number of relays in Denver.
var width = 740,
height = 550;
var projection = d3.geo.mercator()
.translate([375, 330])
.scale(160)
var color = d3.scale.linear()
.domain([0, 1500000])
.range(["#ffcc00", "red"])
.interpolate(d3.interpolateLab);
var path = d3.geo.path()
.projection(projection);
var radius = d3.scale.sqrt()
.domain([0, 100])
.range([0, 2]);
var hexbin = d3.hexbin()
.size([width, height])
.radius(2);
var graticule = d3.geo.graticule();
var svg = d3.select("svg")
.style("display", "block")
.attr("width", width)
.attr("height", height)
.call(d3.behavior.zoom()
.on("zoom", redraw))
.append("g")
function redraw() {
svg.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
queue()
.defer(d3.json, "/data/world-50m.json")
.defer(d3.json, "/data/relays-23-may.json")
.await(ready);
svg.append("path")
.datum(graticule)
.attr("class", "graticule")
.attr("d", path);
function ready(error, world, details) {
var relays = details.relays;
var ry = []
relays.forEach(function(r) {
r.proj = projection([r[0],r[1]]);
ry.push({
"0": r.proj[0],
"1": r.proj[1],
"observed_bandwidth": r[2]
})
});
svg.insert("path", ".graticule")
.datum(topojson.feature(world, world.objects.land))
.attr("class", "land")
.attr("d", path);
svg.insert("path", ".graticule")
.datum(topojson.mesh(world, world.objects.countries, function(a, b) { return a !== b; }))
.attr("class", "boundary")
.attr("d", path);
svg.append("g")
.attr("class", "hexagons")
.selectAll("path")
.data(hexbin(ry))
.enter().append("path")
.attr("d",function(d) { return hexbin.hexagon(radius(d.length)); })
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.style("fill", function(d) { return color(d3.median(d, function(d) { return d.observed_bandwidth})); })
}