Roman
Roman

Reputation: 9451

D3.js streamgraph transition plots nothing

Another D3.js noob here.

I'm starting out with editing the stock streamgraph example from http://bl.ocks.org/mbostock/4060954.

I've looked as several similar questions, such as this, this and this, but I can't seem to find my error.

I have two json mock data, layers0 and layers1 (originating from one of the examples above, coped from this fiddle). I can plot both of these without a problem by changing my data in:

svg.selectAll("path")
    .data(layers0/1)  #layers0 or layers1, both work
.enter().append("path")
    .attr("d", function(d) { return area(d.values); })
    .style("fill", function() { return color(Math.random()); });

However, when I try and create a transition between these two plots, the original plot vanishes and I am left with an empty space:

function transition() {
d3.selectAll("path")
    .data(layers1/0)  #the alternate (or same) one from the function above
    .transition()
    .duration(500)
    .attr("d", area);
}

Eventually I would like lots of buttons, and transition should move between them all, such as in this example, but for now I just want to move between two datasets.

Upvotes: 0

Views: 518

Answers (2)

questing
questing

Reputation: 172

I tried to reproduce what you ask for here:

http://jsfiddle.net/g9JNH/

<button class="button1" onclick="transition()">Transition 1</button>
<button class="button2" onclick="transition2()">Transition 2</button>
<script>
var n = 20, // number of layers
    m = 200, // number of samples per layer
    stack = d3.layout.stack().offset("wiggle"),
    layers0 = stack(d3.range(n).map(function() { return bumpLayer(m); })),
    layers1 = stack(d3.range(n).map(function() { return bumpLayer(m); }));

    var width = 960,
    height = 500;

    var x = d3.scale.linear()
        .domain([0, m - 1])
        .range([0, width]);

    var y = d3.scale.linear()
       .domain([0, d3.max(layers0.concat(layers1), function(layer) { 
            return d3.max(layer, function(d) { return d.y0 + d.y; }); })])
       .range([height, 0]);

    var color = d3.scale.linear()
       .range(["#aad", "#556"]);

    var area = d3.svg.area()
       .x(function(d) { return x(d.x); })
       .y0(function(d) { return y(d.y0); })
       .y1(function(d) { return y(d.y0 + d.y); });

    var svg = d3.select("body").append("svg")
       .attr("width", width)
       .attr("height", height);

    svg.selectAll("path")
       .data(layers0)
       .enter().append("path")
       .attr("d", area)
       .style("fill", function() { return color(Math.random()); });

    function transition() {
       d3.selectAll("path")
           .data(layers1)
           .transition()
           .duration(2500)
           .attr("d", area);
    }

    function transition2() {
        d3.selectAll("path")
           .data(layers0)
           .transition()
           .duration(2500)
           .attr("d", area);
    }

    // Inspired by Lee Byron's test data generator.
    function bumpLayer(n) {

         function bump(a) {
             var x = 1 / (.1 + Math.random()),
             y = 2 * Math.random() - .5,
             z = 10 / (.1 + Math.random());
             for (var i = 0; i < n; i++) {
                 var w = (i / n - y) * z;
                 a[i] += x * Math.exp(-w * w);
             }
         }

         var a = [], i;
         for (i = 0; i < n; ++i) a[i] = 0;
         for (i = 0; i < 5; ++i) bump(a);
         return a.map(function(d, i) { return {x: i, y: Math.max(0, d)}; });
    }
    </script>

I used the same example, and the code in principle works, could there be be something else that you have implemented like more html, or some error in the data sets? or maybe I didn't understood your question...

Upvotes: 0

Roman
Roman

Reputation: 9451

All right, I have found a solution that works, even though I don't fully understand it yet. The solution was mostly copied from this fiddle. I will point out differences between the example code and this code that have to be made (if someone can please explain why, that would be great) for it to work.

I will post my entire code for any fellow newbies that find D3 confusing.

HTML: (I'm working off a pyramid application, hence the script imports in the head)

<head>
<title>           
    Graph
</title>

    <link rel="stylesheet" type="text/css" href="${request.static_url('monitor:static/css/graph.css')}"/>

    <script type="text/javascript" src="${request.static_url('monitor:static/jquery-ui-1.10.3/jquery-1.9.1.js')}"></script>
    <script type="text/javascript" src="${request.static_url('monitor:static/jquery-ui-1.10.3/ui/jquery-ui.js')}"></script>
    <script type="text/javascript" src="${request.static_url('monitor:static/D3/d3.min.js')}"></script>
    <script type="text/javascript" src="${request.static_url('monitor:static/js/graph.js')}"></script>
</head>

<body>
    <body onload="graphplot()">
    <h4 id="Heading">Streamgraph</h4>
    <div id="graph_area"></div>
</body>

And then here is my graph.js:

function graphplot(){


test_data0 = [{"0": 0.2, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.6, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.6}, {"0": 0.3, "1": 0.0, "-1": 0.1}, {"0": 0.0, "1": 0.2, "-1": 0.3}, {"0": 0.3, "1": 0.5, "-1": 0.0}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.0}]
test_data1 = [{"0": 0.0, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.6, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.6}, {"0": 0.3, "1": 0.3, "-1": 0.0}, {"0": 0.0, "1": 0.3, "-1": 0.3}, {"0": 0.3, "1": 0.1, "-1": 0.6}, {"0": 0.3, "1": 0.0, "-1": 0.0}, {"0": 0.0, "1": 0.0, "-1": 0.0}]


    var width = $("#graph_area").width(),
        height = 500;


    var toggle = 0;  //just so that the animation toggles between these two data

    $("#graph_area").click(function(){
        console.log('test')

        if (toggle == 0){
            streamed_history(test_data1)
            toggle = 1;
        }else {
            streamed_history(test_data0)
            toggle=0;
        }
    });


    var colors = {'0': '#6ff500', '1': '#ffad0a', '-1': '#f90035'}, //colours for the three layers; no longer random
        feedbacks = [-1, 0, 1],
        stack = d3.layout.stack().offset("wiggle"); //the plot sits on its base without wiggle
        //.values(function(d) { return d.values; }); --->removed. Throws: TypeError: t is undefined

    var svg = d3.select("#graph_area").append("svg")
        .attr("width", width)
        .attr("height", height);



    streamed_history(test_data0)

    function streamed_history(data) {
        data_array = feedbacks.map(function (f) {
            return data.map(function(element, i) { return {x: i, y: element[f]}; })
        }),
        layers = stack(data_array)  //--->No nest.entries.  And the below code for only one layers variable
        layers = feedbacks.map(function (f, i) {
            return {layer: layers[i], feedback: f, color: colors[f]}
        })



        var x = d3.scale.linear()
            .domain([0, data.length - 1])
            .range([0, width]);

        var y = d3.scale.linear()
            .domain([0,1])  //--->coplicated thing removed.  It just needs to be:([0, max of data])
            .range([height, 0]);

        var area = d3.svg.area().interpolate("basis")
            .x(function(d) { return x(d.x); })
            .y0(function(d) { return y(d.y0); })
            .y1(function(d) { return y(d.y0 + d.y); });

        //enter
        svg.selectAll("path")
            .data(layers)
        .enter().append("path")
            .attr("d", function (d) {return area(d.layer);})
            .style("fill", function(d) { return d.color; });

        //update
        d3.selectAll("path") //this effectively replaces transition(), since now each dataset is plotted independently with the duration of transition defined here.
        .data(layers)
        .transition()
        .duration(500)
        .attr("d", function (d) {return area(d.layer);});
    }

}//end graphplot

graphplot()

Hope this helps anyone in the future!

Upvotes: 2

Related Questions