Created
April 9, 2014 09:38
-
-
Save philgyford/10247700 to your computer and use it in GitHub Desktop.
d3 Line Chart problem
This file contains 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
Country | 1999 | 2000 | 2001 | 2002 | 2003 | |
---|---|---|---|---|---|---|
France | 19 | 20 | 30 | 32 | 9 | |
UK | 15 | 22 | 25 | 20 | 21 |
This file contains 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
Country | 1999 | 2000 | 2001 | 2002 | 2003 | |
---|---|---|---|---|---|---|
France | 15 | 18 | 17 | 20 | 25 | |
UK | 10 | 20 | 30 | 25 | 28 |
This file contains 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> | |
<html> | |
<head> | |
<meta charset="utf-8"> | |
<title>Import/Export</title> | |
<link rel="stylesheet" href="style.css"> | |
<script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script> | |
</head> | |
<body> | |
<div id="wait">Wait</div> | |
<div id="ready">Ready</div> | |
<div id="container"></div> | |
<script src="script.js"></script> | |
</body> | |
</html> |
This file contains 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
var impexp = {}; | |
impexp.dataManager = function module() { | |
var exports = {}, | |
// Custom events: | |
dispatch = d3.dispatch('dataReady', 'dataLoading'), | |
data; | |
exports.loadCsvData = function(_file, _cleaningFunc) { | |
var loadCsv = d3.csv(_file); | |
loadCsv.on('progress', function() { | |
dispatch.dataLoading(d3.event.loaded); }); | |
loadCsv.get(function(_error, _response) { | |
// Apply the cleaning function supplied in the _cleaningFunc parameter. | |
_response.forEach(function(d) { | |
_cleaningFunc(d); | |
}); | |
// Assign cleaned response to data. | |
data = _response; | |
dispatch.dataReady(_response); | |
}); | |
}; | |
exports.getCleanedData = function() { | |
return data; | |
}; | |
d3.rebind(exports, dispatch, 'on'); | |
return exports; | |
}; | |
/** | |
* For combining our sets of import and export data into one data structure. | |
* var combiner = impexp.dataCombiner(); | |
* var data = combiner.combine(imports_data, exports_data); | |
*/ | |
impexp.dataCombiner = function module() { | |
var exports = {}; | |
/** | |
* Both imports_data and exports_data are like: | |
* [ | |
* {1999: '15', 2000: '25', 2001: '30', 'Country': 'France'} | |
* {1999: '16', 2000: '18', 2001: '22', 'Country': 'UK'} | |
* ] | |
* and it returns this: | |
* { | |
* 'France': [ | |
* { | |
* year: 1999, | |
* imports: 15, | |
* exports: 33 | |
* }... | |
* ], | |
* 'UK': [ | |
* ... | |
* ] | |
* } | |
*/ | |
exports.combine = function(imports_data, exports_data) { | |
// The basis for what we'll return: | |
combined_data = keyByCountryWithArrays('imports', imports_data); | |
// So we can access its data more easily: | |
exports_by_country = keyByCountry(exports_data); | |
// Add the export data to the transformed import data: | |
d3.keys(combined_data).forEach(function(country) { | |
if (country in exports_by_country) { | |
combined_data[country].forEach(function(year_data, n) { | |
var year = year_data['year']; | |
if (year in exports_by_country[country]) { | |
combined_data[country][n]['exports'] = exports_by_country[country][year]; | |
}; | |
}); | |
}; | |
}); | |
return combined_data; | |
}; | |
/** | |
* `kind` is one of 'imports' or 'exports'. | |
* `rows` is an array or objects. | |
* | |
* Changes from: | |
* [ | |
* {1999: '15', 2000: '25', 2001: '30', 'Country': 'France'} | |
* {1999: '16', 2000: '18', 2001: '22', 'Country': 'UK'} | |
* ] | |
* | |
* to (if `kind` is 'imports'): | |
* { | |
* 'France': [ | |
* {'year': 1999, 'imports': 15}, | |
* {'year': 2000, 'imports': 25}, ... | |
* ], | |
* 'UK': [ ... | |
* } | |
*/ | |
keyByCountryWithArrays = function(kind, rows) { | |
var countries = {}; | |
rows.forEach(function(row) { | |
var years = []; | |
// k will be either a year or 'Country': | |
d3.keys(row).forEach(function(k) { | |
if (k !== 'Country') { | |
var year = {year: +k}; | |
year[kind] = +row[k]; | |
// year will be like {'year': 1999, 'imports': 15} | |
years.push(year); | |
}; | |
}); | |
countries[row['Country']] = years; | |
}); | |
return countries; | |
}; | |
/** | |
* Takes this: | |
* [ | |
* {1999: '15', 2000: '25', 2001: '30', 'Country': 'France'} | |
* {1999: '16', 2000: '18', 2001: '22', 'Country': 'UK'} | |
* ] | |
* and returns this: | |
* { | |
* 'France': {1999: 15, 2000: 25, 2001: 30}, | |
* 'UK': {1999: 16, 2000: 18, 2001: 22} | |
* } | |
*/ | |
keyByCountry = function(rows) { | |
var countries = {}; | |
rows.forEach(function(row) { | |
// Get the country name and remove from the row's data. | |
var country = row['Country']; | |
delete row['Country']; | |
// Make sure all years and values are numeric: | |
var year_data = {}; | |
d3.keys(row).forEach(function(k) { | |
var year = +k; | |
year_data[year] = +row[k]; | |
}); | |
countries[country] = year_data; | |
}) | |
return countries; | |
}; | |
return exports; | |
}; | |
impexp.chart = function module() { | |
var margin = {top: 20, right: 20, bottom: 30, left: 50}, | |
width = 400, | |
height = 300, | |
xValue = function(d) { return d[0]; }, | |
yValue = function(d) { return d[1]; }, | |
xScale = d3.scale.ordinal(), | |
yScale = d3.scale.linear(), | |
xAxis = d3.svg.axis().scale(xScale).orient('bottom'), | |
yAxis = d3.svg.axis().scale(yScale).orient('left'), | |
line = d3.svg.area().x(X).y(Y); | |
//area = d3.svg.area().x(X).y1(Y); | |
var dispatch = d3.dispatch('customHover'); | |
function exports(_selection) { | |
_selection.each(function(data) { | |
var inner_width = width - margin.left - margin.right, | |
inner_height = height - margin.top - margin.bottom; | |
// Update scales. | |
xScale.domain(d3.extent(data, function(d) { return d.year; })) | |
.rangePoints([0, inner_width]); | |
yScale.domain([ | |
d3.min(data, function(d) { return Math.min(d['imports'], d['exports']); }), | |
d3.max(data, function(d) { return Math.max(d['imports'], d['exports']); }) | |
]).range([inner_height, 0]); | |
// Select svg element if it exists. | |
var svg = d3.select(this) | |
.selectAll('svg') | |
.data([data]); | |
// Or create skeletal chart. | |
var gEnter = svg.enter().append('svg').append('g'); | |
gEnter.append('path').attr('class', 'line'); | |
gEnter.append('g').attr('class', 'x axis'); | |
gEnter.append('g').attr('class', 'y axis'); | |
// Update outer dimensions. | |
svg.transition() | |
.attr({ width: width, height: height }); | |
// Update inner dimensions. | |
var g = svg.select('g') | |
.attr('transform', | |
'translate(' + margin.left + ',' + margin.right + ')'); | |
console.log(xScale.range(), xScale.domain(), yScale.range(), yScale.domain(), JSON.stringify(data)); | |
// THIS IS WHERE IT GENERATES AN ERROR: | |
// Update line path. | |
g.select('.line').attr('d', line); | |
// Update axes. | |
g.select('.x.axis') | |
.attr('transform', 'translate(0,' + yScale.range()[0] + ')') | |
.call(xAxis); | |
g.select('.y.axis') | |
.call(yAxis); | |
}); | |
}; | |
// The x-accessor for the path generator; xScale ∘ xValue. | |
function X(d) { | |
return xScale(d.year); | |
} | |
// The x-accessor for the path generator; yScale ∘ yValue. | |
function Y(d) { | |
return yScale(d.imports); | |
} | |
exports.margin = function(_) { | |
if (!arguments.length) return margin; | |
margin = _; | |
return this; | |
}; | |
exports.width = function(_) { | |
if (!arguments.length) return width; | |
width = _; | |
return this; | |
}; | |
exports.height = function(_) { | |
if (!arguments.length) return height; | |
height = _; | |
return this; | |
}; | |
exports.x = function(_) { | |
if (!arguments.length) return xValue; | |
xValue = _; | |
return chart; | |
}; | |
exports.y = function(_) { | |
if (!arguments.length) return yValue; | |
yValue = _; | |
return chart; | |
}; | |
d3.rebind(exports, dispatch, "on"); | |
return exports; | |
}; | |
var draw_chart = function() { | |
d3.select('#wait').style('visibility', 'hidden'); | |
d3.select('#ready').style('visibility', 'visible'); | |
var combiner = impexp.dataCombiner(); | |
var data = combiner.combine(importsDataManager.getCleanedData(), | |
exportsDataManager.getCleanedData()); | |
var chart = impexp.chart() | |
.width(600).height(400) | |
.margin({top: 50, right: 50, bottom: 50, left: 50}); | |
var container = d3.select('#container') | |
.data([data['France']]) | |
.call(chart); | |
}; | |
var importsDataManager = impexp.dataManager(), | |
exportsDataManager = impexp.dataManager(); | |
// We don't really use this cleaning function. | |
var csvCleaner = function(d){}; | |
importsDataManager.loadCsvData('imports.csv', csvCleaner); | |
exportsDataManager.loadCsvData('exports.csv', csvCleaner); | |
var loaded = 0; | |
importsDataManager.on('dataReady', function() { | |
loaded++; | |
if (loaded == 2) { | |
draw_chart(); | |
}; | |
}); | |
exportsDataManager.on('dataReady', function() { | |
loaded++; | |
if (loaded == 2) { | |
draw_chart(); | |
}; | |
}); |
This file contains 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
.axis path, | |
.axis line { | |
fill: none; | |
stroke: #000; | |
shape-rendering: crispEdges; | |
} | |
.line { | |
fill: none; | |
stroke: steelblue; | |
stroke-width: 1.5px; | |
} | |
#ready { | |
visibility: hidden; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment