Skip to content

Instantly share code, notes, and snippets.

@sebabelmar
Created August 27, 2014 03:26
Show Gist options
  • Save sebabelmar/e67fe78a9aa88b86f584 to your computer and use it in GitHub Desktop.
Save sebabelmar/e67fe78a9aa88b86f584 to your computer and use it in GitHub Desktop.
<html>
<head>
<title> Visualize Your Demographics</title>
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<p class="display_text">Other pigglets voted:</p>
<div class="agedisplay">
<div class="infobox">
<p id="data-info"></p>
</div>
</div>
</body>
<script type="text/javascript">
// ----- BUBBLE PLOT -----
// infobox code
// this will be ran whenever we mouse over a circle
var myMouseOverFunction = function() {
var node = d3.select(this);
// show infobox div on mouseover.
// block means sorta "render on the page" whereas none would mean "don't render at all"
d3.select(".infobox").style("display", "block");
// add test to p tag in infobox
d3.select("#data-info")
.text("People at the age of " + this.__data__.age + " voted " + this.__data__.total_group_votes + " times. This age represents " + this.__data__.votes_percentage + "% of the total votes of the '" + this.__data__.group + "' group.");
}
var myMouseOutFunction = function() {
var circle = d3.select(this);
// display none removes element totally, whereas visibilty in last example just hid it
d3.select(".infobox").style("display", "none");
}
var ageWidth = 320,
ageHeight = 300,
agePadding = 1.5, // separation between same-color nodes
ageClusterPadding = 6, // separation between different-color nodes
ageMaxRadius = 50;
// what about creating a structure like this but jsonic style in order to do not perform any
// logic on the viualization side
var nodes_info = [{group_num: 2, group: "41 - 50", total_positive_votes: 35, age: 46, total_group_votes: 77}, {group_num: 2, group: "41 - 50", total_positive_votes: 42, age: 45, total_group_votes: 77}, {group_num: 4, group: "61 - :)", total_positive_votes: 56, age: 86, total_group_votes: 608}, {group_num: 4, group: "61 - :)", total_positive_votes: 71, age: 90, total_group_votes: 608}, {group_num: 0, group: "18 - 30", total_positive_votes: 5, age: 25, total_group_votes: 164}, {group_num: 3, group: "51 - 60", total_positive_votes: 49, age: 59, total_group_votes: 180}, {group_num: 4, group: "61 - :)", total_positive_votes: 72, age: 87, total_group_votes: 608}, {group_num: 1, group: "31 - 40", total_positive_votes: 70, age: 38, total_group_votes: 70}, {group_num: 4, group: "61 - :)", total_positive_votes: 50, age: 70, total_group_votes: 608}, {group_num: 4, group: "61 - :)", total_positive_votes: 64, age: 64, total_group_votes: 608}, {group_num: 0, group: "18 - 30", total_positive_votes: 69, age: 29, total_group_votes: 164}, {group_num: 3, group: "51 - 60", total_positive_votes: 65, age: 58, total_group_votes: 180}, {group_num: 3, group: "51 - 60", total_positive_votes: 66, age: 54, total_group_votes: 180}, {group_num: 4, group: "61 - :)", total_positive_votes: 39, age: 67, total_group_votes: 608}, {group_num: 4, group: "61 - :)", total_positive_votes: 73, age: 85, total_group_votes: 608}, {group_num: 4, group: "61 - :)", total_positive_votes: 68, age: 78, total_group_votes: 608}, {group_num: 0, group: "18 - 30", total_positive_votes: 32, age: 28, total_group_votes: 164}, {group_num: 0, group: "18 - 30", total_positive_votes: 58, age: 19, total_group_votes: 164}, {group_num: 4, group: "61 - :)", total_positive_votes: 62, age: 80, total_group_votes: 608}, {group_num: 4, group: "61 - :)", total_positive_votes: 53, age: 82, total_group_votes: 608}]
var counter = 0
var n = nodes_info.length, // total number of nodes
m = 5; // number of distinct clusters for us age groups
var color = d3.scale.category20()
.domain(d3.range(m));
// The largest node for each cluster.
var clusters = new Array(m);
var nodes = d3.range(n).map(function() {
var i = nodes_info[counter].group_num;
var age = nodes_info[counter].age;
var percentage = nodes_info[counter].total_positive_votes / nodes_info[counter].total_group_votes
var r = percentage * (ageMaxRadius + 1);
var d = {
age: nodes_info[counter].age,
group: nodes_info[counter].group,
total_group_votes: nodes_info[counter].total_group_votes,
votes_percentage: Math.floor(percentage * 100),
cluster: i,
radius: r,
x: Math.cos(i / m * 2 * Math.PI) * 200 + ageWidth / 2 + Math.random(),
y: Math.sin(i / m * 2 * Math.PI) * 200 + ageHeight / 2 + Math.random()
};
counter += 1
if (!clusters[i] || (r > clusters[i].radius)) clusters[i] = d;
return d
});
var force = d3.layout.force()
.nodes(nodes)
.size([ageWidth, ageHeight])
.gravity(.02)
.charge(0)
.on("tick", ageTick)
.start();
var svg = d3.select(".agedisplay").append("svg")
.attr("width", ageWidth)
.attr("height", ageHeight);
var node = svg.selectAll("circle")
.data(nodes)
.enter().append("circle")
.style("fill", function(d) { return color(d.cluster); })
.on("mouseover", myMouseOverFunction)
.on("mouseout", myMouseOutFunction)
.call(force.drag);
node.transition()
.duration(750)
.delay(function(d, i) { return i * 5; })
.attrTween("r", function(d) {
var i = d3.interpolate(0, d.radius);
return function(t) { return d.radius = i(t); };
});
function ageTick(e) {
node
.each(cluster(10 * e.alpha * e.alpha))
.each(collide(.5))
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
// Move d to be adjacent to the cluster node.
function cluster(alpha) {
return function(d) {
var cluster = clusters[d.cluster];
if (cluster === d) return;
var x = d.x - cluster.x,
y = d.y - cluster.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + cluster.radius;
if (l != r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
cluster.x += x;
cluster.y += y;
}
};
}
// Resolves collisions between d and all other circles.
function collide(alpha) {
var quadtree = d3.geom.quadtree(nodes);
return function(d) {
var r = d.radius + ageMaxRadius + Math.max(agePadding, ageClusterPadding),
nx1 = d.x - r,
nx2 = d.x + r,
ny1 = d.y - r,
ny2 = d.y + r;
quadtree.visit(function(quad, x1, y1, x2, y2) {
if (quad.point && (quad.point !== d)) {
var x = d.x - quad.point.x,
y = d.y - quad.point.y,
l = Math.sqrt(x * x + y * y),
r = d.radius + quad.point.radius + (d.cluster === quad.point.cluster ? agePadding : ageClusterPadding);
if (l < r) {
l = (l - r) / l * alpha;
d.x -= x *= l;
d.y -= y *= l;
quad.point.x += x;
quad.point.y += y;
}
}
return x1 > nx2 || x2 < nx1 || y1 > ny2 || y2 < ny1;
});
};
}
</script>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment