I am trying to replicate this example using d3.js
So far I have managed to build a stacked bar chart that shows all data but my purpose is to filter the csv and bind the new data to my chart based on the user selection of country in a combo.
Here is my code:
var outerWidth = 500;
var outerHeight = 250;
var margin = { left: 90, top: 30, right: 30, bottom: 40 };
var barPadding = 0.2;
var xColumn = "City";
var yColumn = "Population";
var colorColumn = "Year";
var layerColumn = colorColumn;
var innerWidth = outerWidth - margin.left - margin.right;
var innerHeight = outerHeight - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", outerWidth)
.attr("height", outerHeight);
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var xAxisG = g.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + innerHeight + ")");
var yAxisG = g.append("g")
.attr("class", "y axis");
var xScale = d3.scale.ordinal().rangeBands([0, innerWidth], barPadding);
var yScale = d3.scale.linear().range([innerHeight, 0]);
var colorScale = d3.scale.category10();
// Use a modified SI formatter that uses "B" for Billion.
var siFormat = d3.format("s");
var customTickFormat = function (d){
return siFormat(d).replace("G", "B");
};
var xAxis = d3.svg.axis().scale(xScale).orient("bottom")
.outerTickSize(0);
var yAxis = d3.svg.axis().scale(yScale).orient("left")
.ticks(5)
.tickFormat(customTickFormat)
.outerTickSize(0);
function render(data){
var nested = d3.nest()
.key(function (d){ return d[layerColumn]; })
.entries(data)
var stack = d3.layout.stack()
.y(function (d){ return d[yColumn]; })
.values(function (d){ return d.values; });
var layers = stack(nested);
xScale.domain(layers[0].values.map(function (d){
return d[xColumn];
}));
yScale.domain([
0,
d3.max(layers, function (layer){
return d3.max(layer.values, function (d){
return d.y0 + d.y;
});
})
]);
colorScale.domain(layers.map(function (layer){
return layer.key;
}));
xAxisG.call(xAxis);
yAxisG.call(yAxis);
var layerGroups = g.selectAll(".layer").data(layers);
layerGroups.enter().append("g").attr("class", "layer");
layerGroups.exit().remove();
layerGroups.style("fill", function (d){
return colorScale(d.key);
});
var bars = layerGroups.selectAll("rect").data(function (d){
return d.values;
});
bars.enter().append("rect")
bars.exit().remove();
bars
.attr("x", function (d){ return xScale(d[xColumn]); })
.attr("y", function (d){ return yScale(d.y0 + d.y); })
.attr("width", xScale.rangeBand())
.attr("height", function (d){ return innerHeight - yScale(d.y); })
}
function type(d){
d.Population = +d.Population;
return d;
}
d3.csv("data.csv", type, render);
</script>
Here is the sample data in data.csv
Country City Year Population
US Dallas 2010 1000
US Dallas 2011 1200
UK London 2010 700
UK London 2011 850
US Chicago 2010 1250
US Chicago 2011 1300
It is possible to reload chart's data through reloading and reparsing CSV
and using Highcharts official data module, but it is more efficient to load and parse data once, next update/rebuild chart when needed.
Example:
http://jsfiddle.net/1wgqyyg9/
// Emulate get
$.get = function(id, fn) {
fn(document.getElementById(id).innerHTML);
};
$(function() {
var chartOptions = {
chart: {
type: 'column'
},
xAxis: {
type: 'category'
},
yAxis: {
min: 0,
title: {
text: 'Total population over years'
},
stackLabels: {
enabled: true,
style: {
fontWeight: 'bold',
color: (Highcharts.theme && Highcharts.theme.textColor) || 'gray'
}
}
},
legend: {
align: 'right',
x: -30,
verticalAlign: 'top',
y: 25,
floating: true,
backgroundColor: (Highcharts.theme && Highcharts.theme.background2) || 'white',
borderColor: '#CCC',
borderWidth: 1,
shadow: false
},
tooltip: {
headerFormat: '<b>{series.name}</b><br/>',
pointFormat: '{point.name}: {point.y}<br/>Total: {point.stackTotal}'
},
plotOptions: {
column: {
stacking: 'normal',
dataLabels: {
enabled: true,
color: (Highcharts.theme && Highcharts.theme.dataLabelsColor) || 'white',
style: {
textShadow: '0 0 3px black'
}
}
}
},
series: []
};
//load CSV - emulated infile to show in JSFiddle
$.get('data.csv', function(data) {
// Split the lines
var lines = data.split('\n'),
countries = {};
// Iterate over the lines and create data sets - countries
$.each(lines, function(lineNo, line) {
var items = line.split(','),
yearFound = false,
country, city, year, population;
if (lineNo === 0) { // header line containes info
} // rest of lines contain data
else {
country = items[0],
city = items[1],
year = items[2],
population = items[3];
// check if new country
if(countries[country] === undefined) {
countries[country] = [{
name: year,
data: [{name: city, y: parseInt(population)}]
}];
} else {
$.each(countries[country], function(yearNo, countryYear){
if(year === countryYear.name) {
yearFound = true;
countryYear.data.push({name: city, y: parseInt(population)});
return false; //exit this each loop
}
});
if(!yearFound) { // new year
countries[country].push({
name: year,
data: [{name: city, y: parseInt(population)}]
});
}
}
}
});
// sort cities in year series
$.each(countries, function(countryName,country){
$('#selectCountry')
.append($("<option></option>")
.attr("value",countryName)
.text(countryName));
$.each(country, function(j,year){
year.data.sort(function(a,b){
a.name > b.name;
});
});
});
$("#selectCountry").change(function(){
var selected = this.value;
if(selected) {
chartOptions.series = $.extend(true, [], countries[selected]); // do deep copy to keep original data
Highcharts.chart('container',chartOptions);
}
});
});
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js"></script>
<script src="https://code.highcharts.com/highcharts.js"></script>
<script src="https://code.highcharts.com/modules/exporting.js"></script>
<div id="container" style="min-width: 310px; height: 400px; margin: 0 auto"></div>
<select id="selectCountry">
<option>Select country</option>
</select>
<pre id="data.csv" style="display: none">Country,City,Year,Population
US,Dallas,2010,1000
US,Dallas,2011,1200
UK,London,2010,700
UK,London,2011,850
US,Chicago,2010,1250
US,Chicago,2011,1300</pre>
Collected from the Internet
Please contact [email protected] to delete if infringement.
Comments