0

I looked on the Stack Exchange for how to create a horizontal stacked bar example, and found: http://tributary.io/inlet/4966973

Which is based on: http://bl.ocks.org/mbostock/3943967

To better understand how this code works I got Bostock's example running on my machine (via SimpleHTTPServer, etc.).

However, I couldn't get gelicia's Tributary example to run. I copied gelicia's Tributary example, and added Bostock's html code (leading up to the script), and additionally the functions below where the Tributary example ends, but the svg body and resulting bar rects aren't created. But there's no obvious error message to fix something.

I tried switching the xs and ys in Bostock's function since I read that was the main conversion issue from going from vertical to horizontal stacked bars, but that didn't help and once again no error appeared.

Can someone explain to me how to get the horizontal bar stack example to run, and what I've been done wrong in trying to get it to work within an html document?

<!DOCTYPE html>
<meta charset="utf-8">
<style>

body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  margin: auto;
  position: relative;
  width: 960px;
}

text {
  font: 10px sans-serif;
}

.axis path,
.axis line {
  fill: none;
  stroke: #000;
  shape-rendering: crispEdges;
}

form {
  position: absolute;
  right: 10px;
  top: 10px;
}

</style>
<form>
  <label><input type="radio" name="mode" value="grouped"> Grouped</label>
  <label><input type="radio" name="mode" value="stacked" checked> Stacked</label>
</form>
<script type="text/javascript" src="d3.v3.js"></script>
<script>
        //modified from Mike Bostock at http://bl.ocks.org/3943967 */

        var data = [
        {"key":"FL", "pop1":3000, "pop2":4000, "pop3":5000},
        {"key":"CA", "pop1":3000, "pop2":3000, "pop3":3000},
        {"key":"NY", "pop1":12000, "pop2":5000, "pop3":13000},
        {"key":"NC", "pop1":8000, "pop2":21000, "pop3":11000},
        {"key":"SC", "pop1":30000, "pop2":12000, "pop3":8000},
        {"key":"AZ", "pop1":26614, "pop2":6944, "pop3":30778},
        {"key":"TX", "pop1":8000, "pop2":12088, "pop3":20000}
        ];

        var n = 3, // number of layers
            m = data.length, // number of samples per layer
            stack = d3.layout.stack(),
            labels = data.map(function(d) {return d.key;}),

            //go through each layer (pop1, pop2 etc, that's the range(n) part)
            //then go through each object in data and pull out that objects's population data
            //and put it into an array where x is the index and y is the number
            layers = stack(d3.range(n).map(function(d) { 
                        var a = [];
                        for (var i = 0; i < m; ++i) {
                            a[i] = {x: i, y: data[i]['pop' + (d+1)]};  
                        }
                        return a;
                     })),

            //the largest single layer
            yGroupMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y; }); }),
            //the largest stack
            yStackMax = d3.max(layers, function(layer) { return d3.max(layer, function(d) { return d.y0 + d.y; }); });

        var margin = {top: 40, right: 10, bottom: 20, left: 50},
            width = 677 - margin.left - margin.right,
            height = 533 - margin.top - margin.bottom;

        var y = d3.scale.ordinal()
            .domain(d3.range(m))
            .rangeRoundBands([2, height], .08);

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

        var color = d3.scale.linear()
            .domain([0, n - 1])
            .range(["#aad", "#556"]);

        var svg = d3.select("svg")
            .attr("width", width + margin.left + margin.right)
            .attr("height", height + margin.top + margin.bottom)
          .append("g")
            .attr("transform", "translate(" + margin.left + "," + margin.top + ")");

        var layer = svg.selectAll(".layer")
            .data(layers)
          .enter().append("g")
            .attr("class", "layer")
            .style("fill", function(d, i) { return color(i); });

        layer.selectAll("rect")
            .data(function(d) { return d; })
            .enter().append("rect")
            .attr("y", function(d) { return y(d.x); })
            .attr("x", function(d) { return x(d.y0); })
            .attr("height", y.rangeBand())
            .attr("width", function(d) { return x(d.y); });

        var yAxis = d3.svg.axis()
            .scale(y)
            .tickSize(1)
            .tickPadding(6)
            .tickValues(labels)
            .orient("left");

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



        //ADD IN BOSTOCK CODE -- replace xs with ys and vice versa

        d3.selectAll("input").on("change", change);

        var timeout = setTimeout(function() {
          d3.select("input[value=\"grouped\"]").property("checked", true).each(change);
        }, 2000);

        function change() {
          clearTimeout(timeout);
          if (this.value === "grouped") transitionGrouped();
          else transitionStacked();
        }

        function transitionGrouped() {
          x.domain([0, xGroupMax]);

          rect.transition()
              .duration(500)
              .delay(function(d, i) { return i * 10; })
              .attr("y", function(d, i, j) { return y(d.y) + y.rangeBand() / n * j; })
              .attr("width", y.rangeBand() / n)
            .transition()
              .attr("x", function(d) { return x(d.x); })
              .attr("height", function(d) { return height - x(d.x); });
        }

        function transitionStacked() {
          x.domain([0, xStackMax]);

          rect.transition()
              .duration(500)
              .delay(function(d, i) { return i * 10; })
              .attr("x", function(d) { return x(d.x0 + d.x); })
              .attr("height", function(d) { return x(d.x0) - x(d.x0 + d.x); })
            .transition()
              .attr("y", function(d) { return y(d.y); })
              .attr("width", y.rangeBand());
        }

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

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

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

          }

        </script>   
    </body>
</html>
Conor
  • 57
  • 4
  • 13
  • I answered a very similar question here (which has working code): http://stackoverflow.com/questions/10568802/need-help-using-d3-js-to-create-stacked-bar-chart/21368473#21368473 – datashaman Jan 26 '14 at 20:01
  • I found the example linked in the beginning of the question very helpful (http://tributary.io/inlet/4966973): One of the most simple, clean implementations I've seen. – cellepo Sep 10 '14 at 17:17

2 Answers2

0

It looks like you don't have an svg element on your page. You can simply add in your body (above or below your form) and it should work.

(tributary creates an svg element for you by default, which is they the code runs there and not in your example)

enjalot
  • 660
  • 3
  • 7
  • That's great! That tributary creats an svg element by default, was not obvious to me looking through their website. The image displays but the functions are not working so I am guessing I'll have to tinker with those more -- seems like it's a bit more complicated than just switching xs and ys... – Conor Dec 20 '13 at 04:05
0

Yeah, like enjalot said, you'll need an svg element, and then you'll need to wrap the javascript in a function and add an onload to your html so it will execute that function when it loads the page. Something like <body onLoad="loadChart()">