0
votes

I think I'm almost there. I have a working version for a single line that updates dynamically, but am having trouble getting to the multi-line part. I believe it has something to do with the way the data is being filtered at _selection.each. Not sure the best way to proceed from here though. The example found here (Drawing Multiple Lines in D3.js) seems to deal with this without much work.

Here is the jsfiddle for this, but the code is represented below as well: http://jsfiddle.net/seoulbrother/NhK43/

Thanks in advance. Also, would love to hear best practices regarding this as well.

So if I have a page with a matrix where each row represents a time-series:

<html>
<body>
<div id="container"><div id="viz"></div></div>
</body>
</html>

<script type="text/javascript">
var matrix = [
 [23,12,44,22,12,33,14,76,45,55,66,55],
 [33,22,11,88,32,63,13,36,35,51,26,25]
];
</script>

I call this using:

mult_line = d3.graph.line(500, 250, "#viz");
d3.select("#container").datum(matrix).call(mult_line);

where d3.graph.line is:

d3.graph.line = function module(w, h, id) {
    var svg;
    var margin = {top: 10, right: 20, bottom: 20, left: 40},
        width = w - margin.left - margin.right,
        height = h - margin.top - margin.bottom;

    function chart(_selection) {
       _selection.each(function(_data) {
            console.log(_data);
            var x = d3.scale.linear().domain([1, _data.length]).range([0, width]);
            var y = d3.scale.linear().domain([0, 100]).range([height, 0]);

            var xAxis = d3.svg.axis().scale(x).ticks(5).orient("bottom");       
            var yAxis = d3.svg.axis().scale(y).ticks(5).orient("left");

            var line = d3.svg.line()
                .x(function(d,i) { return x(i+1); })
                .y(function(d) { return y(d); });

            if (!svg){
                svg = d3.select(id).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 + ")");

                svg.append("path")
                    .attr("class","line")
                    .attr("d", line(_data));

                svg.append("g")
                      .attr("class", "x-axis")
                      .attr("transform", "translate(0," + height + ")")
                      .call(xAxis);

                svg.append("g")
                      .attr("class", "y-axis")
                      .call(yAxis);
            }

            var line_m = d3.selectAll("path.line")
                .data(_data);

            line_m.transition()
                .duration(1000)
                .attr('d', line(_data));

            line_m.enter().append("path")
                .attr("d",line(_data));

            update_axis();
            function update_axis() {
                d3.select(".x-axis").call(xAxis);
            }
        });
    }
    return chart;
}
1
How did this work out for you?meetamit

1 Answers

1
votes

I think there may be varying opinions about this, but I wouldn't use selection.datum() as the mechanism of passing data into the chart component. Instead, I would add a .data() setter method as a property of the multiline component. I would do the same for width, height, etc. This is the way d3's components are implemented (for example, check out the source code for the d3.svg.axis). Then your chart creation would look like this:

mult_line = d3.graph.line()
  .width(500)
  .height(250)
  .id("#viz")
  .data(matrix);
d3.select("#container").call(mult_line);

You can refine this so that there are 2 data-setting methods of multiline: .data() and .multilineData(). The first would expect data for a single line (an array) and the 2nd would expect an array of arrays (multiline). Internally, they'd always be represented as an array of arrays. That way, the chart drawing code always expects to draw multiline data, except sometimes the data has just one line.