0

I'm trying to re-create the interactive streamgraph presented on GitHub, with data that I'm obtaining from a Web Service. I'm using the same code from the sample on GitHub to generate the graph, however I'm getting MNaN and NaN coordinates for the path elements (precisely, their d attribute). The results is an empty graph where only the x and y axes are drawn but nothing more.

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

body {
  font: 10px sans-serif;
}

.chart { 
  background: #fff;
}

p {
  font: 12px helvetica;
}


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

button {
  position: absolute;
  right: 50px;
  top: 10px;
}

</style>
<body>
  <script src="http://d3js.org/d3.v3.min.js" charset="utf-8"></script>
    <script src="//code.jquery.com/jquery-1.11.0.min.js"></script>



<div class="chart"></div>
</body>

<script>
    var dataset;
    function getData() {
        $.ajax({
            type: "POST",
            contentType: "application/json; charset=utf-8",
            url: "VoxPoliticoService.asmx/ComparePoliticians",
            data: {},
            dataType: "json",
            success: function (res) {

                dataset = res.d;

                renderChart();
            }
        });
    }

    jQuery(document).ready(function ($) {
        getData();

    });


    function renderChart()
    {
        color = "blue";

        var datearray = [];
        var colorrange = [];
        var data = dataset;




            if (color == "blue") {
                colorrange = ["#045A8D", "#2B8CBE", "#74A9CF", "#A6BDDB", "#D0D1E6", "#F1EEF6"];
            }
            else if (color == "pink") {
                colorrange = ["#980043", "#DD1C77", "#DF65B0", "#C994C7", "#D4B9DA", "#F1EEF6"];
            }
            else if (color == "orange") {
                colorrange = ["#B30000", "#E34A33", "#FC8D59", "#FDBB84", "#FDD49E", "#FEF0D9"];
            }
            strokecolor = colorrange[0];

            var format = d3.time.format("%m/%d/%y");

            var margin = {top: 20, right: 40, bottom: 30, left: 30};
            var width = document.body.clientWidth - margin.left - margin.right;
            var height = 400 - margin.top - margin.bottom;

            var tooltip = d3.select("body")
                .append("div")
                .attr("class", "remove")
                .style("position", "absolute")
                .style("z-index", "20")
                .style("visibility", "hidden")
                .style("top", "30px")
                .style("left", "55px");

            var x = d3.time.scale()
                .range([0, width]);

            var y = d3.scale.linear()
                .range([height-10, 0]);

            var z = d3.scale.ordinal()
                .range(colorrange);

            var xAxis = d3.svg.axis()
                .scale(x)
                .orient("bottom")
                .ticks(d3.time.weeks);

            var yAxis = d3.svg.axis()
                .scale(y);

            var yAxisr = d3.svg.axis()
                .scale(y);

            var stack = d3.layout.stack()
                .offset("silhouette")
                .values(function(d) { return d.values; })
                .x(function(d) { return d.date; })
                .y(function(d) { return d.value; });

            var nest = d3.nest()
                .key(function(d) { return d.key; });

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

            var svg = d3.select(".chart").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 + ")");

                data.forEach(function(d) {
                    d.date = format.parse(d.date);
                    d.value = +d.value;
                });

                var layers = stack(nest.entries(data));

                x.domain(d3.extent(data, function(d) { return d.date; }));
                y.domain([0, d3.max(data, function(d) { return d.y0 + d.y; })]);

                svg.selectAll(".layer")
                    .data(layers)
                  .enter().append("path")
                    .attr("class", "layer")
                    .attr("d", function(d) { return area(d.values); })
                    .style("fill", function(d, i) { return z(i); });


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

                svg.append("g")
                    .attr("class", "y axis")
                    .attr("transform", "translate(" + width + ", 0)")
                    .call(yAxis.orient("right"));

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

                svg.selectAll(".layer")
                  .attr("opacity", 1)
                  .on("mouseover", function(d, i) {
                      svg.selectAll(".layer").transition()
                      .duration(250)
                      .attr("opacity", function(d, j) {
                          return j != i ? 0.6 : 1;
                      })})

                  .on("mousemove", function(d, i) {
                      mousex = d3.mouse(this);
                      mousex = mousex[0];
                      var invertedx = x.invert(mousex);
                      invertedx = invertedx.getMonth() + invertedx.getDate();
                      var selected = (d.values);
                      for (var k = 0; k < selected.length; k++) {
                          datearray[k] = selected[k].date
                          datearray[k] = datearray[k].getMonth() + datearray[k].getDate();
                      }

                      mousedate = datearray.indexOf(invertedx);
                      pro = d.values[mousedate].value;

                      d3.select(this)
                      .classed("hover", true)
                      .attr("stroke", strokecolor)
                      .attr("stroke-width", "0.5px"), 
                      tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "visible");

                  })
                  .on("mouseout", function(d, i) {
                      svg.selectAll(".layer")
                       .transition()
                       .duration(250)
                       .attr("opacity", "1");
                      d3.select(this)
                      .classed("hover", false)
                      .attr("stroke-width", "0px"), tooltip.html( "<p>" + d.key + "<br>" + pro + "</p>" ).style("visibility", "hidden");
                  })

                var vertical = d3.select(".chart")
                      .append("div")
                      .attr("class", "remove")
                      .style("position", "absolute")
                      .style("z-index", "19")
                      .style("width", "1px")
                      .style("height", "380px")
                      .style("top", "10px")
                      .style("bottom", "30px")
                      .style("left", "0px")
                      .style("background", "#fff");

                d3.select(".chart")
                    .on("mousemove", function(){  
                        mousex = d3.mouse(this);
                        mousex = mousex[0] + 5;
                        vertical.style("left", mousex + "px" )})
                    .on("mouseover", function(){  
                        mousex = d3.mouse(this);
                        mousex = mousex[0] + 5;
                        vertical.style("left", mousex + "px")});

    }

</script>

For convenience, here is the code of the web service (in C#) that generates the dataset:

    [WebMethod]
    [ScriptMethod(ResponseFormat = ResponseFormat.Json)]
    public PoliticianWords[] ComparePoliticians()
    {
        List<PoliticianWords> ret = new List<PoliticianWords>();
        Random r = new Random();
        string[] politicians = { "A B", "C D", "E F", "G H" };

        Random gen = new Random();

        List<DateTime> dates = new List<DateTime>();


        for (int i = 0; i < 20; i++)
        {
            DateTime start = new DateTime(1991, 9, 11);

            int range = (DateTime.Today - start).Days;
            dates.Add(start.AddDays(gen.Next(range)));
        }

        dates.Sort();


        foreach (string politician in politicians)
        { 
            foreach(DateTime d in dates)
            {
                PoliticianWords pw = new PoliticianWords();
                pw.key = politician;
                pw.value = gen.Next(0, 100);
                pw.date = d.Day.ToString() + "/" + d.Month.ToString() + "/" + d.Year.ToString(); 
                ret.Add(pw);
            }
        }

        return ret.ToArray();

    }

EDIT 1: I forgot to add the definition of the members of the dataset as a C# class. I know this question might have been already lost in the pile of other question, but any help would be still much appreciated:

public class PoliticianWords
{
    public string key { get; set; }
    public int value { get; set; }
    public string date { get; set; }
}

EDIT 2: I came up with an example dataset which turned out to be working. Instead of generating it randomly from a webservice, I'm storing it in a .csv file and obtaining it similarly to the code given in GitHub.

key,value,date A B,100,01/08/13 A B,15,01/09/13 A B,35,01/10/13 A B,38,01/11/13 A B,22,01/12/13 A B,16,01/13/13 C D,35,01/08/13 C D,36,01/09/13 C D,37,01/10/13 C D,22,01/11/13 C D,24,01/12/13 C D,26,01/13/13 E F,21,01/08/13 E F,25,01/09/13 E F,27,01/10/13 E F,23,01/11/13 E F,24,01/12/13 E F,21,01/13/13 G H,10,01/08/13 G H,15,01/09/13 G H,35,01/10/13 G H,38,01/11/13 G H,22,01/12/13 G H,16,01/13/13

And here is also a non-working example, a randomly generated dataset obtained from the webservice:

key,value,date A B,1,6/5/1992 A B,4,11/29/1992 A B,2,11/3/1993 A B,7,8/1/1995 A B,9,8/1/1996 A B,15,8/1/1997 C D,1,6/5/1992 C D,2,11/29/1992 C D,5,11/3/1993 C D,10,8/1/1995 C D,12,8/1/1996 C D,19,8/1/1997 E F,1,6/5/1992 E F,20,11/29/1992 E F,16,11/3/1993 E F,6,8/1/1995 E F,10,8/1/1996 E F,5,8/1/1997 G H,1,6/5/1992 G H,17,11/29/1992 G H,16,11/3/1993 G H,6,8/1/1995 G H,10,8/1/1996 G H,20,8/1/1997

Deniz
  • 53
  • 2
  • 7
  • Could you provide a complete working and minimal example please? It's a lot of code and the error is probably due to something small. – Lars Kotthoff Jul 14 '14 at 10:22
  • @LarsKotthoff Done. On the bottom of the opening post you can see a working and a non-working example. To test them, you can store them in a .csv file. The d3 code will obtain them if you use the same code given in the example from GitHub. – Deniz Jul 14 '14 at 10:53

1 Answers1

1

I found a solution and it turns out that my mistake was a lame one: the format of the dates. The correct format should have all three components (day, month, year) represented by two digits each (01/08/13, 01/09/13). Instead, my web service has been generating dates with a format that allows months and days represented with a single digit and the numbers of the years containing all of their digits (1/8/2013, 1/9/2013). I've modified the part of my code (the webservice) that converts the date into a string into the following form.

                if (d.Day < 10)
                    pw.date = "0" + d.Day.ToString();
                else
                    pw.date = d.Day.ToString();
                if (d.Month < 10)
                    pw.date += "/0" + d.Month.ToString();
                else
                    pw.date += "/" + d.Month.ToString();
                int year = d.Year % 100;
                if (year < 10)
                    pw.date += "/0" + year.ToString();
                else
                    pw.date += "/" + year.ToString();
Deniz
  • 53
  • 2
  • 7