Created
August 27, 2014 03:26
-
-
Save sebabelmar/e67fe78a9aa88b86f584 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<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