DEVELOPER_ONE
DEVELOPER_ONE

Reputation: 109

Animated Path multi series line chart in D3.Js

I want to reproduce an animated multi-series line chart.

code as below,

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

body {
  font: 12px Helvetica;
}

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

.x.axis path {
  display: none;
}

.line {
  fill: none;
  stroke: steelblue;
  stroke-width: 2px;
}

</style>
<body>
<script src="https://d3js.org/d3.v3.js"></script>
<script>

var margin = {top: 20, right: 50, bottom: 30, left: 50},
    width = 630 - margin.left - margin.right,
    height = 400 - margin.top - margin.bottom;

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

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

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

var color = d3.scale.category10();

var xAxis = d3.svg.axis()
    .scale(x)
    .ticks(5)
    .innerTickSize(15)
    .outerTickSize(0)
    .orient("bottom");

var yAxis = d3.svg.axis()
    .scale(y)
    .tickFormat(function(d) { return d + "%";})
    .ticks(5)
    .innerTickSize(15)
    .outerTickSize(0)
    .orient("left");

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


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

d3.csv('http://localhost:8000/Desktop/Dat/data.csv', function(error, data) {
  color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));

  data.forEach(function(d) {
    d.date = parseDate(d.date);
  });

  var companies = color.domain().map(function(name) {
    return {
      name: name,
      values: data.map(function(d) {
        return {date: d.date, price: +d[name]};
      })
    };
  });


  x.domain(d3.extent(data, function(d) { return d.date; }));

  y.domain([
    d3.min(companies, function(c) { return d3.min(c.values, function(v) { return v.price; }); }),
    d3.max(companies, function(c) { return d3.max(c.values, function(v) { return v.price; }); })
  ]);

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

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


  svg.append("line")
        .attr(
        {
            "class":"horizontalGrid",
            "x1" : 0,
            "x2" : width,
            "y1" : y(0),
            "y2" : y(0),
            "fill" : "none",
            "shape-rendering" : "crispEdges",
            "stroke" : "black",
            "stroke-width" : "1px",
            "stroke-dasharray": ("3, 3")
        });


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



  var path = svg.selectAll(".company").append("path")
      .attr("class", "line")
      .attr("d", function(d) { return line(d.values); })
      .style("stroke", function(d) { if (d.name == "Airbus") 
                                        {return "rgb(000,255,000)"}
                                      else {return "#000";}
                                         });



  //var totalLength = path.node().getTotalLength();
/*
console.log(path);
console.log(path.node());
console.log(path[0][0]);
console.log(path[0][1]);
*/
var totalLength = [path[0][0].getTotalLength(), path[0][1].getTotalLength()];

console.log(totalLength);


   d3.select(path[0][0])
      .attr("stroke-dasharray", totalLength[0] + " " + totalLength[0] ) 
      .attr("stroke-dashoffset", totalLength[0])
      .transition()
        .duration(15000)
        .ease("linear")
        .attr("stroke-dashoffset", 0);

   d3.select(path[0][1])
      .attr("stroke-dasharray", totalLength[1] + " " + totalLength[1] )
      .attr("stroke-dashoffset", totalLength[1])
      .transition()
        .duration(15000)
        .ease("linear")
        .attr("stroke-dashoffset", 0);

});

</script>

This works fine using this data here https://www.kaggle.com/dataset/465051e62819ab5a9903eb1658c33492adc374f4075504f10c24307505cb4e55

NB: This is a reproduction of this example here http://bl.ocks.org/atmccann/8966400

I want to reproduce the same chart using this data < www.kaggle.com/dataset/43c2b3bb5741cb8ebdcedf4fcecf4fcd73beb72513d17a29cb081025d41176c7 >

I am getting this:

Error: <path> attribute d: Expected number, "MNaN,344.43863848…".

I have tried to format my second data so as it exactly resembles the first one, that's by renaming the first to columns to 'Airbus' and 'Boeing' but still keep raising error.

Upvotes: 0

Views: 217

Answers (1)

Jasdeep Singh
Jasdeep Singh

Reputation: 8301

Added the code with a dummy dataset. The shape of dataset is similar to one specified by you. You just need to use it the way it is shown in example given by you.

Two changes you need to do:

  1. Change the parsing of date. Replace with below line:

    var parseDate = d3.time.format("%d/%m/%Y").parse

  2. Use different logic for coloring as we have more countries data.

    var path = svg.selectAll(".company").append("path") .attr("class", "line") .attr("d", function(d) { return line(d.values); }) .style("stroke", function(d) { return color(d.name) });

<!DOCTYPE html>
<meta charset="utf-8">
<style>
  body {
    font: 12px Helvetica;
  }
  
  .axis line {
    fill: none;
    stroke: #000;
    shape-rendering: crispEdges;
  }
  
  .x.axis path {
    display: none;
  }
  
  .line {
    fill: none;
    stroke: steelblue;
    stroke-width: 2px;
  }
</style>

<body>
  <script src="https://d3js.org/d3.v3.js"></script>
  <script>
    var margin = {
        top: 20,
        right: 50,
        bottom: 30,
        left: 50
      },
      width = 630 - margin.left - margin.right,
      height = 400 - margin.top - margin.bottom;
    debugger;
    var parseDate = d3.time.format("%d/%m/%Y").parse;

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

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

    var color = d3.scale.category10();

    var xAxis = d3.svg.axis()
      .scale(x)
      .ticks(5)
      .innerTickSize(15)
      .outerTickSize(0)
      .orient("bottom");

    var yAxis = d3.svg.axis()
      .scale(y)
      .tickFormat(function(d) {
        return d + "%";
      })
      .ticks(5)
      .innerTickSize(15)
      .outerTickSize(0)
      .orient("left");

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


    var svg = d3.select("body").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 = [{
        date: '1/31/1995',
        Austria: '23',
        Belgium: '14',
        Bulgaria: '-96',
      },
      {
        date: '1/31/1996',
        Austria: '-23',
        Belgium: '114',
        Bulgaria: '50',
      },
      {
        date: '1/31/1997',
        Austria: '27',
        Belgium: '-14',
        Bulgaria: '96',
      },
    ]

    color.domain(d3.keys(data[0]).filter(function(key) {
      return key !== "date";
    }));

    data.forEach(function(d) {
      d.date = parseDate(d.date);
    });

    var companies = color.domain().map(function(name) {
      return {
        name: name,
        values: data.map(function(d) {
          return {
            date: d.date,
            price: +d[name]
          };
        })
      };
    });


    x.domain(d3.extent(data, function(d) {
      return d.date;
    }));

    y.domain([
      d3.min(companies, function(c) {
        return d3.min(c.values, function(v) {
          return v.price;
        });
      }),
      d3.max(companies, function(c) {
        return d3.max(c.values, function(v) {
          return v.price;
        });
      })
    ]);

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

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


    svg.append("line")
      .attr({
        "class": "horizontalGrid",
        "x1": 0,
        "x2": width,
        "y1": y(0),
        "y2": y(0),
        "fill": "none",
        "shape-rendering": "crispEdges",
        "stroke": "black",
        "stroke-width": "1px",
        "stroke-dasharray": ("3, 3")
      });


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



    var path = svg.selectAll(".company").append("path")
      .attr("class", "line")
      .attr("d", function(d) {
        return line(d.values);
      })
      .style("stroke", function(d) {
        return color(d.name)
      });



    var totalLength = path.node().getTotalLength();

    d3.select(path[0][0])
      .attr("stroke-dasharray", totalLength[0] + " " + totalLength[0])
      .attr("stroke-dashoffset", totalLength[0])
      .transition()
      .duration(5000)
      .ease("linear")
      .attr("stroke-dashoffset", 0);

    d3.select(path[0][1])
      .attr("stroke-dasharray", totalLength[1] + " " + totalLength[1])
      .attr("stroke-dashoffset", totalLength[1])
      .transition()
      .duration(5000)
      .ease("linear")
      .attr("stroke-dashoffset", 0);
  </script>

Upvotes: 1

Related Questions