Skip to content

Instantly share code, notes, and snippets.

@And-How
Last active December 17, 2015 16:20
Show Gist options
  • Select an option

  • Save And-How/5638417 to your computer and use it in GitHub Desktop.

Select an option

Save And-How/5638417 to your computer and use it in GitHub Desktop.
Need help with Update/Refresh
<!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