|
<!DOCTYPE html> |
|
<meta charset="utf-8"> |
|
<style> |
|
|
|
body { |
|
font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; |
|
font-size: 8px; |
|
width: 960px; |
|
height: 500px; |
|
position: relative; |
|
} |
|
.link { |
|
stroke-opacity: .4; |
|
fill: none; |
|
} |
|
.total{ |
|
font-size: 14px; |
|
font-weight: bold; |
|
} |
|
|
|
.noselect { |
|
-webkit-touch-callout: none; |
|
-webkit-user-select: none; |
|
-khtml-user-select: none; |
|
-moz-user-select: none; |
|
-ms-user-select: none; |
|
user-select: none; |
|
} |
|
|
|
</style> |
|
<body> |
|
<script src="http://d3js.org/d3.v3.min.js"></script> |
|
<script src="http://d3js.org/queue.v1.min.js"></script> |
|
<script> |
|
|
|
var diameter = 800, |
|
radius = diameter / 2, |
|
innerRadius = radius - 200; |
|
|
|
var cluster = d3.layout.cluster() |
|
.size([360, innerRadius]) |
|
.sort(null) |
|
.value(function(d) { return d.size; }); |
|
|
|
var bundle = d3.layout.bundle(); |
|
|
|
var line = d3.svg.line.radial() |
|
.interpolate("bundle") |
|
.tension(.45) |
|
.radius(function(d) { return d.y; }) |
|
.angle(function(d) { return d.x / 180 * Math.PI; }); |
|
|
|
var width = d3.scale.linear().range([0,5]); |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", diameter + 100) |
|
.attr("height", diameter) |
|
.append("g") |
|
.attr("transform", "translate(" + (radius + 90) + "," + (radius - 150) + ")"); |
|
|
|
var total = svg.append("text") |
|
.attr('class', 'total') |
|
.attr("x", 130) |
|
.attr("y", 240); |
|
|
|
queue() |
|
.defer(d3.csv, "refugee_id.csv") |
|
.await(ready); |
|
|
|
function ready(error, refugee) { |
|
|
|
width.domain([0, d3.max(refugee, function (d){ return +d.value})]); |
|
|
|
var stateNest = d3.nest() |
|
.key(function(d) { return d.fips; }) |
|
.rollup(function(state) { return d3.sum(state, function (d){return d.value}); }) |
|
.entries(refugee); |
|
|
|
var countryNest = d3.nest() |
|
.key(function(d) { return d.id; }) |
|
.rollup(function(country) { return d3.sum(country, function (d){return d.value}); }) |
|
.entries(refugee); |
|
|
|
|
|
var state_nodes = refugee.map(function (d){ return {id: d.fips, name: d.destination} }); |
|
|
|
var country_uniques = refugee.map( function (d){ return d.id }).filter( onlyUnique ); |
|
var country_nodes = country_uniques.map(function (d){ |
|
var match = refugee.filter(function (f){ return f.id === d})[0]; |
|
return {id: "c" + match.id, name: match.name} |
|
}); |
|
|
|
var state_uniques = refugee.map( function (d){ return d.fips }).filter( onlyUnique ); |
|
var state_nodes = state_uniques.map(function (d){ |
|
var match = refugee.filter(function (f){ return f.fips === d})[0]; |
|
return {id: "s" + match.fips, name: match.destination} |
|
}); |
|
|
|
var struc = {name: "top", children:[ |
|
{name: "states", children: state_nodes}, |
|
{name: "countries", children: country_nodes}, |
|
]}; |
|
|
|
var nodes = cluster.nodes(struc); |
|
var links = refugee.map(function (d){ |
|
var obj = {source: getNode("c" + d.id), target: getNode("s" + d.fips)}; |
|
obj.source.value = +d.value; |
|
obj.target.value = +d.value; |
|
return obj; |
|
}); |
|
|
|
|
|
|
|
svg.selectAll(".node") |
|
.data(nodes.filter(function(n) { return !n.children; })) |
|
.enter().append("g") |
|
.attr("class", "node") |
|
.attr("transform", function(d) { return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")"; }) |
|
.append("text") |
|
.attr("class", "noselect") |
|
.attr("dx", function(d) { return d.x < 180 ? 8 : -8; }) |
|
.attr("dy", ".31em") |
|
.attr("text-anchor", function(d) { return d.x < 180 ? "start" : "end"; }) |
|
.attr("transform", function(d) { return d.x < 180 ? null : "rotate(180)"; }) |
|
.text(function(d) { return d.name; }); |
|
|
|
svg.selectAll(".link") |
|
.data(bundle(links)) |
|
.enter().append("path") |
|
.attr("class", "link") |
|
.attr("d", line) |
|
.style("stroke", function(d){ return "#2AAD94" }) |
|
.style("fill", "none") |
|
.attr("stroke-width", function(d){ return 1 }); |
|
|
|
svg.selectAll(".node").on("mouseover", function (e){ |
|
total.text(getTotal(e.id) + getName(e.id)); |
|
d3.select(this).style("font-weight", "bold"); |
|
svg.selectAll('.link').transition().duration(100) |
|
.style("stroke-opacity", function (d){ |
|
var check = d.filter(function(n) { return !n.children; }) |
|
.map(function (f){ |
|
if(f.id === e.id){ |
|
return 1; |
|
} |
|
else{ |
|
return 0; |
|
} |
|
}); |
|
return (d3.sum(check) > 0) ? 0.5 : 0.01; |
|
}); |
|
}); |
|
|
|
svg.selectAll(".node").on("mouseout", function (e){ |
|
total.text(""); |
|
d3.select(this).style("font-weight", "normal"); |
|
svg.selectAll('.link').transition().duration(100).style("stroke-opacity", 0.5); |
|
}); |
|
|
|
function getTotal(id){ |
|
var match; |
|
if(id.charAt(0) === "c"){ |
|
match = countryNest.filter(function (d){ |
|
return id.slice(1) === d.key |
|
})[0]; |
|
return match.values + " exited "; |
|
} |
|
else{ |
|
match = stateNest.filter(function (d){ |
|
return id.slice(1) === d.key |
|
})[0]; |
|
return match.values + " entered "; |
|
}; |
|
}; |
|
function getName(id){ |
|
var match; |
|
if(id.charAt(0) === "c"){ |
|
match = refugee.filter(function (d){ |
|
return id.slice(1) === d.id |
|
})[0]; |
|
return match.name; |
|
} |
|
else{ |
|
match = refugee.filter(function (d){ |
|
return id.slice(1) === d.fips |
|
})[0]; |
|
return match.destination; |
|
}; |
|
}; |
|
|
|
function getValue(id){ |
|
|
|
var match = refugee.filter(function (d){ |
|
return "c" + d.id === id; |
|
}); |
|
|
|
return match[0].value; |
|
}; |
|
|
|
function getNode(id){ |
|
return nodes.filter(function (d){ |
|
return d.id === id; |
|
})[0]; |
|
}; |
|
|
|
|
|
}; |
|
|
|
|
|
|
|
function onlyUnique(value, index, self) { |
|
return self.indexOf(value) === index; |
|
} |
|
|
|
</script> |