// Check breakpoint function breakCalc(x){ x <= 480 ? y = 'xs' : y = 'md'; return y; } var breakpoint = breakCalc($(window).width()); $(window).resize(function(){ var breakpoint = breakCalc($(window).width()); }) // change the height of the chart depending on the breakpoint function breakHeight(bp){ bp == 'xs' ? y = 250 : y = 500; return y; } // function to group by multiple properties in underscore.js _.groupByMulti = function (obj, values, context) { if (!values.length) return obj; var byFirst = _.groupBy(obj, values[0], context), rest = values.slice(1); for (var prop in byFirst) { byFirst[prop] = _.groupByMulti(byFirst[prop], rest, context); } return byFirst; }; // function to decide whether to pluralize the word "award" in the tooltip function awardPlural(x){ x == 1 ? y = 'award' : y = 'awards'; return y; } // funciton to determine the century of the datapoint when displaying the tooltip function century(x){ x<100 ? y = '19'+x : y = '20'+(x.toString().substring(1)); return y; } // function to ensure the tip doesn't hang off the side function tipX(x){ var winWidth = $(window).width(); var tipWidth = $('.tip').width(); if (breakpoint == 'xs'){ x > winWidth - tipWidth - 20 ? y = x-tipWidth : y = x; } else { x > winWidth - tipWidth - 30 ? y = x-45-tipWidth : y = x+10; } return y; } // function to create the chart function chart(column, filterBy, groupBy) { // basic chart dimensions var margin = {top: 20, right: 1, bottom: 30, left: 0}; var width = $('.chart-wrapper').width() - margin.left - margin.right; var height = breakHeight(breakpoint) - margin.top - margin.bottom; // chart top used for placing the tooltip var chartTop = $('.chart.'+groupBy+'.'+filterBy).offset().top; // tooltip var tooltip = d3.select("body") .append("div") .attr("class", "tip") .style("position", "absolute") .style("z-index", "20") .style("visibility", "hidden") .style("top", 40+chartTop+"px"); // scales: // x is a time scale, for the horizontal axis // y is a linear (quantitative) scale, for the vertical axis // z is in ordinal scale, to determine the colors (see var colorrange, below) var x = d3.time.scale() .range([0, width]); var y = d3.scale.linear() .range([height-10, 0]); // color range provided by colorbrewer // i just added a bunch of grays at the end so that the categories grouped as other all appear gray. // there's definitely a better way to do this var colorrange = ['#66c2a5','#fc8d62','#8da0cb','#e78ac3','#a6d854','#ffd92f','#e5c494','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3','#b3b3b3']; var z = d3.scale.ordinal() .range(colorrange); // the x-axis. note that the ticks are years, and we'll show every 5 years var xAxis = d3.svg.axis() .scale(x) .orient("bottom") .ticks(d3.timeYears, 5); // stacked layout. the order is reversed to get the largest value on top // if you change the order to inside-out, the streams get all mixed up and look cool // but the graph is harder to read. reversed order ensures that the streams are in the // same order as the legend, which improves readability in lieu of directly labelling // the streams (which is another programming challenge entirely) var stack = d3.layout.stack() .offset("silhouette") .order("reverse") .values(function(d) { return d.values; }) .x(function(d) { return d.date; }) .y(function(d) { return d.value; }); var nest = d3.nest() .key(function(d) { return d.key; }); // there are some ways other than "basis" to interpolate the area between data points // for example, you can use "cardinal", which makes the streams a little more wiggly. // the drawback with that approach is that if you have years where there is no data, // you won't see a flat line across the center of the chart. instead, it will look all bumpy. // ultimately, "cardinal" interpolation is more likely to give an inaccurate represenation of the data, // which is anyway a danger with any type of interpolation, including "basis" var area = d3.svg.area() .interpolate("basis") .x(function(d) { return x(d.date); }) .y0(function(d) { return y(d.y0)-.2; }) // -.2 to create a little space between the layers .y1(function(d) { return y(d.y0 + d.y)+.2; }); // +.2, likewise var svg = d3.select(".chart."+groupBy+'.'+filterBy).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 + ")"); // generate a legend function legend(layers){ // generate the legend title function titler(filter,group){ if (group == 'place') { if (filter == 'india'){ return "State"; } else { return "Country"; } } } $('.chart.'+groupBy+'.'+filterBy).prepend('