Skip to content

Instantly share code, notes, and snippets.

@maritrinez
Created May 25, 2017 16:51
Show Gist options
  • Save maritrinez/c3983e5e04ac7b9f18b030dcbd77fb8c to your computer and use it in GitHub Desktop.
Save maritrinez/c3983e5e04ac7b9f18b030dcbd77fb8c to your computer and use it in GitHub Desktop.
Chart in table
{
"title": "Number of activities per type",
"breadcrumb": [
"Conferences and congresses",
"Internacionalization visitors"
],
"legend": {
"mobility": {
"color": "#A8DBA8",
"text": "Internacionalization mobility program"
},
"visitors": {
"color": "#79BD9A",
"text": "Internacionalization visitors program"
},
"residents": {
"color": "#3B8686",
"text": "Residences"
},
"other": {
"color": "#0B486B",
"text": "Others"
}
},
"data": [
{
"scope": "All",
"pices": {
"mobility": 476,
"visitors": 111,
"residents": 16,
"other": 183
}
},
{
"scope": "Cinema",
"pices": {
"mobility": 70,
"visitors": 24,
"residents": 0,
"other": 10
}
},
{
"scope": "Performing Arts",
"pices": {
"mobility": 94,
"visitors": 19,
"residents": 2,
"other": 9
}
},
{
"scope": "Music",
"pices": {
"mobility": 107,
"visitors": 22,
"residents": 0,
"other": 7
}
},
{
"scope": "Visual Arts",
"pices": {
"mobility": 89,
"visitors": 12,
"residents": 9,
"other": 47
}
},
{
"scope": "Literature",
"pices": {
"mobility": 62,
"visitors": 22,
"residents": 3,
"other": 25
}
},
{
"scope": "Exhibitions",
"pices": {
"mobility": 43,
"visitors": 2,
"residents": 2,
"other": 74
}
},
{
"scope": "Conferences and congresses",
"pices": {
"mobility": 11,
"visitors": 10,
"residents": 0,
"other": 11
}
}
]
}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<!-- fonts -->
<link href='http://fonts.googleapis.com/css?family=Share+Tech' rel='stylesheet' type='text/css'>
<!-- stylesheets -->
<link rel="stylesheet" href="main.css">
<!-- libraries -->
<script src='http://d3js.org/d3.v3.min.js'></script>
<script src='https://cdnjs.cloudflare.com/ajax/libs/d3-legend/1.3.0/d3-legend.min.js'></script>
</head>
<body>
<div id="stacked"></div>
<script src="main.js"></script>
</body>
</html>
body {
font-family: 'Share Tech', sans-serif;
}
.stacked_tooltip {
top: 0;
right: 0;
position: absolute;
padding: 5px;
line-height: 140%;
width: 150px;
height: 50px;
font-size: 80%;
background: #FAF7F5;
border: 1px solid #424242;
border-radius: 5px;
pointer-events: none;
text-align: right;
}
.stacked_tooltip strong {
font-size: 120%;
font-weight: bold;
}
.label {
font-size: 80%
}
var chartWidth = parseInt(d3.select('#stacked').style('width'), 10);
var margin = {top: 50, right: 100, bottom: 150, left: 100},
width = chartWidth - margin.left - margin.right,
height = (chartWidth / 2) - margin.top - margin.bottom;
var formatPercent = d3.format("%");
var titleSize = '180%';
var tableLabels = ['Type', 'Total', 'Percentage'];
var xDivisors = [0, .3, .4, .5, 1],
yDivisors = [0, .1, .25, .35, 1];
var x = d3.scale.linear(),
y = d3.scale.ordinal(),
color = d3.scale.ordinal();
var tooltip = d3.select("body").append("div")
.attr("class", "stacked_tooltip")
.style("opacity", 0);
var svg = d3.select('#stacked').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom);
var svgStacked = svg.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('class', 'table');
d3.json('activities.json', function(error, jsonData) {
if (error) throw error;
var data = jsonData.data;
breadcrumb = jsonData.breadcrumb.join(' / '),
scopes = jsonData.data.map(function(d) { return d.scope; }),
pices = [],
colors = [],
picesLabels = [];
// Get colors and labels for the legend
for (var pice in jsonData.legend) {
colors.push(jsonData.legend[pice].color)
picesLabels.push(jsonData.legend[pice].text)
}
// Calculate the data for the table figures
var nestedData = [],
total;
jsonData.data.forEach(function(d, i) {
var rObj = {},
sum = 0;
for (var prop in d.pices) {
sum += parseFloat(d.pices[prop])
if (i == 0) {
pices.push(prop)
}
}
if (d.scope == "All") {
total = sum;
}
rObj['scope'] = d.scope;
rObj['total'] = sum;
rObj['percentage'] = sum / total;
nestedData.push(rObj);
});
// Calculate the 'stacked' lengths
data.forEach(function(d) {
var x0 = 0;
d.plotPices = pices.map(function(name, i) { return {name: name, x0: x0, x1: x0 += +d.pices[name], value: d.pices[name], label: picesLabels[i]}; });
d.total = d.plotPices[d.plotPices.length - 1].x1;
});
// Define the scales
x.domain([0, d3.max(data, function(d) { return d.total; })])
.range([xDivisors[3], xDivisors[3] * width])
y.domain(scopes)
.rangeRoundBands([0, height * (yDivisors[4] - yDivisors[3])], .1);
color.domain(pices)
.range(colors);
// Add svg
svgStacked.selectAll('.title')
.data([jsonData.title])
.enter()
.append('text')
.attr('class', 'title')
.attr('x', 0)
.attr('y', 0)
.style('font-size', titleSize)
.text(function(d) { return d; });
svgStacked.selectAll('.breadcrumb')
.data([breadcrumb])
.enter()
.append('text')
.attr('class', 'breadcrumb')
.attr('x', 0)
.attr('y', (yDivisors[1] * height) + 'px')
.style('fill', '#707070')
.style('font-size', '120%')
.text(function(d) { return d; })
var tableHeader = svgStacked.append('g')
.attr('class', 'tableHeader')
.attr('transform', 'translate(0,' + (yDivisors[2] * height) + ')');
tableHeader.selectAll('tableLabels')
.data(tableLabels)
.enter()
.append('text')
.attr('x', function(d, i) { return xDivisors[i] * width; })
.attr('y', 0)
.text(function(d) { return d; });
tableHeader
.append('line')
.attr('x1', 0)
.attr('y1', 10)
.attr('x2', width)
.attr('y2', 10)
.attr('stroke', 'black')
.attr('stroke-width', '1px');
var tableChart = svgStacked.append('g')
.attr('class', 'tableChart')
.attr('transform', 'translate(0,' + (yDivisors[3] * height) + ')');
var type = tableChart.append('g')
.attr('class', 'type');
var total = tableChart.append('g')
.attr('class', 'total')
.attr('transform', 'translate(' + (xDivisors[1] * width) + ',0)');
var percentage = tableChart.append('g')
.attr('class', 'percentage')
.attr('transform', 'translate(' + (xDivisors[2] * width) + ',0)');
var chart = tableChart.append('g')
.attr('class', 'chart')
.attr('transform', 'translate(' + (xDivisors[3] * width) + ',0)');
type.selectAll('text')
.data(scopes)
.enter()
.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return y(d); })
.attr('dy', y.rangeBand()/5)
.text(function(d) { return d; });
total.selectAll('text')
.data(nestedData)
.enter()
.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return y(d.scope); })
.attr('dy', y.rangeBand()/5)
.text(function(d) { return d.total ; });
percentage.selectAll('text')
.data(nestedData)
.enter()
.append('text')
.attr('x', 0)
.attr('y', function(d, i) { return y(d.scope); })
.attr('dy', y.rangeBand()/5)
.text(function(d) { return formatPercent(d.percentage) ; })
// Draw the chart:
// a group for each scope
var scope = chart.selectAll(".scope")
.data(data)
.enter().append("g")
.attr("class", function(d) { return "g scope " + normalize(d.scope); })
.attr("transform", function(d) { return "translate(0," + (y(d.scope) - y.rangeBand()/2) + ")"; });
// and inside each group
scope.selectAll("rect")
.data(function(d) { return d.plotPices; })
.enter().append("rect")
.attr('class', function(d, i) { return 'bar ' + d.name; })
.attr("height", y.rangeBand())
.attr("x", function(d) { return x(d.x0); })
.attr("width", function(d) { return x(d.x1) - x(d.x0); })
.style("fill", function(d) { return color(d.name); })
.style('stroke', 'white')
.on('mouseover', function(d) {
var thisData = d3.select(this).data()[0];
var thisClass = this.classList;
d3.selectAll('.bar')
.filter(function(d) { return d.name != thisClass[1]; })
.transition()
.duration(200)
.style('opacity', .5)
tooltip.transition()
.duration(500)
.style("opacity", .8);
tooltip.html(thisData.label + '<br><strong>' + thisData.value + '</strong> activities')
.style("left", (d3.event.pageX - 45) + "px")
.style("top", (d3.event.pageY - 80) + "px");
})
.on('mouseout', function(d) {
d3.selectAll('.bar')
.transition()
.duration(500)
.style('opacity', 1);
tooltip.transition()
.duration(500)
.style("opacity", 0);
})
// Add the legend
svg.append('g')
.attr('class', 'legend')
.attr('transform', 'translate(' + margin.left + ',' + (height + (margin.top * 1.5)) + ')');
var legend = d3.legend.color()
.shapePadding(10)
.shapeHeight(15)
.shapeWidth(15)
.scale(color)
.labels(picesLabels);
svg.select(".legend")
.call(legend);
});
var normalize = (function() {
var from = "ÃÀÁÄÂÈÉËÊÌÍÏÎÒÓÖÔÙÚÜÛãàáäâèéëêìíïîòóöôùúüûÑñÇç ",
to = "AAAAAEEEEIIIIOOOOUUUUaaaaaeeeeiiiioooouuuunncc_",
mapping = {};
for(var i = 0, j = from.length; i < j; i++ )
mapping[ from.charAt( i ) ] = to.charAt( i );
return function( str ) {
var ret = [];
for( var i = 0, j = str.length; i < j; i++ ) {
var c = str.charAt( i );
if( mapping.hasOwnProperty( str.charAt( i ) ) )
ret.push( mapping[ c ] );
else
ret.push( c );
}
return ret.join( '' ).toLowerCase();
}
})();
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment