简体   繁体   中英

Updating data in a simple reusable D3 chart

In the book "Data Visualization with D3 Cookbook" (chapter 8) appears an example where the author plots two lines. Basically, the data is created randomly:

var numberOfSeries = 2,
    numberOfDataPoint = 11,
    data = [];

for (var i = 0; i < numberOfSeries; ++i)
    data.push(d3.range(numberOfDataPoint).map(function (i) {
        return {x: i, y: randomData()};
    }));

Then, the author creates an instance of the chart and defines the scales' domain:

var chart = lineChart()
        .x(d3.scale.linear().domain([0, 10]))
        .y(d3.scale.linear().domain([0, 10]));

After that, the data is introduced using the addSeries() method:

data.forEach(function (series) {
    chart.addSeries(series);
});

And finally, the chart is rendered calling the render() method:

chart.render();

Additionally, the plot has an Update buttom that allows to update the data showed by calling this function:

function update() {
    for (var i = 0; i < data.length; ++i) {
        var series = data[i];
        series.length = 0;
        for (var j = 0; j < numberOfDataPoint; ++j)
            series.push({x: j, y: randomData()});
    }

    chart.render();
}

My problem is that I can't understand how the data is updated in the plot without calling the addSeries() method again , after press the Update button, ie, in the update() function. The data internally is stored in the variable _data , which can be modified with the method addSeries() . Any ideas?

Many thanks!

The complete code is here , but following I've copied the most important parts:

function lineChart() { // <-1A
    var _chart = {};

    var _width = 600, _height = 300, // <-1B
            _margins = {top: 30, left: 30, right: 30, bottom: 30},
            _x, _y,
            _data = [],
            _colors = d3.scale.category10(),
            _svg,
            _bodyG,
            _line;

    _chart.render = function () { // <-2A
        if (!_svg) {
            _svg = d3.select("body").append("svg") // <-2B
                    .attr("height", _height)
                    .attr("width", _width);

            renderAxes(_svg);

            defineBodyClip(_svg);
        }

        renderBody(_svg);
    };

    // Axis rendering functions ...


    function renderBody(svg) { // <-2D
        if (!_bodyG)
            _bodyG = svg.append("g")
                    .attr("class", "body")
                    .attr("transform", "translate(" 
                        + xStart() + "," 
                        + yEnd() + ")") // <-2E
                    .attr("clip-path", "url(#body-clip)");        

        renderLines();

    }

    function renderLines() {
        _line = d3.svg.line() //<-4A
                        .x(function (d) { return _x(d.x); })
                        .y(function (d) { return _y(d.y); });

        _bodyG.selectAll("path.line")
                    .data(_data)
                .enter() //<-4B
                .append("path")                
                .style("stroke", function (d, i) { 
                    return _colors(i); //<-4C
                })
                .attr("class", "line");

        _bodyG.selectAll("path.line")
                .data(_data)
                .transition() //<-4D
                .attr("d", function (d) { return _line(d); });
    }

    //Some getter/setters functions and other stuff

    _chart.addSeries = function (series) { // <-1D
        _data.push(series);
        return _chart;
    };

    return _chart; // <-1E
}

function randomData() {
    return Math.random() * 9;
}

function update() {
    for (var i = 0; i < data.length; ++i) {
        var series = data[i];
        series.length = 0;
        for (var j = 0; j < numberOfDataPoint; ++j)
            series.push({x: j, y: randomData()});
    }

    chart.render();
}

var numberOfSeries = 2,
    numberOfDataPoint = 11,
    data = [];

for (var i = 0; i < numberOfSeries; ++i)
    data.push(d3.range(numberOfDataPoint).map(function (i) {
        return {x: i, y: randomData()};
    }));

var chart = lineChart()
        .x(d3.scale.linear().domain([0, 10]))
        .y(d3.scale.linear().domain([0, 10]));

data.forEach(function (series) {
    chart.addSeries(series);
});

chart.render();

The update() function is modifying each series in place, which is picked up automatically by the render() function. This works because the closure stores a reference to the series (which is modified) rather than the actual values.

What happens is the same as when you would modify a global variable. The addSeries() function adds a new series, but in this particular case you're not adding any new series, but modifying the existing ones.

That said, I wouldn't consider the code to be particularly well designed.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM