/////////////////////////////////////////////////////////////////////////// /////////////////////////////// Initiate SVG ////////////////////////////// /////////////////////////////////////////////////////////////////////////// var margin = { top: 160, right: 140, bottom: 50, left: 140 }; var width = Math.max(window.innerWidth - margin.left - margin.right, 500) - 30, height = Math.min(window.innerHeight - margin.top - margin.bottom - 100, width*2/3); //Reset the overall font size var newFontSize = Math.min(width * 62.5 / 1400, 62.5); d3.select("html").style("font-size", newFontSize + "%"); //SVG container var svg = d3.select("#chart") .append("svg") .attr("width", width + margin.left + margin.right) .attr("height", height + margin.top + margin.bottom) .append("g") .attr("transform", "translate(" + margin.left + "," + margin.top + ")"); /////////////////////////////////////////////////////////////////////////// /////////////////////////////// Append titles ///////////////////////////// /////////////////////////////////////////////////////////////////////////// var titleWrapper = svg.append("g").attr("class", "titleWrapper"); //Append title to the top titleWrapper.append("text") .attr("class", "title") .attr("x", width/2) .attr("y", -100) .style("text-anchor", "middle") .text("Mean Body Mass Index (BMI) per Country"); titleWrapper.append("text") .attr("class", "subtitle") .attr("x", width/2) .attr("y", -50) .style("text-anchor", "middle") .text("Men"); /////////////////////////////////////////////////////////////////////////// //////////////////////////////// Create axes ////////////////////////////// /////////////////////////////////////////////////////////////////////////// var axisWrapper = svg.append("g").attr("class", "axisWrapper"); var xScale = d3.scale.linear() .range([0, width]) .domain(d3.extent(bmi, function(d) { return d.year; })) .nice(); var yScale = d3.scale.linear() .range([height, 0]) .domain(d3.extent(bmi, function(d) { return d.mean_bmi; })) .nice(); var xAxis = d3.svg.axis() .scale(xScale) .orient("bottom") .tickFormat(d3.format("d")); axisWrapper.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + height + ")") .call(xAxis); var yAxis = d3.svg.axis() .scale(yScale) .orient("left"); axisWrapper.append("g") .attr("class", "y axis") .call(yAxis); /////////////////////////////////////////////////////////////////////////// ////////////////////////////// Create legend ////////////////////////////// /////////////////////////////////////////////////////////////////////////// var legendWrapper = svg.append("g").attr("class", "legendWrapper"); //Legend legendWrapper.append("text") .attr("class", "axisLegend") .attr("transform", "rotate(-90)") .attr("y", width) .attr("x", -yScale(32)) .style("fill", "#D62C2B") .text("Obese"); legendWrapper.append("text") .attr("class", "axisLegend") .attr("transform", "rotate(-90)") .attr("y", width) .attr("x", -yScale(27.5)) .style("fill", "#F7804B") .text("Overweight"); legendWrapper.append("text") .attr("class", "axisLegend") .attr("transform", "rotate(-90)") .attr("y", width) .attr("x", -yScale(22.5)) .style("fill", "#9C9C9C") .text("Normal weight"); ////////////////////////////////////////////////////////////// //////////////////////// Gradients /////////////////////////// ////////////////////////////////////////////////////////////// var defs = svg.append("defs"); linearGradient = defs.append("linearGradient") .attr("id", "gradient-bmi") .attr("gradientUnits", "userSpaceOnUse") .attr("x1", 0) .attr("y1", 0) .attr("x2", 0) .attr("y2", height); linearGradient.append("stop") .attr("class", "left") .attr("offset", yScale(30)/yScale.range()[0]) .attr("stop-color", "#D62C2B"); linearGradient.append("stop") .attr("class", "left") .attr("offset", yScale(30)/yScale.range()[0]) .attr("stop-color", "#F7804B"); linearGradient.append("stop") .attr("class", "left") .attr("offset", yScale(25)/yScale.range()[0]) .attr("stop-color", "#F7804B"); linearGradient.append("stop") .attr("class", "left") .attr("offset", yScale(25)/yScale.range()[0]) .attr("stop-color", "#BABABA"); /////////////////////////////////////////////////////////////////////////// ////////////////////////////////// Voronoi //////////////////////////////// /////////////////////////////////////////////////////////////////////////// //Initiate the voronoi function var voronoi = d3.geom.voronoi() .x(function(d) { return xScale(d.year); }) .y(function(d) { return yScale(d.mean_bmi); }) .clipExtent([[0, yScale(35)], [width, yScale(17)]]); //Prepare the data var gender = d3.nest() .key(function(d) { return d.sex; }) .entries(bmi); //Initiate the voronoi group element var voronoiGroup = svg.append("g").attr("class", "voronoiWrapper"); //Create a new voronoi map including only the visible points voronoiGroup.selectAll("path") .data(voronoi(gender[0].values)) .enter().append("path") .attr("class", function(d,i) { return "voronoi " + d.point.iso; }) //.attr("d", function(d) { return "M" + d.join("L") + "Z"; }) .datum(function(d) { return d.point; }) .attr("class", "voronoiCells") .on("mouseover", mouseoverVor) .on("mouseout", mouseoutVor); /////////////////////////////////////////////////////////////////////////// //////////////////////////////// Plot Data //////////////////////////////// /////////////////////////////////////////////////////////////////////////// var line = d3.svg.line() .interpolate("basis") .x(function(d) { return xScale(d.year); }) .y(function(d) { return yScale(d.mean_bmi); }); //Wrapper for all the lines var countriesWrapper = svg.append("g").attr("class","countryWrapper"); //Prepare the data var countries = d3.nest() .key(function(d) { return d.iso; }) .key(function(d) { return d.sex; }) .entries(bmi); //Create a group for each country var countryGroup = countriesWrapper.selectAll(".countryGroup") .data(countries, function(d) { return d.key; }) .enter().append("g") .attr("class", function(d) { return "country " + d.key; }); //Draw a line for each country countryGroup.append("path") .attr("class", "country line") //.attr("d", function(d) { return line(d.values[0].values); }) .style("stroke", "url(#gradient-bmi)"); //Append the actual text to the right of the last value for each country countryGroup.append("text") .attr("class", "countryName") .datum(function(d) { return {country: d.values[0].values[0].country, valueMen: d.values[0].values[d.values[0].values.length - 1], valueWomen: d.values[1].values[d.values[1].values.length - 1]}; }) //.attr("transform", function(d) { return "translate(" + xScale(d.valueMen.year) + "," + yScale(d.valueMen.mean_bmi) + ")"; }) .attr("x", 3) .attr("dy", ".35em") .text(function(d) { return d.country; }); /////////////////////////////////////////////////////////////////////////// ///////////////////////////// Threshold lines ///////////////////////////// /////////////////////////////////////////////////////////////////////////// countriesWrapper.append("path") .attr("class", "threshold") .attr("d", "M" + 0 + "," + yScale(25) + " L" + width + "," + yScale(25)) .style("stroke", "#F7804B"); countriesWrapper.append("path") .attr("class", "threshold") .attr("d", "M" + 0 + "," + yScale(30) + " L" + width + "," + yScale(30)) .style("stroke", "#D62C2B"); /////////////////////////////////////////////////////////////////////////// //////////////////////// Change between datasets ////////////////////////// /////////////////////////////////////////////////////////////////////////// function changeToMen() { //Change subtitle d3.select(".subtitle") .text("Men"); //Change to females d3.selectAll(".country") .transition().duration(1000) .attr("d", function(d) { return line(d.values[0].values); }); //Move labels to men d3.selectAll(".countryName") .attr("transform", function(d) { return "translate(" + xScale(d.valueMen.year) + "," + yScale(d.valueMen.mean_bmi) + ")"; }); //Create voronois setTimeout(function() { d3.selectAll(".voronoiWrapper").selectAll("path") .data(voronoi(gender[0].values)) .attr("d", function(d) { return "M" + d.join("L") + "Z"; }) .datum(function(d) { return d.point; }); },1000); }//men function changeToWomen() { //Change subtitle d3.select(".subtitle") .text("Women"); //Change to females d3.selectAll(".country") .transition().duration(1000) .attr("d", function(d) { return line(d.values[1].values); }); //Move labels to women d3.selectAll(".countryName") .attr("transform", function(d) { return "translate(" + xScale(d.valueWomen.year) + "," + yScale(d.valueWomen.mean_bmi) + ")"; }); //Create voronois setTimeout(function() { d3.selectAll(".voronoiWrapper").selectAll("path") .data(voronoi(gender[1].values)) .attr("d", function(d) { return "M" + d.join("L") + "Z"; }) .datum(function(d) { return d.point; }); },1000); }//women /////////////////////////////////////////////////////////////////////////// //////////////////////////// Button Activity ////////////////////////////// /////////////////////////////////////////////////////////////////////////// d3.select("#menButton").on("click", changeToMen); d3.select("#womenButton").on("click", changeToWomen); /////////////////////////////////////////////////////////////////////////// /////////////////////////// Mouseover functions /////////////////////////// /////////////////////////////////////////////////////////////////////////// function mouseoverVor(d) { //Dim all lines d3.selectAll(".line") .style("opacity", 0.1); //Highlight selected line d3.select(".country." + d.iso + " .line") .style("stroke-width", 5) .style("opacity", 1); //Show selected country d3.select(".country." + d.iso + " .countryName") .style("opacity", 1); //Hide legends d3.selectAll(".axisLegend") .style("opacity", 0.1); }//mouseover function mouseoutVor(d) { //Remove stylings d3.selectAll(".line, .countryName, .axisLegend") .style("opacity", null); d3.select(".country." + d.iso + " .line") .style("stroke-width", null) .style("opacity", null); }//mouseoutVor changeToMen();