Last active
December 17, 2015 16:20
-
-
Save And-How/5638417 to your computer and use it in GitHub Desktop.
Need help with Update/Refresh
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; | |
| width: 960px; | |
| } | |
| h2 { | |
| text-align: right; | |
| margin: 0 30px 0 0; | |
| } | |
| text { | |
| font: 20px sans-serif; | |
| } | |
| .axis path, .axis line { | |
| fill: none; | |
| stroke: black; | |
| shape-rendering: crispEdges; | |
| } | |
| form { | |
| right: 10px; | |
| top: 10px; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <h1>Stacked to Grouped Bar Chart</h1> | |
| <form> | |
| <label><input type="radio" name="mode" value="grouped"> Grouped</label> | |
| <label><input type="radio" name="mode" value="stacked" checked> Stacked</label> | |
| </form> | |
| <div> | |
| <h2>Legend</h2> <br> | |
| </div> | |
| <input name="updateButton" | |
| type="button" | |
| value="Update" | |
| onclick="updateData()"/> | |
| <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 =[ | |
| [ | |
| { "markervalue": 10,"employmenttype":"retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": -5}, | |
| { "markervalue": 10,"employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 29}, | |
| { "markervalue": 10,"employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 30}, | |
| { "markervalue": 10,"employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 35}, | |
| { "markervalue": 10,"employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 10} | |
| ], | |
| [ | |
| { "markervalue": 10,"employmenttype": "retail","dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 25}, | |
| { "markervalue": 10,"employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 30}, | |
| { "markervalue": 10,"employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 40}, | |
| { "markervalue": 10,"employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 12}, | |
| { "markervalue": 10,"employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 12} | |
| ], | |
| [ | |
| { "markervalue": 10,"employmenttype": "retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 30}, | |
| { "markervalue": 10,"employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 12}, | |
| { "markervalue": 10,"employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 28}, | |
| { "markervalue": 10,"employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 16}, | |
| { "markervalue": 10,"employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 16} | |
| ], | |
| [ | |
| { "markervalue": 10,"employmenttype": "retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 25}, | |
| { "markervalue": 10,"employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 39}, | |
| { "markervalue": 10,"employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": -30}, | |
| { "markervalue": 10,"employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 25}, | |
| { "markervalue": 10,"employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 16} | |
| ], | |
| [ | |
| { "markervalue": 10,"employmenttype": "retail", "dwellingunittype": "single-family","scenarioname":"Base", "x": 0, "y": 6}, | |
| { "markervalue": 10,"employmenttype": "office","dwellingunittype": "attached single-family","scenarioname":"Scenario 1", "x": 1, "y": 29}, | |
| { "markervalue": 10,"employmenttype": "industrial","dwellingunittype": "detached single-family","scenarioname":"Scenario 2","x": 2, "y": 30}, | |
| { "markervalue": 10,"employmenttype": "public","dwellingunittype": "multifamily","scenarioname":"Scenario 3","x": 3, "y": 30}, | |
| { "markervalue": 10,"employmenttype": "civic","dwellingunittype": "yurt","scenarioname":"Scenario 4","x": 4, "y": 40} | |
| ] | |
| ]; | |
| var scenarionames = sampledata[0].map(function(d){return d.scenarioname}),//returns array of scenarionames | |
| employmentypelabels = sampledata[0].map(function(d){return d.employmenttype});//returns array of employmenttypes | |
| var stack = d3.layout.stack(), | |
| layers = stack(sampledata), | |
| yStackMax = layers.extent;//calculates max for stacked graph | |
| barStack(sampledata);//Modified from http://bl.ocks.org/ZJONSSON/2979974 | |
| //barGroup(layers); | |
| //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(scenarionames.length)) | |
| .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); | |
| //for grouped | |
| xGroup = d3.scale.ordinal() | |
| .domain(d3.range(sampledata.length)) | |
| .rangeBands([0, x.rangeBand()]); | |
| //sets y-scale for stacked graph | |
| var y = d3.scale.linear() | |
| //.domain(layers.extent) | |
| .domain([returnDomain().min, returnDomain().max])//uses returndomain for negative values | |
| .range([height, 0]); | |
| //sets y-scale for grouped graph | |
| var yGroupedScale = d3.scale.linear() | |
| //.domain([-yGroupMax, yGroupMax]) | |
| .domain(layers.extent) | |
| .range([height, 0]); | |
| var yPercentScale = d3.scale.linear() | |
| .domain([0,1]) | |
| .range([height, 0]); | |
| //sets color scale | |
| var color = d3.scale.linear() | |
| .domain([0, layers.length - 1]) | |
| .range(["green", "yellow"]); | |
| //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); }); | |
| //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); | |
| function render() { | |
| rect.data(function(d) { return d; }) | |
| .enter().append("rect") | |
| .on("click", clickEvent) | |
| .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) | |
| //.attr("y", height) | |
| .text(function(d) {return d.y;}); | |
| openingTransition(); | |
| //opening transition, draws stacked bars up from ground | |
| function openingTransition() { | |
| rect.transition() | |
| .delay(function(d, i) { return i * 100; })//staggers drawing | |
| .attr("y", function(d) { return y(d.y0); }) | |
| .attr("height", function(d) { return y(0)-y(d.size)}) | |
| .attr("width",x.rangeBand()); | |
| //draw x-Axis | |
| svg.append("g") | |
| .attr("class", "axis x") | |
| .attr("transform","translate (0 "+y(0)+")") | |
| .call(xAxis); | |
| //draw Stacked y-Axis | |
| svg.append("g") | |
| .attr("class", "axis y") | |
| .attr("transform", "translate("+ margin.left +",0)") | |
| .call(yStackedAxis); | |
| } | |
| //adding control total guide | |
| var controlMarker = 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", 100) | |
| .attr("y2", 100) | |
| .style("stroke","black") | |
| .style("stroke-width", "3"); | |
| //sets legend margins | |
| var | |
| legendmargin = {top: 10, right: 10, bottom: 20, left: 40}, | |
| legendwidth = 960 -30, | |
| legendheight = 150 - 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"); | |
| text = 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 | |
| //button response | |
| d3.selectAll("input").on("change", change);//input changes on change event | |
| var timeout = setTimeout(function() { | |
| d3.select("input[value=\"stacked\"]").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() { | |
| barGroup(layers); | |
| svg.selectAll("layers") | |
| .attr("transform", function (d, i) { return "translate(" + xGroup(i) + ")" ; }); | |
| 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("y",function(d,i) { return y(d.y0);}) | |
| .attr("width", function(d,i) { return xGroup.rangeBand(); }) | |
| .attr("height",function(d,i) { return (y(0)-y(d.size)); }) | |
| .style("stroke","white") | |
| .style("stroke-width", "3") | |
| svg.append("g") | |
| .attr("class", "axis y") | |
| .attr("transform", "translate("+ margin.left +",0)") | |
| .call(yGroupedAxis); | |
| } | |
| function transitionStacked() { | |
| y.domain(layers.extent); | |
| ///* Reposition and redraw axis | |
| svg.select("axis x") | |
| .attr("transform","translate (0 "+y(0)+")") | |
| .call(xAxis.orient("bottom")); | |
| svg.select("axis y") | |
| .attr("transform","translate ("+x(0)+" 0)") | |
| .call(yStackedAxis.orient("left")); | |
| ///* Reposition the elements | |
| svg.selectAll("rect") | |
| .attr("x",function(d,i) { return x(i); }) | |
| .attr("y",function(d) { return y(d.y0)}) | |
| .attr("height",function(d) { return y(0)-y(d.size)}) | |
| .attr("width", function(d,i) { return xlabels.rangeBand(); }) | |
| .attr("transform", function (d, i) { return "translate(0)" ; }); | |
| } | |
| /* | |
| updateData: function() { | |
| this.set('updating',true); | |
| // Get the new data | |
| var sampledata = sampledata1; | |
| // Update the seriesSet to the new data | |
| this.getPath('chartView.seriesSet').data(sampledata); | |
| this.set('updating',false); | |
| this.getPath('chartView').openingTransition() | |
| }, | |
| */ | |
| function clickEvent(d, i){ | |
| var usernumber = prompt("Please enter the value", d);//dialog box defaults to d | |
| sampledata[i] = + usernumber; //||https://github.com/mbostock/d3/wiki/CSV#wiki-parse | |
| //parseInt(op, 10);//radix (base) 10 | |
| render(); | |
| } | |
| //Modified from http://bl.ocks.org/ZJONSSON/2979974 | |
| function barStack(d) { | |
| var l = d[0].length | |
| while (l--) { | |
| var posBase = 0, negBase = 0; | |
| d.forEach(function(d) { | |
| d=d[l] | |
| d.size = Math.abs(d.y) | |
| if (d.y<0) { | |
| d.y0 = negBase | |
| negBase-=d.size | |
| } else | |
| { | |
| d.y0 = posBase = posBase + d.size | |
| } | |
| }) | |
| } | |
| d.extent= d3.extent(d3.merge(d3.merge(d.map(function(e) { return e.map(function(f) { return [f.y0,f.y0-f.size]})})))) | |
| return d | |
| } | |
| function barGroup(d) { | |
| var l = d[0].length | |
| while (l--) { | |
| d.forEach(function(d) { | |
| d=d[l] | |
| d.size = Math.abs(d.y); | |
| d.y0 = Math.max(0, d.y); | |
| }) | |
| } | |
| d.extent = d3.extent(d3.merge(d3.merge(d.map(function(e) { return e.map(function(f) { return [f.y0,f.y0-f.size]})})))) | |
| return d; | |
| } | |
| function returnDomain () { | |
| var domain = {}; | |
| domain.min = Math.min(d3.min(sampledata.extent),0); | |
| domain.max = Math.max(d3.max(sampledata.extent),0); | |
| return domain; | |
| } | |
| </script> | |
| </body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment