I have a data file with several time series and I want to plot them all in a single graph using Javascript/D3. I am fairly new to Javascript/D3 but after searching for examples I have managed to come up with a code that almost does the trick. There is still one issue remaining, namely I face a weird problem when I try to initialize and draw my line objects dynamically.
My three code files below contain a MWE with two series. In charts.js I load in some data (date column and two series). I initialize my line objects in two ways: first hard coding (e.g. line1 = d3.svg.line()
) and secondly assigning them into an array (e.g. allLines[0] = d3.svg.line()
) allowing for dynamic initialization of the series. I try to render the lines as
// Version 1: Harcoded lines
allPaths[0].attr('d', line1);
allPaths[1].attr('d', line2);
allPaths[1].style("stroke", "red")
// Version 2: Lines from array
allPaths[0].attr('d', allLines[0]);
allPaths[1].attr('d', allLines[1]);
allPaths[1].style("stroke", "red")
Version 1 works just fine and both lines appear as they should. However, I encounter a weird problem when executing the lines in Version 2: when running line allPaths[0].attr('d', allLines[0]);
the execution somehow jumps back in the code to the part where objects are allocated into allLines
(or so it at least seems in debugging mode). This then throws an error of undefined object/variable and the script fails to execute. To me this makes absolutely no sense. Further, allLines[0]
and line1
seem to be equivalent so I cannot figure out why they won't produce the same result.
In summary, does anybody have an idea what could cause this weird behavior? I am also open to other ways of obtaining the desired result, i.e. being able to plot all the series in the data file dynamically.
main.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width" />
<title>D3 Line Chart</title>
<link rel="stylesheet" href="style.css">
<script src="//d3js.org/d3.v3.min.js" charset="utf-8"></script>
</head>
<body>
<div id="chart"></div>
<script src="chart.js"></script>
</body>
</html>
chart.js
var Chart = (function(window,d3) {
// Declare global variables
var svg, data, x, y, xAxis, yAxis, dim, chartWrapper, line1, line2, allLines, allPaths, margin = {}, width, height;
// Create data
var data = [{date: "1.1.2005", serie1: 10, serie2: 15},
{date: "1.2.2005", serie1: 14, serie2: 16},
{date: "1.3.2005", serie1: 12, serie2: 17},
{date: "1.4.2005", serie1: 19, serie2: 21},
{date: "1.5.2005", serie1: 7, serie2: 13},
{date: "1.6.2005", serie1: 12, serie2: 16},
];
// Define how dates are presented in the source file
var parseDate = d3.time.format("%d.%m.%Y").parse;
// Format each series into its own, distinguishable elements
color = d3.scale.category10();
color.domain(d3.keys(data[0]).filter(function(key) {
return key !== "date";
}));
lineNames = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {
date: d.date,
};
})
};
});
//Initialize scales. HACK: only selecting scales based on the 1st series
xExtent = d3.extent(data, function(d,i) { return new Date(parseDate(d.date)) });
yExtent = d3.extent(data, function(d,i) { return +d.serie1 });
x = d3.time.scale().domain(xExtent);
y = d3.scale.linear().domain(yExtent);
//Initialize axes
xAxis = d3.svg.axis().orient('bottom');
yAxis = d3.svg.axis().orient('left');
//Initialize svg
svg = d3.select('#chart').append('svg');
chartWrapper = svg.append('g');
chartWrapper.append('g').classed('x axis', true);
chartWrapper.append('g').classed('y axis', true);
// Create lines:
// Version 1: Hardcoded lines
line1 = d3.svg.line()
.x(function(d) { return x(new Date(parseDate(d.date))) })
.y(function(d) { return y(+d.serie1) });
line2 = d3.svg.line()
.x(function(d) { return x(new Date(parseDate(d.date))) })
.y(function(d) { return y(+d.serie2) });
// Version 2: Dynamic lines
allLines =[];
for (var i = 0; i <= 2; i++){
allLines[i] = d3.svg.line()
.x(function(d) { return x(new Date(parseDate(d.date))) })
.y(function(d) { return y( d.lineNames[i].name ) });
};
//Paths to all lines
allPaths =[];
for (var i = 0; i <= allLines.length - 1; i++){
allPaths[i] = chartWrapper.append('path').datum(data).classed('line', true);
};
//render the chart
render(data);
function render(data) {
margin.top = 100;
margin.right = 300;
margin.left = 50;
margin.bottom = 100;
width = window.innerWidth - margin.left - margin.right;
height = 500 - margin.top - margin.bottom;
// update x and y scales to new dimensions
x.range([0, width]);
y.range([height, 0]);
// update svg elements to new dimensions
svg
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom);
chartWrapper.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
// axes scales
xAxis.scale(x);
yAxis.scale(y);
// x-axis
svg.select('.x.axis')
.attr('transform', 'translate(0,' + height + ')')
.call(xAxis);
// y-axis
svg.select('.y.axis')
.call(yAxis);
// Draw all lines:
// Version 1: Harcoded lines
allPaths[0].attr('d', line1);
allPaths[1].attr('d', line2);
allPaths[1].style("stroke", "red")
// Version 2: Lines from array. COMMENTED OUT SINCE DOESN'T WORK!
//allPaths[0].attr('d', allLines[0]);
//allPaths[1].attr('d', allLines[1]);
//allPaths[1].style("stroke", "red")
}
}
)(window,d3);
style.css
body {
font: 12px sans-serif;
margin: 0;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: steelblue;
stroke-width: 1.5px;
}