|
<html> |
|
<head> |
|
<style> |
|
body { |
|
margin: 0; |
|
} |
|
.node circle { |
|
stroke: #3a403d; |
|
stroke-width: .5px; |
|
opacity: .9; |
|
} |
|
.voronoi path { |
|
stroke: #fff; |
|
stroke-width: 2px; |
|
fill: #333; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script> |
|
var width = window.innerWidth, height = window.innerHeight, sizeDivisor = 100, nodePadding = 2.5; |
|
|
|
var svg = d3.select("body") |
|
.append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
var color = d3.scaleOrdinal(["#66c2a5", "#fc8d62", "#8da0cb", "#e78ac3", "#a6d854", "#ffd92f", "#e5c494", "#b3b3b3"]); |
|
|
|
var simulation = d3.forceSimulation() |
|
.force("forceX", d3.forceX().strength(.1).x(width * .5)) |
|
.force("forceY", d3.forceY().strength(.1).y(height * .5)) |
|
.force("center", d3.forceCenter().x(width * .5).y(height * .5)); |
|
|
|
var voronoi = d3.voronoi() |
|
.extent([[0, 0], [width, height]]) |
|
.x(function(d) { return d.x; }) |
|
.y(function(d) { return d.y; }); |
|
|
|
var voronoiGroup = svg.append("g") |
|
.attr("class", "voronoi"); |
|
|
|
d3.csv("data.csv", types, function(error,graph){ |
|
if (error) throw error; |
|
|
|
// sort the nodes so that the bigger ones are at the back |
|
graph = graph.sort(function(a,b){ return b.size - a.size; }); |
|
|
|
//update the simulation based on the data |
|
simulation |
|
.nodes(graph) |
|
.force("charge", d3.forceManyBody().strength(-15)) |
|
.force("collide", d3.forceCollide().strength(.5).radius(function(d){ return d.radius + nodePadding; }).iterations(1)) |
|
.on("tick", function(d){ |
|
|
|
node |
|
.attr("cx", function(d){ return d.x; }) |
|
.attr("cy", function(d){ return d.y; }); |
|
|
|
voronoi |
|
.x(function(d) { return d.x; }) |
|
.y(function(d) { return d.y; }); |
|
|
|
voronoiGroup.selectAll("path") |
|
.data(voronoi(graph).polygons()) |
|
.enter().append("path"); |
|
|
|
voronoiGroup.selectAll("path") |
|
.attr("d", function(d) { return d ? "M" + d.join("L") + "Z" : null; }); |
|
|
|
}); |
|
|
|
|
|
var node = svg.append("g") |
|
.attr("class", "node") |
|
.selectAll("circle") |
|
.data(graph) |
|
.enter().append("circle") |
|
.attr("r", function(d) { return d.radius; }) |
|
.attr("fill", function(d) { return color(d.continent); }) |
|
.attr("cx", function(d){ return d.x; }) |
|
.attr("cy", function(d){ return d.y; }) |
|
.call(d3.drag() |
|
.on("start", dragstarted) |
|
.on("drag", dragged) |
|
.on("end", dragended)) |
|
.on("mouseover", function(d){ |
|
d3.select(this) |
|
.style("opacity", 1); |
|
}) |
|
.on("mouseout", function(d){ |
|
d3.selectAll(".node circle") |
|
.style("opacity", .9) |
|
}); |
|
|
|
}); |
|
|
|
function dragstarted(d) { |
|
if (!d3.event.active) simulation.alphaTarget(.03).restart(); |
|
d.fx = d.x; |
|
d.fy = d.y; |
|
} |
|
|
|
function dragged(d) { |
|
d.fx = d3.event.x; |
|
d.fy = d3.event.y; |
|
} |
|
|
|
function dragended(d) { |
|
if (!d3.event.active) simulation.alphaTarget(.03); |
|
d.fx = null; |
|
d.fy = null; |
|
} |
|
|
|
function types(d){ |
|
d.gdp = +d.gdp; |
|
d.size = +d.gdp / sizeDivisor; |
|
d.size < 3 ? d.radius = 3 : d.radius = d.size; |
|
return d; |
|
} |
|
</script> |
|
</body> |
|
</html> |