Created
September 8, 2011 19:03
-
-
Save kforeman/1204344 to your computer and use it in GitHub Desktop.
treemap transitions
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
<!DOCTYPE html> | |
<html> | |
<head> | |
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"> | |
<title>Treemap</title> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js"></script> | |
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.layout.js"></script> | |
<script type="text/javascript" src="http://simiantics.net/d3/lib/jquery/jquery.min.js"></script> | |
<script type="text/javascript" src="http://simiantics.net/d3/lib/jquery-ui/jquery-ui.min.js"></script> | |
<style type="text/css"> @import url("http://simiantics.net/d3/lib/jquery-ui/jquery-ui.css"); text { font-family: sans-serif; } </style> | |
<script type="text/javascript" src="http://simiantics.net/treemap_USA.js"></script> | |
</head> | |
<body> | |
<script type="text/javascript"> | |
// setup initial params | |
var w = 800, | |
h = 500, | |
min_labeled_cf = .01, | |
max_color_cf = .3, | |
ysa = '00F30', | |
change_duration = 500; | |
// age sliders | |
age_title = d3.select('body') | |
.append('div') | |
.style ('position', 'absolute') | |
.style ('left', '10px') | |
.style ('top', '5px') | |
.style ('width', '100px') | |
.append('span') | |
.text ('Age') | |
.style ('font-family', 'sans-serif') | |
.style ('font-weight', 'bold'); | |
// play button for age slider | |
age_play = d3.select('body') | |
.append('div') | |
.style ('position', 'absolute') | |
.style ('left', '50px') | |
.style ('top', '5px') | |
.style ('width', '20px') | |
.append('img') | |
.attr ('src', 'http://simiantics.net/play.png') | |
.attr ('alt', 'Play') | |
.attr ('onclick', 'play_age()'); | |
// a div to contain the slider | |
age_slider_div = d3.select('body') | |
.append('div') | |
.attr ('id', 'age_slider') | |
.style ('position', 'absolute') | |
.style ('left', '85px') | |
.style ('top', '7px') | |
.style ('width', '500px'); | |
// the age slider itself | |
var age = 'E', display_age = '30 to 34', slider_age = 30; | |
$('#age_slider').slider({ | |
min: -15, | |
max: 80, | |
value: slider_age, | |
width: '100px', | |
animate: true, | |
step: 5, | |
slide: function(event, ui) { refresh_age(ui.value); } | |
}); | |
// text to indicate the currently selected age | |
age_label = d3.select('body') | |
.append('div') | |
.attr ('id', 'age_label') | |
.style ('position', 'absolute') | |
.style ('left', '605px') | |
.style ('top', '5px') | |
.style ('width', '125px') | |
.append('span') | |
.text (display_age) | |
.style ('font-family', 'sans-serif') | |
.style ('font-weight', 'bold'); | |
// function to update the plot when a new age is selected | |
function refresh_age(a) { | |
// change the slider age to reflect whatever was just picked | |
slider_age = a; | |
// update the display ages to match what the slider is set to (again accounting for weirdness in the age cutpoints) | |
switch(slider_age) { | |
case -15: age = 'E'; display_age = 'Early Neonatal'; break; | |
case -10: age = 'L'; display_age = 'Late Neonatal'; break; | |
case -5: age = 'P'; display_age = 'Post Neonatal'; break; | |
case 0: age = '1'; display_age = '1 to 4'; break; | |
case 80: age = slider_age; display_age = '80 plus'; break; | |
default: age = slider_age; display_age = slider_age + ' to ' + (slider_age + 4); break; | |
} | |
// update the text specifying the current age | |
age_label.text (display_age); | |
// refresh the tree | |
ysa = ysa.substr(0,3) + age | |
change_ysa(ysa); | |
} | |
// function to step through age groups if the play button is clicked | |
var age_timer = undefined; | |
function play_age() { | |
// if the map is already playing, then stop it | |
if (age_timer) stop_age(); | |
else { | |
// if we're at the last age group, start again at the beginning | |
if (slider_age == 80) { | |
slider_age = -15; | |
refresh_age(slider_age) | |
$('#age_slider').slider('value', slider_age); | |
} | |
// change the play button into a stop button | |
age_play.transition().attr ('src', 'http://simiantics.net/stop.png'); | |
// increment the age groups | |
age_timer = setInterval(function() { | |
// stop if we've reached the last age group | |
if (slider_age >= 80) stop_age(); | |
else { | |
// deal with awkward age groups again | |
switch(age) { | |
case 0: age = .01; slider_age = -10; break; | |
case .01: age = .1; slider_age = -5; break; | |
case .1: age = 1; slider_age = 0; break; | |
case 1: age = 5; slider_age = 5; break; | |
default: age = age+5; slider_age = slider_age+5; break; | |
} | |
} | |
// update the slider | |
$('#age_slider').slider('value', slider_age); | |
// update the map as though the slider were moved | |
refresh_age(slider_age); | |
// increment every 1 second | |
}, 1000); | |
} | |
} | |
// function to stop looping through ages | |
function stop_age() { | |
// clear the timer | |
clearInterval(age_timer); | |
age_timer = undefined; | |
// change the stop button back into a play button | |
age_play.transition().attr ('src', 'http://simiantics.net/play.png'); | |
} | |
// year sliders | |
year_title = d3.select('body') | |
.append('div') | |
.style ('position', 'absolute') | |
.style ('left', '10px') | |
.style ('top', '30px') | |
.style ('width', '100px') | |
.append('span') | |
.text ('Year') | |
.style ('font-family', 'sans-serif') | |
.style ('font-weight', 'bold'); | |
// play button for year slider | |
year_play = d3.select('body') | |
.append('div') | |
.style ('position', 'absolute') | |
.style ('left', '50px') | |
.style ('top', '30px') | |
.style ('width', '20px') | |
.append('img') | |
.attr ('src', 'http://simiantics.net/play.png') | |
.attr ('alt', 'Play') | |
.attr ('onclick', 'play_year()'); | |
// a div to contain the slider | |
year_slider_div = d3.select('body') | |
.append('div') | |
.attr ('id', 'year_slider') | |
.style ('position', 'absolute') | |
.style ('left', '85px') | |
.style ('top', '32px') | |
.style ('width', '500px'); | |
// the year slider itself | |
var year = '90', slider_year = 2000; | |
$('#year_slider').slider({ | |
min: 1980, | |
max: 2010, | |
value: slider_year, | |
width: '100px', | |
animate: true, | |
step: 1, | |
slide: function(event, ui) { refresh_year(ui.value); } | |
}); | |
// text to indicate the currently selected year | |
year_label = d3.select('body') | |
.append('div') | |
.attr ('id', 'year_label') | |
.style ('position', 'absolute') | |
.style ('left', '605px') | |
.style ('top', '30px') | |
.style ('width', '125px') | |
.append('span') | |
.text (slider_year) | |
.style ('font-family', 'sans-serif') | |
.style ('font-weight', 'bold'); | |
// function to update the plot when a new year is selected | |
function refresh_year(y) { | |
// change the slider year to reflect whatever was just picked | |
slider_year = y; | |
// update the display years to match what the slider is set to (again accounting for weirdness in the year cutpoints) | |
year = (slider_year+'').substr(2,2) | |
// update the text specifying the current age | |
year_label.text (slider_year); | |
// refresh the tree | |
ysa = year + ysa.substr(2,3) | |
change_ysa(ysa); | |
} | |
// function to step through year groups if the play button is clicked | |
var year_timer = undefined; | |
function play_year() { | |
// if the map is already playing, then stop it | |
if (year_timer) stop_year(); | |
else { | |
// if we're at the last year group, start again at the beginning | |
if (slider_year == 2010) { | |
slider_year = 1980; | |
refresh_year(slider_year) | |
$('#year_slider').slider('value', slider_year); | |
} | |
// change the play button into a stop button | |
year_play.transition().attr ('src', 'http://simiantics.net/stop.png'); | |
// increment the year groups | |
year_timer = setInterval(function() { | |
// stop if we've reached the last year group | |
if (slider_year >= 2010) stop_year(); | |
else slider_year++; | |
// update the slider | |
$('#year_slider').slider('value', slider_year); | |
// update the map as though the slider were moved | |
refresh_year(slider_year); | |
// increment every 1 second | |
}, 1000); | |
} | |
} | |
// function to stop looping through years | |
function stop_year() { | |
// clear the timer | |
clearInterval(year_timer); | |
year_timer = undefined; | |
// change the stop button back into a play button | |
year_play.transition().attr ('src', 'http://simiantics.net/play.png'); | |
} | |
// color interpolators for red/blue/green | |
reds = d3.interpolate(d3.rgb(252,174,145), d3.rgb(165,15,21)); | |
blues = d3.interpolate(d3.rgb(189,215,231), d3.rgb(8,81,156)); | |
greens = d3.interpolate(d3.rgb(186,228,179), d3.rgb(0,109,44)); | |
// scale CF for coloring | |
color_scale = d3.scale.sqrt().domain([0,max_color_cf]).clamp(true); | |
// function to color based on category | |
function color(d) { | |
switch(d.name.substr(0,1)) { | |
case 'A': | |
return reds(color_scale(d[ysa])); | |
break; | |
case 'B': | |
return blues(color_scale(d[ysa])); | |
break; | |
case 'C': | |
return greens(color_scale(d[ysa])); | |
break; | |
default: | |
return 'white'; | |
break; | |
} | |
} | |
// make the treemap layout | |
var treemap = d3.layout.treemap() | |
.size([w+1, h+1]) | |
.value(function(d) { return d[ysa]; }) | |
.padding(1) | |
.sticky(true); | |
// create the visualization area | |
var vis = d3.select('body') | |
.append('div') | |
.attr ('id', 'vis') | |
.style ('position', 'absolute') | |
.style ('top', '50px') | |
.append('svg:svg') | |
.style('width', w) | |
.style('height', h) | |
.append('svg:g') | |
.attr('transform', 'translate(-.5, -.5)'); | |
// make cells for each cause | |
var cell = vis.selectAll('g') | |
.data(treemap.nodes(USA)) | |
.enter().append('svg:g') | |
.attr('class', 'cell') | |
.attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; }); | |
// add a rectangle to each cell | |
var rect = cell.append('svg:rect') | |
.attr('width', function(d) { return d.dx; }) | |
.attr('height', function(d) { return d.dy; }) | |
.attr('stroke', 'white') | |
.attr('stroke-width', function(d) { return d.depth == 0 ? 0 : 1; }) | |
.style('fill', function(d) { return d.children ? 'white' : color(d); }); | |
// label the cells | |
label_sizes = d3.interpolate(8,30); | |
label_sizer = d3.scale.sqrt().domain([min_labeled_cf, max_color_cf]).clamp(true); | |
labels = cell.append('svg:text') | |
.attr('x', function(d) { return d.dx/2; }) | |
.attr('y', function(d) { return d.dy/2; }) | |
.attr('font-size', function(d) { return label_sizes(label_sizer(d[ysa])); }) | |
.attr('text-anchor', 'middle') | |
.attr('alignment-baseline', 'middle') | |
.text(function(d) { return d.children ? null : (d[ysa] > min_labeled_cf ? d.short : null); }); | |
cell.append('svg:title') | |
.text(function(d) { return d.children ? null : d.desc; }); | |
// function to change the year-sex-age (ysa) | |
function change_ysa(new_ysa) { | |
ysa = new_ysa; | |
cell.data(treemap.value(function(d) { return d[ysa]; })); | |
refreshTree(); | |
} | |
// function to change the country | |
var iso3 = 'USA', | |
loaded = {}; | |
loaded['USA'] = 1; | |
function change_country(new_iso3) { | |
iso3 = new_iso3; | |
if (!loaded[iso3]) { | |
var newdata = document.createElement('script'); | |
newdata.setAttribute('type', 'text/javascript'); | |
newdata.setAttribute('src', 'data/treemap_' + iso3 + '.js'); | |
document.getElementsByTagName('head')[0].appendChild(newdata); | |
} | |
cell.data(treemap.nodes(iso3)); | |
change_ysa(ysa); | |
} | |
// function to resize the treemap | |
function refreshTree() { | |
cell.transition().ease('linear').duration(change_duration).attr('transform', function(d) { return 'translate(' + d.x + ',' + d.y + ')'; }); | |
rect.transition().ease('linear').duration(change_duration).attr('width', function(d) { return d.dx; }).attr('height', function(d) { return d.dy; }).style('fill', function(d) { return d.children ? 'white' : color(d); }); | |
labels.transition().ease('linear').duration(change_duration).attr('x', function(d) { return d.dx/2; }).attr('y', function(d) { return d.dy/2; }).attr('font-size', function(d) { return label_sizes(label_sizer(d[ysa])); }).text(function(d) { return d.children ? null : (d[ysa] > min_labeled_cf ? d.short : null); });; | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment