JamesE
JamesE

Reputation: 3923

D3 multi-line chart line path not displaying: d attribute missing

I am trying to create a simple multi-line chart using JSON data similar to the following:

[
    {
        sampleDate: "2014-04-14",
        shortName: "PFOA",
        pfcLevel: "0.3500000"
    },
    {
        sampleDate: "2014-05-14",
        shortName: "PFOA",
        pfcLevel: "0.3200000"
    },
    {
        sampleDate: "2014-04-14",
        shortName: "PFOS",
        pfcLevel: "2.5000000"
    },
    {
        sampleDate: "2014-05-14",
        shortName: "PFOS",
        pfcLevel: "2.4000000"
    }
]

I have basic X and Y axis showing, but the actual value lines are not displaying. Looking at the DOM the path element is not showing the d attribute.

<path class="line" style="stroke: green;"></path>

The code is below:

<script>

  var data = <?php echo $wellSamples ?>;

  console.log(data);

  // Set the dimensions of the canvas / graph
  var margin = {top: 30, right: 20, bottom: 30, left: 50},
      width = 600 - margin.left - margin.right,
      height = 270 - margin.top - margin.bottom;

  // Parse the date / time
  var parseDate = d3.time.format("%Y-%m-%d").parse;

  // Set the ranges
  var x = d3.time.scale().range([0, width]);
  var y = d3.scale.linear().range([height, 0]);

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

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

  var line = d3.svg.line()
      .interpolate("basis")
      .x(function(d) { console.log(d.sampleDate); return x(d.sampleDate); })
      .y(function(d) { console.log(d.pfcLevel); return y(d.pfcLevel); });

  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 + ")");

  x.domain(d3.extent(data, function(d) { return parseDate(d.sampleDate); }));
  y.domain(d3.extent(data, function(d) { return d.pfcLevel; }));

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

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("PFC Level");


  var pfc = svg.selectAll(".pfc")
      .data(data)
    .enter().append("g")
      .attr("class", "pfc");

  pfc.append("path")
      .attr("class", "line")
      .attr("d", line)
      .style("stroke", "green");

</script>  

Upvotes: 0

Views: 1226

Answers (1)

Mark
Mark

Reputation: 108512

Several problems here:

  1. Your data-binding is invalid. You bind the data then call .attr("d", line), this would call the line function on each point. It needs awhole array -- .attr("d", line(data))
  2. You've made no attempt to create more then one line from that data. I'm guessing you want a line per "shortName"? You need to nest the data.
  3. Your line x accessor calls .x(function(d) { return x(d.sampleDate); }), d.sampleDate has never been converted to a date though, it's still a string.

Putting this all together:

<!DOCTYPE html>
<html>

  <head>
    <script data-require="[email protected]" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
    <link rel="stylesheet" href="style.css" />
    <script src="script.js"></script>
  </head>

  <body>
    <div id="chart"></div>
    <script>

  var data = [
    {
        sampleDate: "2014-04-14",
        shortName: "PFOA",
        pfcLevel: "0.3500000"
    },
    {
        sampleDate: "2014-05-14",
        shortName: "PFOA",
        pfcLevel: "0.3200000"
    },
    {
        sampleDate: "2014-04-14",
        shortName: "PFOS",
        pfcLevel: "2.5000000"
    },
    {
        sampleDate: "2014-05-14",
        shortName: "PFOS",
        pfcLevel: "2.4000000"
    }
];

  // Parse the date / time
  var parseDate = d3.time.format("%Y-%m-%d").parse;

  // clean up data
  data.forEach(function(d){
    d.sampleDate = parseDate(d.sampleDate);
    d.pfcLevel = +d.pfcLevel;
  });

  // nest data
  var nested_data = d3.nest()
    .key(function(d) { return d.shortName; })
    .entries(data);

  // Set the dimensions of the canvas / graph
  var margin = {top: 30, right: 20, bottom: 30, left: 50},
      width = 600 - margin.left - margin.right,
      height = 270 - margin.top - margin.bottom;

  // Set the ranges
  var x = d3.time.scale().range([0, width]);
  var y = d3.scale.linear().range([height, 0]);

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

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

  var line = d3.svg.line()
      .interpolate("basis")
      .x(function(d) { return x(d.sampleDate); })
      .y(function(d) { return y(d.pfcLevel); });

  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 + ")");

  x.domain(d3.extent(data, function(d) { return d.sampleDate; }));
  y.domain(d3.extent(data, function(d) { return d.pfcLevel; }));

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

  svg.append("g")
      .attr("class", "y axis")
      .call(yAxis)
    .append("text")
      .attr("transform", "rotate(-90)")
      .attr("y", 6)
      .attr("dy", ".71em")
      .style("text-anchor", "end")
      .text("PFC Level");

  // now we bind to nested_data, an array of arrays
  var pfc = svg.selectAll(".pfc")
      .data(nested_data)
      .enter()
      .append("g")
      .attr("class", "pfc");

  pfc.append("path")
      .attr("class", "line")
      .attr("d", function(d){
        // our inner array is d.values from the nesting
        return line(d.values);
      })
      .style("stroke", "green");


    </script>
  </body>

</html>

Upvotes: 2

Related Questions