Last active
December 14, 2015 11:19
-
-
Save And-How/5077820 to your computer and use it in GitHub Desktop.
grouped to stacked bar chart modified from http://bl.ocks.org/3943967
This file contains hidden or 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> | |
| <meta charset="utf-8"> | |
| <head> | |
| <title>Stacked and Grouped Bar with Animation</title> | |
| <!--modified from http://bl.ocks.org/3943967--> | |
| <style> | |
| body { | |
| font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; | |
| margin: auto; | |
| position: relative; | |
| width: 960px; | |
| } | |
| h1 { | |
| font-size: 40px; | |
| font-weight: 300; | |
| letter-spacing: -2px; | |
| margin: .3em 0 .1em 40px; | |
| } | |
| h2 { | |
| text-align: right; | |
| margin: 0 30px 0 0; | |
| } | |
| text { | |
| font: 20px sans-serif; | |
| } | |
| .yaxis path, | |
| .yaxis line { | |
| fill: none; | |
| stroke: black; | |
| shape-rendering: crispEdges; | |
| } | |
| .axis path, | |
| .axis line { | |
| fill: none; | |
| stroke: black; | |
| shape-rendering: crispEdges; | |
| } | |
| form { | |
| position: absolute; | |
| right: 10px; | |
| top: 10px; | |
| } | |
| legend { | |
| background-color: #fff; | |
| width: 300px; | |
| height: 60px; | |
| border: 1px solid #bbb; | |
| margin: 10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Stacked to Grouped Bar Chart</h1> | |
| <form> | |
| </form> | |
| <div> | |
| <h2>Legend</h2> <br> | |
| </div> | |
| <script src="http://d3js.org/d3.v3.js"></script> | |
| <script> | |
| d3.select(self.frameElement).style("height", "1000px"); | |
| d3.select(self.frameElement).style("width", "1000px"); | |
| var sampledata = [ | |
| [ | |
| { "employmenttype": "retail","dwellingunittype": "single-family", "scenarioname":"Base", "x": 0, "y": 9}, | |
| { "employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 29}, | |
| { "employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 30}, | |
| { "employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 35}, | |
| { "employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 10} | |
| ], | |
| [ | |
| { "employmenttype": "retail","dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 25}, | |
| { "employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 30}, | |
| { "employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 40}, | |
| { "employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 12}, | |
| { "employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 12} | |
| ], | |
| [ | |
| { "employmenttype": "retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 30}, | |
| { "employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 12}, | |
| { "employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 28}, | |
| { "employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 16}, | |
| { "employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 16} | |
| ], | |
| [ | |
| { "employmenttype": "retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 25}, | |
| { "employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 39}, | |
| { "employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 30}, | |
| { "employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 25}, | |
| { "employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 16} | |
| ], | |
| [ | |
| { "employmenttype": "retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 6}, | |
| { "employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 29}, | |
| { "employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 30}, | |
| { "employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 30}, | |
| { "employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 40} | |
| ] | |
| ]; | |
| var scenarionames = sampledata[1].map(function(d){return d.scenarioname}),//returns array of scenarioname values | |
| numberofscenarios = scenarionames.length, //number of scenarios | |
| dwellingunittypelabels = sampledata[0].map(function(d){return d.dwellingunittype}),//returns array of dwellingunittype values | |
| employmentypelabels = sampledata[0].map(function(d){return d.employmenttype}),//returns array of employmenttype values | |
| markervalue = 400; | |
| var stack = d3.layout.stack(), | |
| layers = stack(sampledata),//creates stack layers from data | |
| yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),//calculates max for grouped graph | |
| yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });//calculates max for stacked graph | |
| //sets margins | |
| var margin = {top: 40, right: 10, bottom: 20, left: 40}, | |
| width = 960 - margin.left - margin.right, | |
| height = 500 - margin.top - margin.bottom; | |
| //sets x-scale to draw bars | |
| var x = d3.scale.ordinal() | |
| .domain(d3.range(numberofscenarios)) | |
| .rangeRoundBands([30, width],.08);//sets bands for labels and ratio of groups to space between | |
| //sets x-scale for labeled axis | |
| var xlabels = d3.scale.ordinal() | |
| .domain(scenarionames) | |
| .rangeRoundBands([0, width], .08); | |
| //sets y-scale for stacked graph | |
| var y = d3.scale.linear() | |
| .domain([0, yStackMax]) | |
| .range([height, 0]); | |
| //sets y-scale for grouped graph | |
| var yGroupedScale = d3.scale.linear() | |
| .domain([0, yGroupMax]) | |
| .range([height, 0]); | |
| //sets color scale | |
| var color = d3.scale.linear() | |
| .domain([0, layers.length - 1]) | |
| .range([ "yellow","green"]); | |
| //sets x-Axis | |
| var xAxis = d3.svg.axis() | |
| .scale(xlabels) | |
| .tickSize(0) | |
| .tickPadding(6) | |
| .orient("bottom"); | |
| //sets y-axis for stacked graph | |
| var yStackedAxis = d3.svg.axis() | |
| .scale(y) | |
| .tickSize(5) | |
| .tickPadding(0) | |
| .orient("left"); | |
| //sets y-axis for grouped graph | |
| var yGroupedAxis = d3.svg.axis() | |
| .scale(yGroupedScale) | |
| .tickSize(5) | |
| .tickPadding(0) | |
| .orient("left"); | |
| //draws box to draw graph in | |
| var svg = d3.select("body").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 + ")"); | |
| //assigns color to layers | |
| var layer = svg.selectAll(".layer") | |
| .data(layers) | |
| .enter().append("g") | |
| .attr("class", "layer") | |
| .style("fill", function(d, i) { return color(i); }) | |
| .style("stroke","white") | |
| .style("stroke-width", "3"); | |
| //adding control total guide | |
| var marker = layer.selectAll("line") | |
| .data(function(d) {return d;}) | |
| .enter().append("line") | |
| .attr("x1", function(d) { return x(d.x); }) | |
| .attr("x2", function(d) { return x(d.x) + x.rangeBand(); }) | |
| .attr("y1", markervalue) | |
| .attr("y2", markervalue) | |
| .style("stroke","red") | |
| .style("stroke-width", "3"); | |
| //draws rectangles | |
| var rect = layer.selectAll("rect") | |
| .data(function(d) { return d; }) | |
| .enter().append("rect") | |
| .attr("x", function(d) { return x(d.x); }) | |
| .attr("y", height) | |
| .attr("width", x.rangeBand()) | |
| .attr("height", 0); | |
| //adds label on hover | |
| // know I should add tooltip a-la https://gist.github.com/biovisualize/1016860 | |
| rect.append("title") | |
| .attr("x", function(d) { return x(d.x); }) | |
| .attr("y", height) | |
| .text(function(d) {return d.y;}); | |
| //opening transition, draws stacked bars up from ground | |
| rect.transition() | |
| .delay(function(d, i) { return i * 10; })//staggers drawing | |
| .attr("y", function(d) { return y(d.y0 + d.y); }) | |
| .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }); | |
| //draw x-Axis | |
| svg.append("g") | |
| .attr("class", "x axis") | |
| .attr("transform", "translate(0," + height + ")") | |
| .call(xAxis); | |
| //draw Stacked y-Axis | |
| svg.append("g") | |
| .attr("class", "yaxis") | |
| .attr("transform", "translate("+ margin.left +",0)") | |
| .call(yStackedAxis) | |
| //re-draw Grouped y-Axis | |
| .transition() | |
| .delay(2000).duration(1000) | |
| .call(yGroupedAxis); | |
| //sets legend margins | |
| var legendmargin = {top: 40, right: 10, bottom: 20, left: 40}, | |
| legendwidth = 960 -30, | |
| legendheight = 200 - legendmargin.top - legendmargin.bottom; | |
| //draws legend box | |
| var legendsvg = d3.select("div").append("svg") | |
| .attr("width", legendwidth) | |
| .attr("height", legendheight) | |
| .append("g"); | |
| //sets data for legends | |
| var legend = legendsvg.selectAll("legend") | |
| .data(employmentypelabels.slice()) //copies scenarionames array in reverse order | |
| .enter().append("g") | |
| .attr("class", "legend") | |
| .attr("transform", function(d,i) { return "translate(0," + i * 20 + ")"; });//20 pixel between boxes 'i' is iterator | |
| //draws colored boxes | |
| legend.append("rect")//ads rectangles to legend svg. | |
| .data(layers) | |
| .attr("x", legendwidth - 20) //18 pixel boxes (2 pixels between) | |
| .attr("width", 20) | |
| .attr("height", 20) | |
| .style("fill", function(d, i) { return color(i); })//fills with corresponding color | |
| .style("stroke","white") | |
| .style("stroke-width", "3"); | |
| //adds legend labels | |
| legend.append("text") | |
| .attr("x", legendwidth - 24) // adds text 24 pixels away from end of svg | |
| .attr("y", 9) | |
| .attr("dy", ".35em") | |
| .style("text-anchor", "end") //right adjust | |
| .text(function(d) { return d; }); //fills text with name from scenarionames array | |
| var button = d3.selectAll("form") | |
| .append("label") | |
| .append("input") | |
| .attr("type", "radio") | |
| .attr("name","mode") | |
| .attr("value", "grouped") | |
| .text("grouped"); | |
| var button2 = d3.selectAll("form").append("label") | |
| .append("input") | |
| .attr("type", "radio") | |
| .attr("name","mode") | |
| .attr("value", "stacked") | |
| .text("stacked"); | |
| //button response | |
| d3.selectAll("input").on("change", change);//input changes on change event | |
| var timeout = setTimeout(function() { | |
| d3.select("input[value=\"grouped\"]").property("checked", true).each(change); | |
| }, 2000); | |
| function change() { | |
| clearTimeout(timeout); | |
| if (this.value === "grouped") transitionGrouped();//if grouped is checked transition to grouped | |
| else transitionStacked();//otherwise | |
| } | |
| function transitionGrouped() { | |
| y.domain([0, yGroupMax]);//sets to grouped domain (rather than stacked) | |
| rect.transition()//bartransition | |
| .duration(500) | |
| .delay(function(d, i) { return i * 10; }) | |
| .attr("x", function(d, i, j) { | |
| return x(d.x) + x.rangeBand() / layers.length * j; }) | |
| .attr("width", x.rangeBand() / layers.length) | |
| .transition() | |
| .attr("y", function(d) { return y(d.y); }) | |
| .attr("height", function(d) { return height - y(d.y); }); | |
| svg.select(".yaxis")//y-axis transition | |
| .transition() | |
| .duration(500) | |
| .call(yGroupedAxis); | |
| } | |
| function transitionStacked() { | |
| y.domain([0, yStackMax]); | |
| rect.transition() | |
| .duration(500) | |
| .delay(function(d, i) { return i * 10; }) | |
| .attr("y", function(d) { return y(d.y0 + d.y); }) | |
| .attr("height", function(d) { return y(d.y0) - y(d.y0 + d.y); }) | |
| .transition() | |
| .attr("x", function(d) { return x(d.x); }) | |
| .attr("width", x.rangeBand()); | |
| svg.select(".yaxis") | |
| .transition() | |
| .duration(500) | |
| .call(yStackedAxis); | |
| } | |
| </script> | |
| </body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment